@sjawhar/pi-legion-envoy 7.8.1 → 7.9.0
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 +800 -197
- package/dist/legion.js +648 -175
- package/dist/skills/dispatch/SKILL.md +6 -0
- package/dist/skills/dispatch/references/documents.md +23 -0
- package/dist/skills/legion-worker/SKILL.md +12 -3
- package/dist/skills/legion-worker/references/pr-body.md +3 -0
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29997,6 +29997,21 @@ function snippetSegments(snippet) {
|
|
|
29997
29997
|
function snippetText(snippet) {
|
|
29998
29998
|
return snippetSegments(snippet).map(({ text, mark }) => mark ? `**${text}**` : text).join("");
|
|
29999
29999
|
}
|
|
30000
|
+
// ../contracts/src/dispatch-text-rules.ts
|
|
30001
|
+
var SESSION_ID_PATTERN = "[^/?#\\s\\p{Z}\\p{Cc}<>\"'`\\[\\]|]+";
|
|
30002
|
+
var sessionIdOnly = new RegExp(`^${SESSION_ID_PATTERN}$`, "u");
|
|
30003
|
+
function pictureCaption(name) {
|
|
30004
|
+
return name.replace(/[\\[\]`]/g, "\\$&").replace(/\s+/g, " ");
|
|
30005
|
+
}
|
|
30006
|
+
var PICTURE_TYPES = {
|
|
30007
|
+
"image/png": true,
|
|
30008
|
+
"image/jpeg": true,
|
|
30009
|
+
"image/gif": true,
|
|
30010
|
+
"image/webp": true
|
|
30011
|
+
};
|
|
30012
|
+
function isPictureType(mime) {
|
|
30013
|
+
return PICTURE_TYPES[mime] === true;
|
|
30014
|
+
}
|
|
30000
30015
|
// ../contracts/src/dispatch-tools.ts
|
|
30001
30016
|
function dispatchToolSchema(spec, z2, opts) {
|
|
30002
30017
|
const shape = spec.arguments(z2);
|
|
@@ -30070,8 +30085,12 @@ function componentsArgument(z2) {
|
|
|
30070
30085
|
}).describe("Attach the issue to architecture components. Attach the root before decomposing it; children inherit unless they choose.");
|
|
30071
30086
|
}
|
|
30072
30087
|
var SPEC_WRITING_POINTER = 'Write a spec as the "Writing a spec" section of skill://dispatch says.';
|
|
30088
|
+
function imagesArgument(z2, owner, cap) {
|
|
30089
|
+
return z2.array(z2.string({ min: 1 }), { min: 1 }).describe(`Optional local picture files to show inline: PNG, JPEG, GIF or WebP (by their bytes, not their name), each at most 25 MiB. Each is uploaded to ${owner} and appended after a blank line as one  line, in this order; those lines count toward ${cap}.`).optional();
|
|
30090
|
+
}
|
|
30073
30091
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
30074
30092
|
var ASK_QUESTION_MAX = 800;
|
|
30093
|
+
var DISPATCH_BODY_MAX = 2000;
|
|
30075
30094
|
var SEARCH_QUERY_MAX = 1000;
|
|
30076
30095
|
var SEARCH_QUERY_HINT = "search with a short phrase of a few words, not a passage";
|
|
30077
30096
|
var SEARCH_KIND_DEPTH = 100;
|
|
@@ -30171,7 +30190,7 @@ var dispatchToolSpecs = [
|
|
|
30171
30190
|
}
|
|
30172
30191
|
]
|
|
30173
30192
|
},
|
|
30174
|
-
description: "Open a to-do or permission only a human can give, or a decision that has no document to " + "live in. A question about the design an issue's document records is not this tool: write " + "it into that document as a decision block (dispatch_doc_edit inserting an ask block at the " + "end of the section it concerns), at every phase, approved spec or not; the block reaches " + "the Inbox and its answer lands next to its context. Never give an ask an Approve option: a " + "document is approved through dispatch_request_approval. Do not use this tool for a status " + "update or discussion; use dispatch_message instead. " + ASK_QUESTION_CONTRACT + " " + ASK_OPTIONS_CONTRACT + " For an action only a human can perform, state what it changes and risks as constraints. " + "Anything that requires a human to do, including a credential or grant renewal, is an ask, " + "never a message. " + "Anchor a to-do about a document passage, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". ` + `A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} characters ` + `and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
30193
|
+
description: "Open a to-do or permission only a human can give, or a decision that has no document to " + "live in. A question about the design an issue's document records is not this tool: write " + "it into that document as a decision block (dispatch_doc_edit inserting an ask block at the " + "end of the section it concerns), at every phase, approved spec or not; the block reaches " + "the Inbox and its answer lands next to its context. Never give an ask an Approve option: a " + "document is approved through dispatch_request_approval. Do not use this tool for a status " + "update or discussion; use dispatch_message instead. " + ASK_QUESTION_CONTRACT + " " + ASK_OPTIONS_CONTRACT + " For an action only a human can perform, state what it changes and risks as constraints. " + "Anything that requires a human to do, including a credential or grant renewal, is an ask, " + "never a message. " + "Anchor a to-do about a document passage, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". ` + `A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} characters, ` + `the lines images appends included, and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
30175
30194
|
arguments: (z2) => ({
|
|
30176
30195
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30177
30196
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -30188,7 +30207,8 @@ var dispatchToolSpecs = [
|
|
|
30188
30207
|
artifact: z2.string().describe("Artifact slug or id containing the quoted text."),
|
|
30189
30208
|
quote: z2.string().describe("Exact text the decision concerns."),
|
|
30190
30209
|
occurrence: z2.number({ int: true, min: 0 }).describe("Zero-based occurrence of the quote.").optional()
|
|
30191
|
-
}).describe("Optional document location for the question.").optional()
|
|
30210
|
+
}).describe("Optional document location for the question.").optional(),
|
|
30211
|
+
images: imagesArgument(z2, "the issue (or the project document's project)", `the ${ASK_QUESTION_MAX}-character question limit`)
|
|
30192
30212
|
}),
|
|
30193
30213
|
validation: documentOwnerValidation(true)
|
|
30194
30214
|
},
|
|
@@ -30263,7 +30283,7 @@ var dispatchToolSpecs = [
|
|
|
30263
30283
|
{
|
|
30264
30284
|
name: "dispatch_comment",
|
|
30265
30285
|
example: { issue: "DSP-1", body: "Looks good." },
|
|
30266
|
-
description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. A quote anchor is pinned to its block. Body is at most 2,000 characters. ${OWNER_REFERENCE}`,
|
|
30286
|
+
description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. A quote anchor is pinned to its block. Body is at most 2,000 characters, the lines images appends included. ${OWNER_REFERENCE}`,
|
|
30267
30287
|
arguments: (z2) => ({
|
|
30268
30288
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30269
30289
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -30271,10 +30291,11 @@ var dispatchToolSpecs = [
|
|
|
30271
30291
|
ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
30272
30292
|
quote: z2.string().describe("Optional exact quoted document text.").optional(),
|
|
30273
30293
|
occurrence: z2.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
30274
|
-
body: z2.string({ max:
|
|
30294
|
+
body: z2.string({ max: DISPATCH_BODY_MAX }).describe("Review comment, at most 2,000 characters."),
|
|
30275
30295
|
reply_to: z2.string().describe("A comment id (uuid); replying to any comment in a thread continues that thread (an " + "ask's clarification thread included). To reply to an ask, use reply_to_ask with the " + "ask id instead.").optional(),
|
|
30276
30296
|
reply_to_ask: z2.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional(),
|
|
30277
|
-
turn: z2.enum(["agent", "human"]).describe("Only with reply_to_ask: who holds the turn after this reply. agent: a progress note - " + "you keep the turn and the ask stays 'Waiting on agents' for the human; human (default): " + "you need the human to act - the ask returns to 'Waiting on you'.").optional()
|
|
30297
|
+
turn: z2.enum(["agent", "human"]).describe("Only with reply_to_ask: who holds the turn after this reply. agent: a progress note - " + "you keep the turn and the ask stays 'Waiting on agents' for the human; human (default): " + "you need the human to act - the ask returns to 'Waiting on you'.").optional(),
|
|
30298
|
+
images: imagesArgument(z2, "the issue (or the project document's project)", "the 2,000-character body limit")
|
|
30278
30299
|
}),
|
|
30279
30300
|
validation: commentValidation
|
|
30280
30301
|
},
|
|
@@ -30302,11 +30323,12 @@ var dispatchToolSpecs = [
|
|
|
30302
30323
|
{
|
|
30303
30324
|
name: "dispatch_message",
|
|
30304
30325
|
example: { issue: "DSP-1", body: "Implementation started." },
|
|
30305
|
-
description: "Post a note humans must read now: a reply to a human's message or a deliverable that landed. A to-do only a human can " + "complete is an ask (dispatch_ask), so it reaches their inbox. Never progress or status updates - Dispatch is a high-signal " + "record, not a log. Not a design decision (write it as a decision block in the document) or document feedback (dispatch_comment). " + "To answer a human's direct message to this session - one sent from the Agents page, which names no issue - pass that message's bare id as " + "in_reply_to and no issue; the reply lands in that conversation. Another call with the same in_reply_to and new text posts a follow-up, " + "threaded under this session's first reply; the same text again posts nothing. dispatch_read({message}) reads " + "that conversation back. Every other message names its issue. " + `Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
30326
|
+
description: "Post a note humans must read now: a reply to a human's message or a deliverable that landed. A to-do only a human can " + "complete is an ask (dispatch_ask), so it reaches their inbox. Never progress or status updates - Dispatch is a high-signal " + "record, not a log. Not a design decision (write it as a decision block in the document) or document feedback (dispatch_comment). " + "To answer a human's direct message to this session - one sent from the Agents page, which names no issue - pass that message's bare id as " + "in_reply_to and no issue; the reply lands in that conversation. Another call with the same in_reply_to and new text posts a follow-up, " + "threaded under this session's first reply; the same text again posts nothing. dispatch_read({message}) reads " + "that conversation back. Every other message names its issue. " + `Body is at most 2,000 characters, the lines images appends included. ${ISSUE_REFERENCE}`,
|
|
30306
30327
|
arguments: (z2) => ({
|
|
30307
30328
|
issue: z2.string().describe(`${ISSUE_REFERENCE} Omit it only when in_reply_to answers a human's direct message to this session.`).optional(),
|
|
30308
|
-
body: z2.string({ max:
|
|
30309
|
-
in_reply_to: z2.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional()
|
|
30329
|
+
body: z2.string({ max: DISPATCH_BODY_MAX }).describe("Update text, at most 2,000 characters."),
|
|
30330
|
+
in_reply_to: z2.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional(),
|
|
30331
|
+
images: imagesArgument(z2, "the issue (a reply to a direct message: your own conversation, dispatch://agent/<your session id>)", "the 2,000-character body limit")
|
|
30310
30332
|
}),
|
|
30311
30333
|
validation: messageValidation
|
|
30312
30334
|
},
|
|
@@ -30351,13 +30373,13 @@ var dispatchToolSpecs = [
|
|
|
30351
30373
|
{
|
|
30352
30374
|
name: "dispatch_doc_read",
|
|
30353
30375
|
example: { issue: "DSP-1" },
|
|
30354
|
-
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch_read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch_doc_edit precondition; use /blocks for per-block tokens. " + "A file that is not UTF-8 text is described, with the route that serves its bytes. " + OWNER_REFERENCE,
|
|
30376
|
+
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch_read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch_doc_edit precondition; use /blocks for per-block tokens. " + "A picture (an uploaded PNG, JPEG, GIF or WebP of at most 3,750,000 bytes, 5 MB once base64-encoded) comes back as an image you see, with its name, type, size and version, every time you ask, including one a read already showed this session; " + "any other file that is not UTF-8 text is described, with the route that serves its bytes. " + OWNER_REFERENCE,
|
|
30355
30377
|
arguments: (z2) => ({
|
|
30356
30378
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30357
30379
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
30358
30380
|
artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
|
|
30359
30381
|
version: z2.number({ int: true, min: 1 }).describe("Optional version number.").optional(),
|
|
30360
|
-
ref: z2.string().describe("Optional dispatch:// document reference.").optional()
|
|
30382
|
+
ref: z2.string().describe("Optional dispatch:// document reference; a picture in a conversation on the Agents page is dispatch://agent/<session id>/artifact/<slug>@vN.").optional()
|
|
30361
30383
|
}),
|
|
30362
30384
|
validation: documentOwnerValidation(true)
|
|
30363
30385
|
},
|
|
@@ -30397,7 +30419,7 @@ var dispatchToolSpecs = [
|
|
|
30397
30419
|
{
|
|
30398
30420
|
name: "dispatch_read",
|
|
30399
30421
|
example: { issue: "DSP-1" },
|
|
30400
|
-
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), `DOCUMENT_UNLOADABLE` (the document " + "needs a rebuild) or `INTERNAL`. " + "An issue read carries a `Progress:` line, `tasks 3/7, children 2/5`: its spec's task-list items " + "(`- [ ]` / `- [x]`, nested lists included) and its direct children, each done of total and each only " + "when it has any (`Progress: none` otherwise). " + "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,
|
|
30422
|
+
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), `DOCUMENT_UNLOADABLE` (the document " + "needs a rebuild) or `INTERNAL`. " + "An issue read carries a `Progress:` line, `tasks 3/7, children 2/5`: its spec's task-list items " + "(`- [ ]` / `- [x]`, nested lists included) and its direct children, each done of total and each only " + "when it has any (`Progress: none` otherwise). " + "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. " + "The pictures the shown messages, asks and comments embed come back as images you see, newest first, at most 8 " + "and 10 MiB of them per read, each a PNG, JPEG, GIF or WebP of at most 3,750,000 bytes (5 MB of base64); a `Pictures:` section names each " + "one shown, in order, and the rest by reference, for dispatch_doc_read. A picture this session was already shown is named, not sent again " + "(every request carries the session's history, and the provider refuses one over 32 MB); dispatch_doc_read shows it again. " + OWNER_REFERENCE,
|
|
30401
30423
|
arguments: (z2) => ({
|
|
30402
30424
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30403
30425
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -31218,6 +31240,116 @@ function dispatchDocumentRef(owner, slug) {
|
|
|
31218
31240
|
function dispatchChildRef(ownerRef, kind, id) {
|
|
31219
31241
|
return `${ownerRef}/${kind}/${id}`;
|
|
31220
31242
|
}
|
|
31243
|
+
function parseDispatchRef(ref) {
|
|
31244
|
+
const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
|
|
31245
|
+
if (projectDocument) {
|
|
31246
|
+
const [, project, artifact2, version3, targetKind, targetID] = projectDocument;
|
|
31247
|
+
if (project === undefined || artifact2 === undefined || version3 !== undefined && Number(version3) < 1) {
|
|
31248
|
+
return null;
|
|
31249
|
+
}
|
|
31250
|
+
if (targetKind === undefined) {
|
|
31251
|
+
return {
|
|
31252
|
+
owner: { kind: "project", project },
|
|
31253
|
+
kind: "artifact",
|
|
31254
|
+
id: artifact2,
|
|
31255
|
+
...version3 === undefined ? {} : { version: Number(version3) }
|
|
31256
|
+
};
|
|
31257
|
+
}
|
|
31258
|
+
if (targetID === undefined || targetKind !== "ask" && targetKind !== "comment")
|
|
31259
|
+
return null;
|
|
31260
|
+
return {
|
|
31261
|
+
owner: { kind: "project", project },
|
|
31262
|
+
kind: targetKind,
|
|
31263
|
+
id: targetID,
|
|
31264
|
+
artifact: artifact2
|
|
31265
|
+
};
|
|
31266
|
+
}
|
|
31267
|
+
const issueReference = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9}-[1-9][0-9]*)(?:\/(spec)|\/(log)|\/(children)|\/artifact\/([^/@]+)(?:@v(\d+))?|\/ask\/([^/]+)|\/comment\/([^/]+)|\/message\/([^/]+))?$/);
|
|
31268
|
+
if (!issueReference)
|
|
31269
|
+
return null;
|
|
31270
|
+
const [, issue2, spec, log, children, artifact, version2, ask, comment, message] = issueReference;
|
|
31271
|
+
if (!issue2 || version2 !== undefined && Number(version2) < 1)
|
|
31272
|
+
return null;
|
|
31273
|
+
const owner = { kind: "issue", issue: issue2 };
|
|
31274
|
+
if (spec)
|
|
31275
|
+
return { owner, kind: "spec", id: spec };
|
|
31276
|
+
if (log)
|
|
31277
|
+
return { owner, kind: "log", id: log };
|
|
31278
|
+
if (children)
|
|
31279
|
+
return { owner, kind: "children", id: children };
|
|
31280
|
+
if (artifact) {
|
|
31281
|
+
return {
|
|
31282
|
+
owner,
|
|
31283
|
+
kind: "artifact",
|
|
31284
|
+
id: artifact,
|
|
31285
|
+
...version2 === undefined ? {} : { version: Number(version2) }
|
|
31286
|
+
};
|
|
31287
|
+
}
|
|
31288
|
+
if (ask)
|
|
31289
|
+
return { owner, kind: "ask", id: ask };
|
|
31290
|
+
if (comment)
|
|
31291
|
+
return { owner, kind: "comment", id: comment };
|
|
31292
|
+
if (message)
|
|
31293
|
+
return { owner, kind: "message", id: message };
|
|
31294
|
+
return { owner, kind: "issue", id: issue2 };
|
|
31295
|
+
}
|
|
31296
|
+
|
|
31297
|
+
// ../envoy-client/src/dispatch-pictures.ts
|
|
31298
|
+
var PICTURE_UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
|
|
31299
|
+
var PICTURE_SHOWN_MAX_BYTES = 3750000;
|
|
31300
|
+
var pictureShownMaxText = `${PICTURE_SHOWN_MAX_BYTES.toLocaleString("en-US")} bytes (5 MB of base64)`;
|
|
31301
|
+
function oversizePictureText(name, bytes) {
|
|
31302
|
+
return `${name} is ${bytes.toLocaleString("en-US")} bytes, over the ${pictureShownMaxText} a model is shown`;
|
|
31303
|
+
}
|
|
31304
|
+
var PICTURES_SHOWN_MAX = 8;
|
|
31305
|
+
var PICTURES_SHOWN_MAX_BYTES = 10 * 1024 * 1024;
|
|
31306
|
+
var PICTURE_SNIFF_BYTES = 12;
|
|
31307
|
+
function bytesAt(bytes, offset, expected) {
|
|
31308
|
+
return expected.every((byte, index) => bytes[offset + index] === byte);
|
|
31309
|
+
}
|
|
31310
|
+
function sniffPictureType(bytes) {
|
|
31311
|
+
if (bytesAt(bytes, 0, [137, 80, 78, 71, 13, 10, 26, 10]))
|
|
31312
|
+
return "image/png";
|
|
31313
|
+
if (bytesAt(bytes, 0, [255, 216, 255]))
|
|
31314
|
+
return "image/jpeg";
|
|
31315
|
+
if (bytesAt(bytes, 0, [71, 73, 70, 56]) && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97) {
|
|
31316
|
+
return "image/gif";
|
|
31317
|
+
}
|
|
31318
|
+
if (bytesAt(bytes, 0, [82, 73, 70, 70]) && bytesAt(bytes, 8, [87, 69, 66, 80])) {
|
|
31319
|
+
return "image/webp";
|
|
31320
|
+
}
|
|
31321
|
+
return;
|
|
31322
|
+
}
|
|
31323
|
+
function toolImage(bytes) {
|
|
31324
|
+
const mimeType = sniffPictureType(bytes);
|
|
31325
|
+
if (mimeType === undefined || bytes.length > PICTURE_SHOWN_MAX_BYTES)
|
|
31326
|
+
return;
|
|
31327
|
+
return { data: Buffer.from(bytes).toString("base64"), mimeType };
|
|
31328
|
+
}
|
|
31329
|
+
function imageBlocks(images) {
|
|
31330
|
+
return images.map(({ data, mimeType }) => ({ type: "image", data, mimeType }));
|
|
31331
|
+
}
|
|
31332
|
+
function pictureLine(name, address) {
|
|
31333
|
+
return ``;
|
|
31334
|
+
}
|
|
31335
|
+
function withPictureLines(text, lines) {
|
|
31336
|
+
if (lines.length === 0)
|
|
31337
|
+
return text;
|
|
31338
|
+
const head = text.trimEnd();
|
|
31339
|
+
return head === "" ? lines.join(`
|
|
31340
|
+
`) : `${head}
|
|
31341
|
+
|
|
31342
|
+
${lines.join(`
|
|
31343
|
+
`)}`;
|
|
31344
|
+
}
|
|
31345
|
+
var PICTURE_SYNTAX = /!\[(?:\\.|[^\\\]])*\]\((dispatch:\/\/[^\s()]+@v[1-9][0-9]*)(?:\s+"(?:\\.|[^\\"])*")?\)/g;
|
|
31346
|
+
function pictureAddresses(text) {
|
|
31347
|
+
return [...text.matchAll(PICTURE_SYNTAX)].map((match) => match[1]);
|
|
31348
|
+
}
|
|
31349
|
+
function picturesNewestFirst(texts) {
|
|
31350
|
+
const ordered = texts.map((item, index) => ({ item, index, at: Date.parse(item.at) })).sort((left, right) => (Number.isNaN(right.at) ? 0 : right.at) - (Number.isNaN(left.at) ? 0 : left.at) || left.index - right.index);
|
|
31351
|
+
return [...new Set(ordered.flatMap(({ item }) => pictureAddresses(item.text)))];
|
|
31352
|
+
}
|
|
31221
31353
|
|
|
31222
31354
|
// ../envoy-client/src/delivery.ts
|
|
31223
31355
|
var KNOWN_SOURCES = EnvelopeSchema.shape.source.enum;
|
|
@@ -31372,6 +31504,33 @@ var RecoverableDispatchDeliveryFailureSchema = exports_external.object({
|
|
|
31372
31504
|
}).passthrough(),
|
|
31373
31505
|
delivery: DispatchTargetedDeliverySchema
|
|
31374
31506
|
}).refine(({ event, delivery }) => event.type === "comment.created" ? isCommentTargetedDelivery(delivery) : !isCommentTargetedDelivery(delivery) && DispatchTargetedResourceIDSchema.safeParse(event.payload.id).success, { message: "delivery resource must match its event type" });
|
|
31507
|
+
function eventPictures(event) {
|
|
31508
|
+
let text;
|
|
31509
|
+
if (event.type.startsWith("message.")) {
|
|
31510
|
+
const parsed = MessageEventPayloadSchema.safeParse(event.payload);
|
|
31511
|
+
text = parsed.success ? parsed.data.body : undefined;
|
|
31512
|
+
} else if (event.type.startsWith("comment.")) {
|
|
31513
|
+
const parsed = CommentEventPayloadSchema.safeParse(event.payload);
|
|
31514
|
+
text = parsed.success ? parsed.data.body : undefined;
|
|
31515
|
+
} else if (event.type.startsWith("ask.")) {
|
|
31516
|
+
const parsed = (event.type === "ask.edited" ? AskEditedEventPayloadSchema : AskEventPayloadSchema).safeParse(event.payload);
|
|
31517
|
+
if (parsed.success) {
|
|
31518
|
+
switch (event.type) {
|
|
31519
|
+
case "ask.answered":
|
|
31520
|
+
text = parsed.data.answer?.text;
|
|
31521
|
+
break;
|
|
31522
|
+
case "ask.resolved":
|
|
31523
|
+
text = parsed.data.resolution?.reason;
|
|
31524
|
+
break;
|
|
31525
|
+
case "ask.opened":
|
|
31526
|
+
case "ask.edited":
|
|
31527
|
+
text = parsed.data.question;
|
|
31528
|
+
break;
|
|
31529
|
+
}
|
|
31530
|
+
}
|
|
31531
|
+
}
|
|
31532
|
+
return typeof text === "string" ? pictureAddresses(text) : [];
|
|
31533
|
+
}
|
|
31375
31534
|
function senderLabel(envelope2) {
|
|
31376
31535
|
const sender = envelope2.source_session ?? envelope2.source ?? "unknown";
|
|
31377
31536
|
return envelope2.sender?.title === undefined ? sender : `${sender} (${envelope2.sender.title})`;
|
|
@@ -31648,6 +31807,7 @@ function renderInbound(raw, sessionID, subject2) {
|
|
|
31648
31807
|
let delivery;
|
|
31649
31808
|
let rejectedDelivery;
|
|
31650
31809
|
let malformedDelivery = false;
|
|
31810
|
+
let pictures = [];
|
|
31651
31811
|
let inReplyTo = envelope2.in_reply_to;
|
|
31652
31812
|
let askAuthor;
|
|
31653
31813
|
const dispatchRendered = envelope2.source === "dispatch" && envelope2.payload !== undefined;
|
|
@@ -31754,6 +31914,7 @@ function renderInbound(raw, sessionID, subject2) {
|
|
|
31754
31914
|
...compactRecord ?? { payload: dispatchPayloadForReader(frame.event, sessionID) }
|
|
31755
31915
|
};
|
|
31756
31916
|
dispatchActor = frame.event.actor;
|
|
31917
|
+
pictures = eventPictures(frame.event);
|
|
31757
31918
|
} else {
|
|
31758
31919
|
dispatchEvent = frame.raw;
|
|
31759
31920
|
rejectedDelivery = frame.rejectedDelivery;
|
|
@@ -31824,7 +31985,8 @@ function renderInbound(raw, sessionID, subject2) {
|
|
|
31824
31985
|
...dispatchActor === undefined ? {} : { dispatchActor },
|
|
31825
31986
|
...delivery === undefined ? {} : { delivery },
|
|
31826
31987
|
...rejectedDelivery === undefined ? {} : { rejectedDelivery },
|
|
31827
|
-
...malformedDelivery ? { malformedDelivery: true } : {}
|
|
31988
|
+
...malformedDelivery ? { malformedDelivery: true } : {},
|
|
31989
|
+
...pictures.length === 0 ? {} : { pictures }
|
|
31828
31990
|
};
|
|
31829
31991
|
}
|
|
31830
31992
|
|
|
@@ -31978,7 +32140,7 @@ function activeDispatchConfig(env, options = {}) {
|
|
|
31978
32140
|
}
|
|
31979
32141
|
|
|
31980
32142
|
// ../envoy-client/src/dispatch-execute.ts
|
|
31981
|
-
import { resolve as
|
|
32143
|
+
import { resolve as resolvePath2 } from "path";
|
|
31982
32144
|
|
|
31983
32145
|
// ../envoy-client/src/dispatch-cwd.ts
|
|
31984
32146
|
import { execFile } from "child_process";
|
|
@@ -32206,21 +32368,27 @@ class DispatchClient {
|
|
|
32206
32368
|
return this.#json("GET", ["api", "v1", "projects", project, "artifacts"], undefined, unlinked ? { unlinked: "true" } : undefined);
|
|
32207
32369
|
}
|
|
32208
32370
|
async projectArtifact(project, input) {
|
|
32209
|
-
|
|
32210
|
-
if ("content" in input)
|
|
32211
|
-
return this.#json("POST", artifactPath, input);
|
|
32212
|
-
const form = new FormData;
|
|
32213
|
-
form.set("name", input.name);
|
|
32214
|
-
if (input.summary !== undefined)
|
|
32215
|
-
form.set("summary", input.summary);
|
|
32216
|
-
if (input.actor !== undefined)
|
|
32217
|
-
form.set("actor", JSON.stringify(input.actor));
|
|
32218
|
-
form.set("file", input.file, input.name);
|
|
32219
|
-
return this.#form("POST", artifactPath, form);
|
|
32371
|
+
return this.#upload(["api", "v1", "projects", project, "artifacts"], input);
|
|
32220
32372
|
}
|
|
32221
32373
|
async getProjectArtifact(project, slug) {
|
|
32222
32374
|
return this.#json("GET", ["api", "v1", "projects", project, "artifacts", slug]);
|
|
32223
32375
|
}
|
|
32376
|
+
async agentArtifact(sessionId, input) {
|
|
32377
|
+
return this.#upload(["api", "v1", "agents", sessionId, "artifacts"], input);
|
|
32378
|
+
}
|
|
32379
|
+
async getAgentArtifact(sessionId, slug) {
|
|
32380
|
+
return this.#json("GET", ["api", "v1", "agents", sessionId, "artifacts", slug]);
|
|
32381
|
+
}
|
|
32382
|
+
async getIssueArtifact(issue2, slug) {
|
|
32383
|
+
return this.#json("GET", [
|
|
32384
|
+
"api",
|
|
32385
|
+
"v1",
|
|
32386
|
+
"issues",
|
|
32387
|
+
await this.#resolveIssue(issue2),
|
|
32388
|
+
"artifacts",
|
|
32389
|
+
slug
|
|
32390
|
+
]);
|
|
32391
|
+
}
|
|
32224
32392
|
async search(query, options = {}) {
|
|
32225
32393
|
return this.#json("GET", ["api", "v1", "search"], undefined, { q: query, ...options });
|
|
32226
32394
|
}
|
|
@@ -32341,17 +32509,7 @@ class DispatchClient {
|
|
|
32341
32509
|
return this.#json("POST", ["api", "v1", "messages", id, "deliveries", String(attempt), "accept"], input);
|
|
32342
32510
|
}
|
|
32343
32511
|
async artifact(issue2, input) {
|
|
32344
|
-
|
|
32345
|
-
if ("content" in input)
|
|
32346
|
-
return this.#json("POST", artifactPath, input);
|
|
32347
|
-
const form = new FormData;
|
|
32348
|
-
form.set("name", input.name);
|
|
32349
|
-
if (input.summary !== undefined)
|
|
32350
|
-
form.set("summary", input.summary);
|
|
32351
|
-
if (input.actor !== undefined)
|
|
32352
|
-
form.set("actor", JSON.stringify(input.actor));
|
|
32353
|
-
form.set("file", input.file, input.name);
|
|
32354
|
-
return this.#form("POST", artifactPath, form);
|
|
32512
|
+
return this.#upload(["api", "v1", "issues", await this.#resolveIssue(issue2), "artifacts"], input);
|
|
32355
32513
|
}
|
|
32356
32514
|
async getArtifact(id) {
|
|
32357
32515
|
return this.#json("GET", ["api", "v1", "artifacts", id]);
|
|
@@ -32458,6 +32616,18 @@ class DispatchClient {
|
|
|
32458
32616
|
});
|
|
32459
32617
|
return { status: response.status, payload: await this.#response(method, url2, response) };
|
|
32460
32618
|
}
|
|
32619
|
+
async#upload(path2, input) {
|
|
32620
|
+
if ("content" in input)
|
|
32621
|
+
return this.#json("POST", path2, input);
|
|
32622
|
+
const form = new FormData;
|
|
32623
|
+
form.set("name", input.name);
|
|
32624
|
+
if (input.summary !== undefined)
|
|
32625
|
+
form.set("summary", input.summary);
|
|
32626
|
+
if (input.actor !== undefined)
|
|
32627
|
+
form.set("actor", JSON.stringify(input.actor));
|
|
32628
|
+
form.set("file", input.file, input.name);
|
|
32629
|
+
return this.#form("POST", path2, form);
|
|
32630
|
+
}
|
|
32461
32631
|
async#form(method, path2, body) {
|
|
32462
32632
|
const url2 = this.#url(path2);
|
|
32463
32633
|
const response = await this.fetchImpl(url2, {
|
|
@@ -32516,70 +32686,8 @@ class DispatchClient {
|
|
|
32516
32686
|
}
|
|
32517
32687
|
}
|
|
32518
32688
|
|
|
32519
|
-
// ../envoy-client/src/
|
|
32520
|
-
|
|
32521
|
-
return `showing ${offset + 1}-${offset + count} of ${total}`;
|
|
32522
|
-
}
|
|
32523
|
-
function searchResultLine(result, baseUrl) {
|
|
32524
|
-
const href = new URL(result.href, baseUrl).toString();
|
|
32525
|
-
const { owner } = result;
|
|
32526
|
-
if (owner.kind === "document") {
|
|
32527
|
-
const reference = dispatchDocumentRef(owner.project, owner.slug);
|
|
32528
|
-
return `${reference} [document] ${owner.name} - ${result.kind}: ${snippetText(result.snippet)} -> ${href}`;
|
|
32529
|
-
}
|
|
32530
|
-
const artifactName = result.artifact ? ` ${result.artifact.name}` : "";
|
|
32531
|
-
const label = `${owner.key} [${owner.status}] ${owner.title} - ${result.kind}${artifactName}`;
|
|
32532
|
-
return `${label}: ${snippetText(result.snippet)} -> ${href}`;
|
|
32533
|
-
}
|
|
32534
|
-
function searchAnswer(search, query, offset, configUrl) {
|
|
32535
|
-
const results = search.results;
|
|
32536
|
-
const count = results.length;
|
|
32537
|
-
const lines = results.map((result) => searchResultLine(result, configUrl));
|
|
32538
|
-
const noun = count === 1 ? "result" : "results";
|
|
32539
|
-
const noResults = `No results for "${query}".`;
|
|
32540
|
-
if (typeof search.total !== "number") {
|
|
32541
|
-
if (offset !== undefined && offset > 0) {
|
|
32542
|
-
throw new Error(`Dispatch answered without a total: it predates search paging and ignored offset ${offset}, so this would be its first page again.`);
|
|
32543
|
-
}
|
|
32544
|
-
return {
|
|
32545
|
-
text: count === 0 ? noResults : [`${count} ${noun} for "${query}" (${search.took_ms} ms)`, ...lines].join(`
|
|
32546
|
-
`),
|
|
32547
|
-
details: { query, results }
|
|
32548
|
-
};
|
|
32549
|
-
}
|
|
32550
|
-
const { total, reachable, offset: pageOffset } = search;
|
|
32551
|
-
const end = pageOffset + count;
|
|
32552
|
-
const cut = reachable < total ? `Each kind lists only its best ${SEARCH_KIND_DEPTH} matches, so ${reachable} of the ${total} can be paged to; narrow the query or name a project to reach the rest.` : undefined;
|
|
32553
|
-
const details = {
|
|
32554
|
-
query,
|
|
32555
|
-
results,
|
|
32556
|
-
total,
|
|
32557
|
-
reachable,
|
|
32558
|
-
offset: pageOffset,
|
|
32559
|
-
limit: search.limit
|
|
32560
|
-
};
|
|
32561
|
-
if (count === 0) {
|
|
32562
|
-
return {
|
|
32563
|
-
text: total === 0 ? noResults : [
|
|
32564
|
-
`No results for "${query}" at offset ${pageOffset}: it matches ${total}, and the pages reach the first ${reachable}.`,
|
|
32565
|
-
...cut === undefined ? [] : [cut]
|
|
32566
|
-
].join(`
|
|
32567
|
-
`),
|
|
32568
|
-
details
|
|
32569
|
-
};
|
|
32570
|
-
}
|
|
32571
|
-
const showing = pageOffset === 0 && count === total ? "" : `${pageSummaryText(pageOffset, count, total)}, `;
|
|
32572
|
-
return {
|
|
32573
|
-
text: [
|
|
32574
|
-
`${count} ${noun} for "${query}" (${showing}${search.took_ms} ms)`,
|
|
32575
|
-
...cut === undefined ? [] : [cut],
|
|
32576
|
-
...lines,
|
|
32577
|
-
...end < reachable ? [`Next page: offset ${end}.`] : []
|
|
32578
|
-
].join(`
|
|
32579
|
-
`),
|
|
32580
|
-
details
|
|
32581
|
-
};
|
|
32582
|
-
}
|
|
32689
|
+
// ../envoy-client/src/dispatch-picture-tools.ts
|
|
32690
|
+
import { basename, resolve as resolvePath } from "path";
|
|
32583
32691
|
|
|
32584
32692
|
// ../envoy-client/src/tool-input-errors.ts
|
|
32585
32693
|
class ToolInputError extends Error {
|
|
@@ -32704,6 +32812,326 @@ function formatZodIssues(issues, schema) {
|
|
|
32704
32812
|
});
|
|
32705
32813
|
}
|
|
32706
32814
|
|
|
32815
|
+
// ../envoy-client/src/dispatch-picture-tools.ts
|
|
32816
|
+
var PICTURE_TYPE_NAMES = "a PNG, JPEG, GIF or WebP";
|
|
32817
|
+
async function localPictures(images, cwd, problems) {
|
|
32818
|
+
if (!Array.isArray(images))
|
|
32819
|
+
return [];
|
|
32820
|
+
const checked = await Promise.all(images.map((path2) => localPicture(path2, cwd)));
|
|
32821
|
+
const pictures = [];
|
|
32822
|
+
for (const outcome of checked) {
|
|
32823
|
+
if (typeof outcome === "string")
|
|
32824
|
+
problems.push(outcome);
|
|
32825
|
+
else if (outcome !== undefined)
|
|
32826
|
+
pictures.push(outcome);
|
|
32827
|
+
}
|
|
32828
|
+
return pictures;
|
|
32829
|
+
}
|
|
32830
|
+
async function localPicture(path2, cwd) {
|
|
32831
|
+
if (typeof path2 !== "string" || path2 === "")
|
|
32832
|
+
return;
|
|
32833
|
+
const absolute = resolvePath(cwd, path2);
|
|
32834
|
+
const file2 = Bun.file(absolute);
|
|
32835
|
+
let size;
|
|
32836
|
+
let head;
|
|
32837
|
+
try {
|
|
32838
|
+
const stats = await file2.stat();
|
|
32839
|
+
if (!stats.isFile())
|
|
32840
|
+
return `images: ${path2} is not a file`;
|
|
32841
|
+
size = stats.size;
|
|
32842
|
+
head = new Uint8Array(await file2.slice(0, PICTURE_SNIFF_BYTES).arrayBuffer());
|
|
32843
|
+
} catch (error48) {
|
|
32844
|
+
return `images: ${path2} cannot be read: ${messageFor(error48)}`;
|
|
32845
|
+
}
|
|
32846
|
+
if (size > PICTURE_UPLOAD_MAX_BYTES) {
|
|
32847
|
+
return `images: ${path2} is ${size.toLocaleString("en-US")} bytes, over the ${PICTURE_UPLOAD_MAX_BYTES / 1024 / 1024} MiB one Dispatch upload takes`;
|
|
32848
|
+
}
|
|
32849
|
+
const type = sniffPictureType(head);
|
|
32850
|
+
if (type === undefined) {
|
|
32851
|
+
return `images: ${path2} is not ${PICTURE_TYPE_NAMES} picture (judged by its bytes, not its name)`;
|
|
32852
|
+
}
|
|
32853
|
+
return { path: path2, name: basename(absolute), file: Bun.file(absolute, { type }) };
|
|
32854
|
+
}
|
|
32855
|
+
function uploadSlug(name) {
|
|
32856
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
32857
|
+
return slug === "" ? "artifact" : slug;
|
|
32858
|
+
}
|
|
32859
|
+
async function textWithPictures(tool, text, pictures, limit, owner, upload, actor) {
|
|
32860
|
+
if (pictures.length === 0)
|
|
32861
|
+
return text;
|
|
32862
|
+
const counted = `${limit.text} plus ${pictures.length} ${pictures.length === 1 ? "picture" : "pictures"}`;
|
|
32863
|
+
const fix = `${limit.shorten} or send fewer pictures`;
|
|
32864
|
+
const predicted = withPictureLines(text, pictures.map((picture) => pictureLine(picture.name, `dispatch://${owner}/artifact/${uploadSlug(picture.name)}@v1`)));
|
|
32865
|
+
if (predicted.length > limit.cap) {
|
|
32866
|
+
throw new ToolInputError(tool, [
|
|
32867
|
+
`${counted} ${overCapMessage(predicted.length, limit.cap)}; ${fix}`
|
|
32868
|
+
]);
|
|
32869
|
+
}
|
|
32870
|
+
const lines = [];
|
|
32871
|
+
const addresses = [];
|
|
32872
|
+
for (const picture of pictures) {
|
|
32873
|
+
let uploaded;
|
|
32874
|
+
try {
|
|
32875
|
+
uploaded = await upload({ name: picture.name, file: picture.file, actor });
|
|
32876
|
+
} catch (error48) {
|
|
32877
|
+
const earlier = addresses.length === 0 ? "" : `; the pictures before it are uploaded (${addresses.join(", ")})`;
|
|
32878
|
+
throw new Error(`images: ${picture.path} was not uploaded: ${messageFor(error48)}. Nothing was posted${earlier}.`);
|
|
32879
|
+
}
|
|
32880
|
+
const address = `dispatch://${owner}/artifact/${uploaded.artifact.slug}@v${uploaded.version.number}`;
|
|
32881
|
+
addresses.push(address);
|
|
32882
|
+
lines.push(pictureLine(picture.name, address));
|
|
32883
|
+
}
|
|
32884
|
+
const posted = withPictureLines(text, lines);
|
|
32885
|
+
if (posted.length > limit.cap) {
|
|
32886
|
+
throw new Error(`${counted} ${overCapMessage(posted.length, limit.cap)} with the addresses Dispatch gave ` + `the uploads (${addresses.join(", ")}), so nothing was posted; ${fix}.`);
|
|
32887
|
+
}
|
|
32888
|
+
return posted;
|
|
32889
|
+
}
|
|
32890
|
+
var AGENT_ARTIFACT_REF = new RegExp(`^dispatch://agent/(${SESSION_ID_PATTERN})/artifact/([^/@]+)(?:@v(\\d+))?$`, "u");
|
|
32891
|
+
function parseAgentArtifactRef(ref) {
|
|
32892
|
+
const match = ref.match(AGENT_ARTIFACT_REF);
|
|
32893
|
+
if (match === null)
|
|
32894
|
+
return null;
|
|
32895
|
+
const [, session, slug, version2] = match;
|
|
32896
|
+
if (session === undefined || slug === undefined || version2 !== undefined && Number(version2) < 1) {
|
|
32897
|
+
return null;
|
|
32898
|
+
}
|
|
32899
|
+
return { session, slug, ...version2 === undefined ? {} : { version: Number(version2) } };
|
|
32900
|
+
}
|
|
32901
|
+
function datedBodies(records) {
|
|
32902
|
+
return records.map((record2) => ({ text: record2.body, at: record2.created_at }));
|
|
32903
|
+
}
|
|
32904
|
+
function askTexts({ ask, replies }) {
|
|
32905
|
+
return [
|
|
32906
|
+
{ text: ask.question, at: ask.created_at },
|
|
32907
|
+
...ask.answer?.text ? [{ text: ask.answer.text, at: ask.answer.at }] : [],
|
|
32908
|
+
...ask.resolution === undefined ? [] : [{ text: ask.resolution.reason, at: ask.resolution.at }],
|
|
32909
|
+
...datedBodies(replies)
|
|
32910
|
+
];
|
|
32911
|
+
}
|
|
32912
|
+
function eventTexts(event) {
|
|
32913
|
+
let texts;
|
|
32914
|
+
switch (event.type) {
|
|
32915
|
+
case "ask.opened":
|
|
32916
|
+
case "ask.anchor_refreshed":
|
|
32917
|
+
case "ask.edited":
|
|
32918
|
+
case "ask.handed_back":
|
|
32919
|
+
case "ask.answered":
|
|
32920
|
+
case "ask.resolved":
|
|
32921
|
+
texts = [
|
|
32922
|
+
event.payload.question,
|
|
32923
|
+
event.payload.answer?.text,
|
|
32924
|
+
event.payload.resolution?.reason
|
|
32925
|
+
];
|
|
32926
|
+
break;
|
|
32927
|
+
case "comment.created":
|
|
32928
|
+
case "comment.answered":
|
|
32929
|
+
case "comment.anchor_refreshed":
|
|
32930
|
+
case "comment.edited":
|
|
32931
|
+
case "comment.resolved":
|
|
32932
|
+
case "comment.reopened":
|
|
32933
|
+
case "suggestion.accepted":
|
|
32934
|
+
case "suggestion.rejected":
|
|
32935
|
+
case "message.created":
|
|
32936
|
+
case "message.answered":
|
|
32937
|
+
texts = [event.payload.body];
|
|
32938
|
+
break;
|
|
32939
|
+
default:
|
|
32940
|
+
texts = [];
|
|
32941
|
+
}
|
|
32942
|
+
return texts.filter((text) => typeof text === "string" && text !== "").map((text) => ({ text, at: event.created_at }));
|
|
32943
|
+
}
|
|
32944
|
+
function shownPictureLine(index, address, name, mimeType, bytes) {
|
|
32945
|
+
return `- image ${index}: ${address} (${name}, ${mimeType}, ${bytes.toLocaleString("en-US")} bytes)`;
|
|
32946
|
+
}
|
|
32947
|
+
var SHOWN_PICTURE_LINE = /^- image [1-9][0-9]*: (dispatch:\/\/[^\s()]+@v[1-9][0-9]*) \(/gm;
|
|
32948
|
+
function shownPictureAddresses(texts) {
|
|
32949
|
+
const addresses = new Set;
|
|
32950
|
+
for (const text of texts) {
|
|
32951
|
+
for (const match of text.matchAll(SHOWN_PICTURE_LINE))
|
|
32952
|
+
addresses.add(match[1]);
|
|
32953
|
+
}
|
|
32954
|
+
return [...addresses];
|
|
32955
|
+
}
|
|
32956
|
+
function pictureTarget(address) {
|
|
32957
|
+
const agent = parseAgentArtifactRef(address);
|
|
32958
|
+
if (agent !== null) {
|
|
32959
|
+
if (agent.version === undefined)
|
|
32960
|
+
return;
|
|
32961
|
+
return {
|
|
32962
|
+
key: `agent/${agent.session}/${agent.slug}`,
|
|
32963
|
+
read: (client) => client.getAgentArtifact(agent.session, agent.slug),
|
|
32964
|
+
version: agent.version
|
|
32965
|
+
};
|
|
32966
|
+
}
|
|
32967
|
+
const ref = parseDispatchRef(address);
|
|
32968
|
+
if (ref?.kind !== "artifact" || ref.version === undefined)
|
|
32969
|
+
return;
|
|
32970
|
+
const { owner, id: slug } = ref;
|
|
32971
|
+
return owner.kind === "issue" ? {
|
|
32972
|
+
key: `${owner.issue}/${slug}`,
|
|
32973
|
+
read: (client) => client.getIssueArtifact(owner.issue, slug),
|
|
32974
|
+
version: ref.version
|
|
32975
|
+
} : {
|
|
32976
|
+
key: `${owner.project}/${slug}`,
|
|
32977
|
+
read: (client) => client.getProjectArtifact(owner.project, slug),
|
|
32978
|
+
version: ref.version
|
|
32979
|
+
};
|
|
32980
|
+
}
|
|
32981
|
+
var SESSIONS_SHOWN_MAX = 64;
|
|
32982
|
+
var picturesShown = new Map;
|
|
32983
|
+
function shownPictures(sessionId) {
|
|
32984
|
+
const shown = picturesShown.get(sessionId) ?? new Set;
|
|
32985
|
+
picturesShown.delete(sessionId);
|
|
32986
|
+
picturesShown.set(sessionId, shown);
|
|
32987
|
+
const oldest = picturesShown.keys().next();
|
|
32988
|
+
if (picturesShown.size > SESSIONS_SHOWN_MAX && oldest.done !== true) {
|
|
32989
|
+
picturesShown.delete(oldest.value);
|
|
32990
|
+
}
|
|
32991
|
+
return shown;
|
|
32992
|
+
}
|
|
32993
|
+
function forgetShownPictures(sessionId) {
|
|
32994
|
+
picturesShown.delete(sessionId);
|
|
32995
|
+
}
|
|
32996
|
+
async function readPictures(client, addresses, shown) {
|
|
32997
|
+
if (addresses.length === 0)
|
|
32998
|
+
return { lines: [], images: [], shown: [] };
|
|
32999
|
+
const artifacts = new Map;
|
|
33000
|
+
const images = [];
|
|
33001
|
+
const showing = [];
|
|
33002
|
+
const lines = ["Pictures:"];
|
|
33003
|
+
let shownBytes = 0;
|
|
33004
|
+
const overBudget = `past this read's ${PICTURES_SHOWN_MAX_BYTES / 1024 / 1024} MiB of pictures; dispatch_doc_read shows it`;
|
|
33005
|
+
for (const address of addresses) {
|
|
33006
|
+
const skip = (reason) => lines.push(`- not shown: ${address} (${reason})`);
|
|
33007
|
+
if (shown?.has(address)) {
|
|
33008
|
+
skip("shown earlier this session; dispatch_doc_read shows it again");
|
|
33009
|
+
continue;
|
|
33010
|
+
}
|
|
33011
|
+
if (images.length === PICTURES_SHOWN_MAX) {
|
|
33012
|
+
skip(`past this read's ${PICTURES_SHOWN_MAX} pictures; dispatch_doc_read shows it`);
|
|
33013
|
+
continue;
|
|
33014
|
+
}
|
|
33015
|
+
const target = pictureTarget(address);
|
|
33016
|
+
if (target === undefined) {
|
|
33017
|
+
skip("names no versioned Dispatch artifact");
|
|
33018
|
+
continue;
|
|
33019
|
+
}
|
|
33020
|
+
try {
|
|
33021
|
+
let read = artifacts.get(target.key);
|
|
33022
|
+
if (read === undefined) {
|
|
33023
|
+
read = target.read(client);
|
|
33024
|
+
artifacts.set(target.key, read);
|
|
33025
|
+
}
|
|
33026
|
+
const artifact = await read;
|
|
33027
|
+
const version2 = artifact.versions.find((candidate) => candidate.number === target.version);
|
|
33028
|
+
if (version2 === undefined) {
|
|
33029
|
+
skip(`${artifact.name} has no version ${target.version}`);
|
|
33030
|
+
continue;
|
|
33031
|
+
}
|
|
33032
|
+
if (artifact.kind !== "image") {
|
|
33033
|
+
skip(`${artifact.name} is a ${artifact.kind}, not a picture`);
|
|
33034
|
+
continue;
|
|
33035
|
+
}
|
|
33036
|
+
const mime = version2.mime?.split(";")[0]?.trim();
|
|
33037
|
+
if (mime !== undefined && !isPictureType(mime)) {
|
|
33038
|
+
skip(`${artifact.name} is ${mime}, not ${PICTURE_TYPE_NAMES} a model is shown`);
|
|
33039
|
+
continue;
|
|
33040
|
+
}
|
|
33041
|
+
if (version2.size !== undefined && version2.size > PICTURE_SHOWN_MAX_BYTES) {
|
|
33042
|
+
skip(oversizePictureText(artifact.name, version2.size));
|
|
33043
|
+
continue;
|
|
33044
|
+
}
|
|
33045
|
+
if (version2.size !== undefined && shownBytes + version2.size > PICTURES_SHOWN_MAX_BYTES) {
|
|
33046
|
+
skip(overBudget);
|
|
33047
|
+
continue;
|
|
33048
|
+
}
|
|
33049
|
+
const file2 = await client.fileVersion(artifact.id, target.version);
|
|
33050
|
+
const image = toolImage(file2.bytes);
|
|
33051
|
+
if (image === undefined) {
|
|
33052
|
+
skip(sniffPictureType(file2.bytes) === undefined ? `${artifact.name} is not ${PICTURE_TYPE_NAMES} a model is shown` : oversizePictureText(artifact.name, file2.bytes.length));
|
|
33053
|
+
continue;
|
|
33054
|
+
}
|
|
33055
|
+
if (shownBytes + file2.bytes.length > PICTURES_SHOWN_MAX_BYTES) {
|
|
33056
|
+
skip(overBudget);
|
|
33057
|
+
continue;
|
|
33058
|
+
}
|
|
33059
|
+
shownBytes += file2.bytes.length;
|
|
33060
|
+
images.push(image);
|
|
33061
|
+
showing.push(address);
|
|
33062
|
+
lines.push(shownPictureLine(images.length, address, artifact.name, image.mimeType, file2.bytes.length));
|
|
33063
|
+
} catch (error48) {
|
|
33064
|
+
skip(`unavailable: ${messageFor(error48)}`);
|
|
33065
|
+
}
|
|
33066
|
+
}
|
|
33067
|
+
return { lines, images, shown: showing };
|
|
33068
|
+
}
|
|
33069
|
+
|
|
33070
|
+
// ../envoy-client/src/search-answer.ts
|
|
33071
|
+
function pageSummaryText(offset, count, total) {
|
|
33072
|
+
return `showing ${offset + 1}-${offset + count} of ${total}`;
|
|
33073
|
+
}
|
|
33074
|
+
function searchResultLine(result, baseUrl) {
|
|
33075
|
+
const href = new URL(result.href, baseUrl).toString();
|
|
33076
|
+
const { owner } = result;
|
|
33077
|
+
if (owner.kind === "document") {
|
|
33078
|
+
const reference = dispatchDocumentRef(owner.project, owner.slug);
|
|
33079
|
+
return `${reference} [document] ${owner.name} - ${result.kind}: ${snippetText(result.snippet)} -> ${href}`;
|
|
33080
|
+
}
|
|
33081
|
+
const artifactName = result.artifact ? ` ${result.artifact.name}` : "";
|
|
33082
|
+
const label = `${owner.key} [${owner.status}] ${owner.title} - ${result.kind}${artifactName}`;
|
|
33083
|
+
return `${label}: ${snippetText(result.snippet)} -> ${href}`;
|
|
33084
|
+
}
|
|
33085
|
+
function searchAnswer(search, query, offset, configUrl) {
|
|
33086
|
+
const results = search.results;
|
|
33087
|
+
const count = results.length;
|
|
33088
|
+
const lines = results.map((result) => searchResultLine(result, configUrl));
|
|
33089
|
+
const noun = count === 1 ? "result" : "results";
|
|
33090
|
+
const noResults = `No results for "${query}".`;
|
|
33091
|
+
if (typeof search.total !== "number") {
|
|
33092
|
+
if (offset !== undefined && offset > 0) {
|
|
33093
|
+
throw new Error(`Dispatch answered without a total: it predates search paging and ignored offset ${offset}, so this would be its first page again.`);
|
|
33094
|
+
}
|
|
33095
|
+
return {
|
|
33096
|
+
text: count === 0 ? noResults : [`${count} ${noun} for "${query}" (${search.took_ms} ms)`, ...lines].join(`
|
|
33097
|
+
`),
|
|
33098
|
+
details: { query, results }
|
|
33099
|
+
};
|
|
33100
|
+
}
|
|
33101
|
+
const { total, reachable, offset: pageOffset } = search;
|
|
33102
|
+
const end = pageOffset + count;
|
|
33103
|
+
const cut = reachable < total ? `Each kind lists only its best ${SEARCH_KIND_DEPTH} matches, so ${reachable} of the ${total} can be paged to; narrow the query or name a project to reach the rest.` : undefined;
|
|
33104
|
+
const details = {
|
|
33105
|
+
query,
|
|
33106
|
+
results,
|
|
33107
|
+
total,
|
|
33108
|
+
reachable,
|
|
33109
|
+
offset: pageOffset,
|
|
33110
|
+
limit: search.limit
|
|
33111
|
+
};
|
|
33112
|
+
if (count === 0) {
|
|
33113
|
+
return {
|
|
33114
|
+
text: total === 0 ? noResults : [
|
|
33115
|
+
`No results for "${query}" at offset ${pageOffset}: it matches ${total}, and the pages reach the first ${reachable}.`,
|
|
33116
|
+
...cut === undefined ? [] : [cut]
|
|
33117
|
+
].join(`
|
|
33118
|
+
`),
|
|
33119
|
+
details
|
|
33120
|
+
};
|
|
33121
|
+
}
|
|
33122
|
+
const showing = pageOffset === 0 && count === total ? "" : `${pageSummaryText(pageOffset, count, total)}, `;
|
|
33123
|
+
return {
|
|
33124
|
+
text: [
|
|
33125
|
+
`${count} ${noun} for "${query}" (${showing}${search.took_ms} ms)`,
|
|
33126
|
+
...cut === undefined ? [] : [cut],
|
|
33127
|
+
...lines,
|
|
33128
|
+
...end < reachable ? [`Next page: offset ${end}.`] : []
|
|
33129
|
+
].join(`
|
|
33130
|
+
`),
|
|
33131
|
+
details
|
|
33132
|
+
};
|
|
33133
|
+
}
|
|
33134
|
+
|
|
32707
33135
|
// ../envoy-client/src/dispatch-execute.ts
|
|
32708
33136
|
function documentTopic(artifact) {
|
|
32709
33137
|
return documentTopicOf(artifact.project, artifact.slug);
|
|
@@ -32931,59 +33359,11 @@ function askQuestionWithRef(args) {
|
|
|
32931
33359
|
function askQuestionProblem(withRef) {
|
|
32932
33360
|
return withRef.length > ASK_QUESTION_MAX ? `question plus ref ${overCapMessage(withRef.length, ASK_QUESTION_MAX)}; shorten the question or drop the ref` : undefined;
|
|
32933
33361
|
}
|
|
32934
|
-
|
|
32935
|
-
|
|
32936
|
-
|
|
32937
|
-
|
|
32938
|
-
|
|
32939
|
-
return null;
|
|
32940
|
-
}
|
|
32941
|
-
if (targetKind === undefined) {
|
|
32942
|
-
return {
|
|
32943
|
-
owner: { kind: "project", project },
|
|
32944
|
-
kind: "artifact",
|
|
32945
|
-
id: artifact2,
|
|
32946
|
-
...version3 === undefined ? {} : { version: Number(version3) }
|
|
32947
|
-
};
|
|
32948
|
-
}
|
|
32949
|
-
if (targetID === undefined || targetKind !== "ask" && targetKind !== "comment")
|
|
32950
|
-
return null;
|
|
32951
|
-
return {
|
|
32952
|
-
owner: { kind: "project", project },
|
|
32953
|
-
kind: targetKind,
|
|
32954
|
-
id: targetID,
|
|
32955
|
-
artifact: artifact2
|
|
32956
|
-
};
|
|
32957
|
-
}
|
|
32958
|
-
const issueReference = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9}-[1-9][0-9]*)(?:\/(spec)|\/(log)|\/(children)|\/artifact\/([^/@]+)(?:@v(\d+))?|\/ask\/([^/]+)|\/comment\/([^/]+)|\/message\/([^/]+))?$/);
|
|
32959
|
-
if (!issueReference)
|
|
32960
|
-
return null;
|
|
32961
|
-
const [, issue2, spec, log, children, artifact, version2, ask, comment, message] = issueReference;
|
|
32962
|
-
if (!issue2 || version2 !== undefined && Number(version2) < 1)
|
|
32963
|
-
return null;
|
|
32964
|
-
const owner = { kind: "issue", issue: issue2 };
|
|
32965
|
-
if (spec)
|
|
32966
|
-
return { owner, kind: "spec", id: spec };
|
|
32967
|
-
if (log)
|
|
32968
|
-
return { owner, kind: "log", id: log };
|
|
32969
|
-
if (children)
|
|
32970
|
-
return { owner, kind: "children", id: children };
|
|
32971
|
-
if (artifact) {
|
|
32972
|
-
return {
|
|
32973
|
-
owner,
|
|
32974
|
-
kind: "artifact",
|
|
32975
|
-
id: artifact,
|
|
32976
|
-
...version2 === undefined ? {} : { version: Number(version2) }
|
|
32977
|
-
};
|
|
32978
|
-
}
|
|
32979
|
-
if (ask)
|
|
32980
|
-
return { owner, kind: "ask", id: ask };
|
|
32981
|
-
if (comment)
|
|
32982
|
-
return { owner, kind: "comment", id: comment };
|
|
32983
|
-
if (message)
|
|
32984
|
-
return { owner, kind: "message", id: message };
|
|
32985
|
-
return { owner, kind: "issue", id: issue2 };
|
|
32986
|
-
}
|
|
33362
|
+
var pictureSendingTools = {
|
|
33363
|
+
dispatch_ask: true,
|
|
33364
|
+
dispatch_comment: true,
|
|
33365
|
+
dispatch_message: true
|
|
33366
|
+
};
|
|
32987
33367
|
function refTarget(ref, kind, id) {
|
|
32988
33368
|
const ownerRef = ref.owner.kind === "issue" ? dispatchIssueRef(ref.owner.issue) : dispatchDocumentRef(ref.owner.project, `${ref.artifact}`);
|
|
32989
33369
|
return dispatchChildRef(ownerRef, kind, id);
|
|
@@ -33163,6 +33543,15 @@ function dispatchRefFromUrl(value, serverUrl) {
|
|
|
33163
33543
|
return `dispatch://${key}/message/${message}`;
|
|
33164
33544
|
return `dispatch://${key}`;
|
|
33165
33545
|
}
|
|
33546
|
+
const agentArtifactPage = url2.pathname.match(/^\/agents\/([^/]+)\/artifacts\/([^/]+)\/?$/);
|
|
33547
|
+
if (agentArtifactPage) {
|
|
33548
|
+
const [, session, slug2] = agentArtifactPage;
|
|
33549
|
+
try {
|
|
33550
|
+
return `dispatch://agent/${decodeURIComponent(session ?? "")}/artifact/${decodeURIComponent(slug2 ?? "")}${versionOf("v")}`;
|
|
33551
|
+
} catch {
|
|
33552
|
+
return;
|
|
33553
|
+
}
|
|
33554
|
+
}
|
|
33166
33555
|
const documentPage = url2.pathname.match(/^\/projects\/([A-Z][A-Z0-9]{1,9})\/documents\/([^/]+)\/?$/);
|
|
33167
33556
|
if (!documentPage)
|
|
33168
33557
|
return;
|
|
@@ -33173,7 +33562,7 @@ function dispatchRefFromUrl(value, serverUrl) {
|
|
|
33173
33562
|
return;
|
|
33174
33563
|
return item === undefined ? document : `${document}/${item.kind}/${item.id}`;
|
|
33175
33564
|
}
|
|
33176
|
-
var refGrammarProblem = "ref must be a valid dispatch:// reference such as dispatch://KEY-1, " + "dispatch://KEY-1/ask/<uuid>, dispatch://KEY-1/comment/<uuid>, " + "dispatch://KEY-1/message/<uuid>, dispatch://KEY-1/artifact/<slug>,
|
|
33565
|
+
var refGrammarProblem = "ref must be a valid dispatch:// reference such as dispatch://KEY-1, " + "dispatch://KEY-1/ask/<uuid>, dispatch://KEY-1/comment/<uuid>, " + "dispatch://KEY-1/message/<uuid>, dispatch://KEY-1/artifact/<slug>, " + "dispatch://PROJECT/artifact/<document-ref> (an artifact id, slug, or filename), or " + "dispatch://agent/<session id>/artifact/<slug> (a picture in a conversation on the Agents page, " + "for dispatch_doc_read); a dashboard URL on this Dispatch server is accepted too";
|
|
33177
33566
|
var ownerRequiredProblem = "issue is required; supply issue or set LEGION_ISSUE";
|
|
33178
33567
|
var toolSchemas = new Map;
|
|
33179
33568
|
function toolSchema(tool) {
|
|
@@ -33197,6 +33586,16 @@ async function resolveOwnerArguments(tool, input, cwd, env, exec, serverUrl, pro
|
|
|
33197
33586
|
const refText = dispatchRefFromUrl(refArgument, serverUrl) ?? refArgument;
|
|
33198
33587
|
if (refText !== refArgument)
|
|
33199
33588
|
args = { ...input, ref: refText };
|
|
33589
|
+
const agentArtifact = parseAgentArtifactRef(refText);
|
|
33590
|
+
if (agentArtifact !== null) {
|
|
33591
|
+
if (tool !== "dispatch_doc_read") {
|
|
33592
|
+
problems.push(`ref ${refText} names a picture in a conversation on the Agents page; only dispatch_doc_read reads one`);
|
|
33593
|
+
}
|
|
33594
|
+
if (args.issue !== undefined || args.project !== undefined || args.artifact !== undefined) {
|
|
33595
|
+
problems.push("a dispatch://agent/... ref names its upload alone: name no issue, project, or artifact with it");
|
|
33596
|
+
}
|
|
33597
|
+
return { args, ref: null, owner: null, agentArtifact };
|
|
33598
|
+
}
|
|
33200
33599
|
ref = parseDispatchRef(refText);
|
|
33201
33600
|
if (ref === null) {
|
|
33202
33601
|
problems.push(tool === "dispatch_ask" && !refText.startsWith("dispatch://") ? "ref must be a dispatch:// reference" : refGrammarProblem);
|
|
@@ -33542,6 +33941,7 @@ function eventHead(event) {
|
|
|
33542
33941
|
case "ask.answered":
|
|
33543
33942
|
return `${textHead(event.payload.question)} -> ${textHead(askAnswerText(event.payload.answer))}`;
|
|
33544
33943
|
case "comment.created":
|
|
33944
|
+
case "comment.answered":
|
|
33545
33945
|
case "comment.anchor_refreshed":
|
|
33546
33946
|
case "comment.edited":
|
|
33547
33947
|
case "comment.resolved":
|
|
@@ -33727,20 +34127,42 @@ async function blockAsks(client, resolved, state) {
|
|
|
33727
34127
|
const asks = await (resolved.issue === undefined ? client.getArtifactAsks(resolved.artifact.id, state) : client.listIssueAsks(resolved.issue.key, state));
|
|
33728
34128
|
return asks.filter((ask) => typeof ask.block_id === "string" && ask.block_artifact?.id === resolved.artifact.id);
|
|
33729
34129
|
}
|
|
33730
|
-
async function readUploadedFile(client,
|
|
33731
|
-
const { artifact } = resolved;
|
|
34130
|
+
async function readUploadedFile(client, artifact, details, requested, owner, shown) {
|
|
33732
34131
|
const latest = Math.max(0, ...artifact.versions.map((version2) => version2.number));
|
|
33733
34132
|
const number4 = requested ?? latest;
|
|
33734
|
-
const file2 = await client.fileVersion(artifact.id, number4);
|
|
33735
34133
|
const of = number4 === latest ? "" : ` of ${latest}`;
|
|
34134
|
+
const bytesRoute = `GET /api/v1/artifacts/${artifact.id}/versions/${number4} serves its bytes.`;
|
|
34135
|
+
const tooLarge = (mime, bytes) => ({
|
|
34136
|
+
text: `${oversizePictureText(artifact.name, bytes)}, so dispatch_doc_read cannot show this ` + `uploaded ${mime} picture (version ${number4}${of}). ${bytesRoute}`,
|
|
34137
|
+
details
|
|
34138
|
+
});
|
|
34139
|
+
const stated = artifact.versions.find((version2) => version2.number === number4);
|
|
34140
|
+
if (artifact.kind === "image" && stated?.size !== undefined && stated.size > PICTURE_SHOWN_MAX_BYTES) {
|
|
34141
|
+
return tooLarge(stated.mime ?? "image", stated.size);
|
|
34142
|
+
}
|
|
34143
|
+
const file2 = await client.fileVersion(artifact.id, number4);
|
|
33736
34144
|
const size = `${file2.bytes.length.toLocaleString("en-US")} bytes`;
|
|
33737
|
-
|
|
34145
|
+
if (artifact.kind === "image") {
|
|
34146
|
+
const image = toolImage(file2.bytes);
|
|
34147
|
+
if (image !== undefined) {
|
|
34148
|
+
const address = `dispatch://${owner}/artifact/${artifact.slug}@v${number4}`;
|
|
34149
|
+
shown?.add(address);
|
|
34150
|
+
return {
|
|
34151
|
+
text: `Picture ${artifact.name}: ${image.mimeType}, version ${number4}${of}, ${size}.
|
|
34152
|
+
` + shownPictureLine(1, address, artifact.name, image.mimeType, file2.bytes.length),
|
|
34153
|
+
details,
|
|
34154
|
+
images: [image]
|
|
34155
|
+
};
|
|
34156
|
+
}
|
|
34157
|
+
if (sniffPictureType(file2.bytes) !== undefined)
|
|
34158
|
+
return tooLarge(file2.mime, file2.bytes.length);
|
|
34159
|
+
}
|
|
33738
34160
|
let text;
|
|
33739
34161
|
try {
|
|
33740
34162
|
text = new TextDecoder("utf-8", { fatal: true }).decode(file2.bytes);
|
|
33741
34163
|
} catch {
|
|
33742
34164
|
return {
|
|
33743
|
-
text: `${artifact.name} is an uploaded ${file2.mime} file (version ${number4}${of}, ${size}) that is not ` + `UTF-8 text, so dispatch_doc_read cannot show it.
|
|
34165
|
+
text: `${artifact.name} is an uploaded ${file2.mime} file (version ${number4}${of}, ${size}) that is not ` + `UTF-8 text, so dispatch_doc_read cannot show it. ${bytesRoute}`,
|
|
33744
34166
|
details
|
|
33745
34167
|
};
|
|
33746
34168
|
}
|
|
@@ -33881,6 +34303,7 @@ async function executeDispatchTool(input) {
|
|
|
33881
34303
|
problems.push(...formatZodIssues(issues, schema));
|
|
33882
34304
|
}
|
|
33883
34305
|
problems.push(...argumentProblems(input.tool, ownerArguments.args));
|
|
34306
|
+
const pictures = pictureSendingTools[input.tool] === true ? await localPictures(ownerArguments.args.images, input.cwd, problems) : [];
|
|
33884
34307
|
if (problems.length > 0)
|
|
33885
34308
|
throw new ToolInputError(input.tool, problems);
|
|
33886
34309
|
const dispatchClient = () => new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
@@ -33932,6 +34355,17 @@ async function executeDispatchTool(input) {
|
|
|
33932
34355
|
throw new Error("issue or project is required");
|
|
33933
34356
|
return owner;
|
|
33934
34357
|
};
|
|
34358
|
+
const postWithPictures = (text, limit, owner2, upload) => textWithPictures(input.tool, text, pictures, limit, owner2, upload, actor);
|
|
34359
|
+
const writePictures = (resolved, text, limit) => {
|
|
34360
|
+
if (resolved?.owner.kind === "project") {
|
|
34361
|
+
const { project } = resolved.owner;
|
|
34362
|
+
return postWithPictures(text, limit, project, (upload) => client.projectArtifact(project, upload));
|
|
34363
|
+
}
|
|
34364
|
+
const issueKey = issue2();
|
|
34365
|
+
return postWithPictures(text, limit, issueKey, (upload) => client.artifact(issueKey, upload));
|
|
34366
|
+
};
|
|
34367
|
+
const hostSession = input.sessionId?.trim();
|
|
34368
|
+
const shown = hostSession ? shownPictures(hostSession) : undefined;
|
|
33935
34369
|
switch (input.tool) {
|
|
33936
34370
|
case "dispatch_issue": {
|
|
33937
34371
|
const project = stringArg(args, "project");
|
|
@@ -34234,8 +34668,15 @@ async function executeDispatchTool(input) {
|
|
|
34234
34668
|
const multiple = optionalBoolean(args, "multiple");
|
|
34235
34669
|
const urgency = askUrgency(args);
|
|
34236
34670
|
const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
|
|
34671
|
+
const withRef = askQuestionWithRef(args);
|
|
34672
|
+
const refAppended = withRef !== stringArg(args, "question");
|
|
34673
|
+
const question = await writePictures(resolved, withRef, {
|
|
34674
|
+
cap: ASK_QUESTION_MAX,
|
|
34675
|
+
text: refAppended ? "question plus ref" : "question",
|
|
34676
|
+
shorten: refAppended ? "shorten the question, drop the ref" : "shorten the question"
|
|
34677
|
+
});
|
|
34237
34678
|
const askInput = {
|
|
34238
|
-
question
|
|
34679
|
+
question,
|
|
34239
34680
|
...Array.isArray(options) ? { options } : {},
|
|
34240
34681
|
...multiple === undefined ? {} : { multiple },
|
|
34241
34682
|
...urgency === undefined ? {} : { urgency },
|
|
@@ -34293,8 +34734,13 @@ ${followsAsk(askOwner)}`,
|
|
|
34293
34734
|
]);
|
|
34294
34735
|
}
|
|
34295
34736
|
const requestedTurn = optionalString(args, "turn");
|
|
34737
|
+
const body = await writePictures(resolved, stringArg(args, "body"), {
|
|
34738
|
+
cap: DISPATCH_BODY_MAX,
|
|
34739
|
+
text: "body",
|
|
34740
|
+
shorten: "shorten the body"
|
|
34741
|
+
});
|
|
34296
34742
|
const commentInput = {
|
|
34297
|
-
body
|
|
34743
|
+
body,
|
|
34298
34744
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
34299
34745
|
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
34300
34746
|
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
@@ -34354,16 +34800,29 @@ ${followsAsk(askOwner)}`,
|
|
|
34354
34800
|
}
|
|
34355
34801
|
case "dispatch_message": {
|
|
34356
34802
|
const inReplyTo = messageInReplyTo(args);
|
|
34357
|
-
const
|
|
34803
|
+
const written = stringArg(args, "body");
|
|
34804
|
+
const bodyLimit = {
|
|
34805
|
+
cap: DISPATCH_BODY_MAX,
|
|
34806
|
+
text: "body",
|
|
34807
|
+
shorten: "shorten the body"
|
|
34808
|
+
};
|
|
34358
34809
|
if (owner === null && inReplyTo !== undefined) {
|
|
34359
|
-
|
|
34360
|
-
if (
|
|
34810
|
+
let body2 = written;
|
|
34811
|
+
if (pictures.length > 0) {
|
|
34812
|
+
const sessionId = input.sessionId?.trim();
|
|
34813
|
+
if (!sessionId) {
|
|
34814
|
+
throw new Error("host session id is required to send pictures in a direct-message reply");
|
|
34815
|
+
}
|
|
34816
|
+
body2 = await postWithPictures(written, bodyLimit, `agent/${sessionId}`, (upload) => client.agentArtifact(sessionId, upload));
|
|
34817
|
+
}
|
|
34818
|
+
const reply = await client.messageReply(inReplyTo, { body: body2, attempt: 1, actor }, { followUp: true });
|
|
34819
|
+
if (reply.duplicate === true && reply.body === body2) {
|
|
34361
34820
|
return {
|
|
34362
34821
|
text: `Dispatch already has this exact text in the conversation (message ${reply.id}); ` + "nothing new was posted. Send different text if you have more to say.",
|
|
34363
34822
|
details: { message: reply.id, in_reply_to: inReplyTo, posted: false, duplicate: true }
|
|
34364
34823
|
};
|
|
34365
34824
|
}
|
|
34366
|
-
if (reply.body !==
|
|
34825
|
+
if (reply.body !== body2) {
|
|
34367
34826
|
return {
|
|
34368
34827
|
text: `Message ${inReplyTo} was already answered by message ${reply.id}; Dispatch kept ` + "that reply and posted nothing. Wait for their next message rather than answering " + "this one again.",
|
|
34369
34828
|
details: { message: reply.id, in_reply_to: inReplyTo, posted: false }
|
|
@@ -34383,6 +34842,7 @@ ${followsAsk(askOwner)}`,
|
|
|
34383
34842
|
};
|
|
34384
34843
|
}
|
|
34385
34844
|
const issueKey = issue2();
|
|
34845
|
+
const body = await postWithPictures(written, bodyLimit, issueKey, (upload) => client.artifact(issueKey, upload));
|
|
34386
34846
|
const message = await client.message(issueKey, {
|
|
34387
34847
|
body,
|
|
34388
34848
|
...inReplyTo === undefined ? {} : { in_reply_to: inReplyTo },
|
|
@@ -34446,11 +34906,20 @@ ${followsAsk(askOwner)}`,
|
|
|
34446
34906
|
};
|
|
34447
34907
|
}
|
|
34448
34908
|
case "dispatch_doc_read": {
|
|
34909
|
+
const { agentArtifact } = ownerArguments;
|
|
34910
|
+
if (agentArtifact !== undefined) {
|
|
34911
|
+
const artifact = await client.getAgentArtifact(agentArtifact.session, agentArtifact.slug);
|
|
34912
|
+
return readUploadedFile(client, artifact, { session: agentArtifact.session, artifact: artifact.slug }, optionalNumber(args, "version") ?? agentArtifact.version, `agent/${agentArtifact.session}`, shown);
|
|
34913
|
+
}
|
|
34449
34914
|
const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
|
|
34450
34915
|
const resolved = await resolveDocument(documentOwner(), artifactReference);
|
|
34451
34916
|
const version2 = optionalNumber(args, "version") ?? ownerArguments.ref?.version;
|
|
34452
34917
|
if (resolved.artifact.kind === "file" || resolved.artifact.kind === "image") {
|
|
34453
|
-
|
|
34918
|
+
const { artifact } = resolved;
|
|
34919
|
+
return readUploadedFile(client, artifact, resolved.owner.kind === "project" ? {
|
|
34920
|
+
project: artifact.project,
|
|
34921
|
+
document: documentLabel(artifact.project, artifact.slug)
|
|
34922
|
+
} : { issue: resolved.issue?.key }, version2, resolved.owner.kind === "project" ? resolved.owner.project : resolved.owner.issue, shown);
|
|
34454
34923
|
}
|
|
34455
34924
|
const documentPromise = client.docRead(resolved.artifact.id, version2);
|
|
34456
34925
|
const marksPromise = openArtifactMarks(client, resolved);
|
|
@@ -34508,7 +34977,7 @@ ${trailer.join(`
|
|
|
34508
34977
|
const content = optionalString(args, "content");
|
|
34509
34978
|
const artifactInput = content === undefined ? {
|
|
34510
34979
|
name,
|
|
34511
|
-
file: Bun.file(
|
|
34980
|
+
file: Bun.file(resolvePath2(input.cwd, stringArg(args, "path"))),
|
|
34512
34981
|
...summary === undefined ? {} : { summary },
|
|
34513
34982
|
actor
|
|
34514
34983
|
} : {
|
|
@@ -34561,18 +35030,29 @@ ${trailer.join(`
|
|
|
34561
35030
|
}
|
|
34562
35031
|
case "dispatch_read": {
|
|
34563
35032
|
const message = optionalString(args, "message");
|
|
35033
|
+
const picturesOf = (texts) => readPictures(client, picturesNewestFirst(texts), shown);
|
|
35034
|
+
const showImages = (pictures3) => {
|
|
35035
|
+
for (const address of pictures3.shown)
|
|
35036
|
+
shown?.add(address);
|
|
35037
|
+
return pictures3.images.length === 0 ? {} : { images: pictures3.images };
|
|
35038
|
+
};
|
|
34564
35039
|
if (message !== undefined) {
|
|
34565
35040
|
const sessionId = input.sessionId?.trim();
|
|
34566
35041
|
if (!sessionId)
|
|
34567
35042
|
throw new Error("host session id is required for dispatch_read({message})");
|
|
34568
35043
|
const thread = await client.getMessageThread(messageIdOf(message), sessionId);
|
|
34569
35044
|
const issueKey2 = thread.message.issue_key;
|
|
35045
|
+
const [pictures3, graph2] = await Promise.all([
|
|
35046
|
+
picturesOf(datedBodies([thread.message, ...thread.replies])),
|
|
35047
|
+
issueKey2 === null ? [] : graphSections(client, dispatchChildRef(dispatchIssueRef(issueKey2), "message", thread.message.id))
|
|
35048
|
+
]);
|
|
34570
35049
|
return {
|
|
34571
|
-
text: messageSummary(thread,
|
|
35050
|
+
text: messageSummary(thread, [...pictures3.lines, ...graph2]),
|
|
34572
35051
|
details: {
|
|
34573
35052
|
message: thread.message.id,
|
|
34574
35053
|
...issueKey2 === null ? {} : { issue: issueKey2 }
|
|
34575
|
-
}
|
|
35054
|
+
},
|
|
35055
|
+
...showImages(pictures3)
|
|
34576
35056
|
};
|
|
34577
35057
|
}
|
|
34578
35058
|
if (ownerArguments.ref?.kind === "ask") {
|
|
@@ -34580,9 +35060,14 @@ ${trailer.join(`
|
|
|
34580
35060
|
const id = await resolveIdPrefix(input.tool, "ask", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.listIssueAsks(ref.owner.issue) : client.getArtifactAsks((await resolveDocument(ref.owner, ref.artifact)).artifact.id, "all"));
|
|
34581
35061
|
const askRead = await client.getAsk(id);
|
|
34582
35062
|
const askRef = refTarget(ref, "ask", id);
|
|
35063
|
+
const [pictures3, graph2] = await Promise.all([
|
|
35064
|
+
picturesOf(askTexts(askRead)),
|
|
35065
|
+
graphSections(client, askRef)
|
|
35066
|
+
]);
|
|
34583
35067
|
return {
|
|
34584
|
-
text: askSummary(askRead,
|
|
34585
|
-
details: ref.owner.kind === "project" ? { project: ref.owner.project } : { issue: ref.owner.issue }
|
|
35068
|
+
text: askSummary(askRead, [...pictures3.lines, ...graph2]),
|
|
35069
|
+
details: ref.owner.kind === "project" ? { project: ref.owner.project } : { issue: ref.owner.issue },
|
|
35070
|
+
...showImages(pictures3)
|
|
34586
35071
|
};
|
|
34587
35072
|
}
|
|
34588
35073
|
if (ownerArguments.ref?.kind === "comment") {
|
|
@@ -34590,9 +35075,14 @@ ${trailer.join(`
|
|
|
34590
35075
|
const id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.getComments(ref.owner.issue) : client.getArtifactComments((await resolveDocument(ref.owner, ref.artifact)).artifact.id));
|
|
34591
35076
|
const comment = await client.getComment(id);
|
|
34592
35077
|
const commentRef = refTarget(ref, "comment", id);
|
|
35078
|
+
const [pictures3, graph2] = await Promise.all([
|
|
35079
|
+
picturesOf(datedBodies([comment.comment, ...comment.replies])),
|
|
35080
|
+
graphSections(client, commentRef)
|
|
35081
|
+
]);
|
|
34593
35082
|
return {
|
|
34594
|
-
text: commentSummary(comment,
|
|
34595
|
-
details: ref.owner.kind === "project" ? { project: ref.owner.project } : { issue: comment.comment.issue_key }
|
|
35083
|
+
text: commentSummary(comment, [...pictures3.lines, ...graph2]),
|
|
35084
|
+
details: ref.owner.kind === "project" ? { project: ref.owner.project } : { issue: comment.comment.issue_key },
|
|
35085
|
+
...showImages(pictures3)
|
|
34596
35086
|
};
|
|
34597
35087
|
}
|
|
34598
35088
|
if (ownerArguments.ref?.kind === "message") {
|
|
@@ -34601,9 +35091,14 @@ ${trailer.join(`
|
|
|
34601
35091
|
}
|
|
34602
35092
|
const messageRead = await client.getMessage(ownerArguments.ref.owner.issue, ownerArguments.ref.id);
|
|
34603
35093
|
const messageRef = dispatchChildRef(dispatchIssueRef(ownerArguments.ref.owner.issue), "message", ownerArguments.ref.id);
|
|
35094
|
+
const [pictures3, graph2] = await Promise.all([
|
|
35095
|
+
picturesOf(datedBodies([messageRead.message, ...messageRead.replies])),
|
|
35096
|
+
graphSections(client, messageRef)
|
|
35097
|
+
]);
|
|
34604
35098
|
return {
|
|
34605
|
-
text: messageSummary(messageRead,
|
|
34606
|
-
details: { issue: messageRead.message.issue_key }
|
|
35099
|
+
text: messageSummary(messageRead, [...pictures3.lines, ...graph2]),
|
|
35100
|
+
details: { issue: messageRead.message.issue_key },
|
|
35101
|
+
...showImages(pictures3)
|
|
34607
35102
|
};
|
|
34608
35103
|
}
|
|
34609
35104
|
if (documentOwner().kind === "project") {
|
|
@@ -34627,9 +35122,12 @@ ${trailer.join(`
|
|
|
34627
35122
|
const issueKey = issue2();
|
|
34628
35123
|
if (ownerArguments.ref?.kind === "log") {
|
|
34629
35124
|
const read2 = await client.read(issueKey);
|
|
35125
|
+
const pictures3 = await picturesOf(read2.events.flatMap(eventTexts));
|
|
34630
35126
|
return {
|
|
34631
|
-
text: logSummary(read2.issue, read2.events),
|
|
34632
|
-
|
|
35127
|
+
text: [logSummary(read2.issue, read2.events), ...pictures3.lines].join(`
|
|
35128
|
+
`),
|
|
35129
|
+
details: { issue: read2.issue.key },
|
|
35130
|
+
...showImages(pictures3)
|
|
34633
35131
|
};
|
|
34634
35132
|
}
|
|
34635
35133
|
if (ownerArguments.ref?.kind === "children") {
|
|
@@ -34642,14 +35140,19 @@ ${trailer.join(`
|
|
|
34642
35140
|
const readPromise = client.read(issueKey);
|
|
34643
35141
|
const referencesPromise = issueReferencesOrUnavailable(client, issueKey);
|
|
34644
35142
|
const read = await readPromise;
|
|
34645
|
-
const [references, graph, titles] = await Promise.all([
|
|
35143
|
+
const [references, graph, titles, pictures2] = await Promise.all([
|
|
34646
35144
|
referencesPromise,
|
|
34647
35145
|
graphSections(client, dispatchIssueRef(read.issue.key)),
|
|
34648
|
-
liveSessionTitles(client, holdsSession(read.issue.claim) || routeHeldBySession(read.issue))
|
|
35146
|
+
liveSessionTitles(client, holdsSession(read.issue.claim) || routeHeldBySession(read.issue)),
|
|
35147
|
+
picturesOf([
|
|
35148
|
+
...read.issue.open_asks.map((ask) => ({ text: ask.question, at: ask.created_at })),
|
|
35149
|
+
...read.events.flatMap(eventTexts)
|
|
35150
|
+
])
|
|
34649
35151
|
]);
|
|
34650
35152
|
return {
|
|
34651
|
-
text: issueSummary(read.issue, read.events, references, graph, titles),
|
|
34652
|
-
details: { issue: read.issue.key }
|
|
35153
|
+
text: issueSummary(read.issue, read.events, references, [...pictures2.lines, ...graph], titles),
|
|
35154
|
+
details: { issue: read.issue.key },
|
|
35155
|
+
...showImages(pictures2)
|
|
34653
35156
|
};
|
|
34654
35157
|
}
|
|
34655
35158
|
default:
|
|
@@ -35385,6 +35888,83 @@ class AgentStreamPublisher {
|
|
|
35385
35888
|
}
|
|
35386
35889
|
}
|
|
35387
35890
|
|
|
35891
|
+
// src/delivery-pictures.ts
|
|
35892
|
+
async function withDeliveredPictures(text, addresses, client, named, shown) {
|
|
35893
|
+
const textAlone = { content: text, shown: [] };
|
|
35894
|
+
if (addresses.length === 0)
|
|
35895
|
+
return textAlone;
|
|
35896
|
+
let dispatch;
|
|
35897
|
+
try {
|
|
35898
|
+
dispatch = client();
|
|
35899
|
+
} catch {
|
|
35900
|
+
return textAlone;
|
|
35901
|
+
}
|
|
35902
|
+
if (dispatch === undefined)
|
|
35903
|
+
return textAlone;
|
|
35904
|
+
const unique = [...new Set(addresses)];
|
|
35905
|
+
const shownEarlier = unique.some((address) => shown.has(address));
|
|
35906
|
+
const pictures = await readPictures(dispatch, unique, shown);
|
|
35907
|
+
const content = named ? `${text}
|
|
35908
|
+
|
|
35909
|
+
${pictures.lines.join(`
|
|
35910
|
+
`)}` : text;
|
|
35911
|
+
if (pictures.images.length === 0)
|
|
35912
|
+
return shownEarlier ? { content, shown: [] } : textAlone;
|
|
35913
|
+
return {
|
|
35914
|
+
content: [{ type: "text", text: content }, ...imageBlocks(pictures.images)],
|
|
35915
|
+
shown: pictures.shown
|
|
35916
|
+
};
|
|
35917
|
+
}
|
|
35918
|
+
function entryMessage(entry) {
|
|
35919
|
+
if (typeof entry !== "object" || entry === null || !("type" in entry))
|
|
35920
|
+
return;
|
|
35921
|
+
if (entry.type === "custom_message") {
|
|
35922
|
+
return { role: "custom", content: "content" in entry ? entry.content : undefined };
|
|
35923
|
+
}
|
|
35924
|
+
if (entry.type !== "message" || !("message" in entry))
|
|
35925
|
+
return;
|
|
35926
|
+
const { message } = entry;
|
|
35927
|
+
if (typeof message !== "object" || message === null)
|
|
35928
|
+
return;
|
|
35929
|
+
return {
|
|
35930
|
+
role: "role" in message ? message.role : undefined,
|
|
35931
|
+
content: "content" in message ? message.content : undefined
|
|
35932
|
+
};
|
|
35933
|
+
}
|
|
35934
|
+
function transcriptPictures(entries) {
|
|
35935
|
+
const shown = new Set;
|
|
35936
|
+
for (const entry of entries) {
|
|
35937
|
+
const message = entryMessage(entry);
|
|
35938
|
+
if (message === undefined)
|
|
35939
|
+
continue;
|
|
35940
|
+
const { content } = message;
|
|
35941
|
+
const texts = [];
|
|
35942
|
+
let images = 0;
|
|
35943
|
+
if (typeof content === "string")
|
|
35944
|
+
texts.push(content);
|
|
35945
|
+
else if (Array.isArray(content)) {
|
|
35946
|
+
for (const block of content) {
|
|
35947
|
+
if (typeof block !== "object" || block === null || !("type" in block))
|
|
35948
|
+
continue;
|
|
35949
|
+
if (block.type === "image")
|
|
35950
|
+
images++;
|
|
35951
|
+
if (block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
35952
|
+
texts.push(block.text);
|
|
35953
|
+
}
|
|
35954
|
+
}
|
|
35955
|
+
}
|
|
35956
|
+
for (const address of shownPictureAddresses(texts))
|
|
35957
|
+
shown.add(address);
|
|
35958
|
+
if (message.role !== "user" || images === 0)
|
|
35959
|
+
continue;
|
|
35960
|
+
const embedded = new Set(texts.flatMap(pictureAddresses));
|
|
35961
|
+
if (embedded.size === images)
|
|
35962
|
+
for (const address of embedded)
|
|
35963
|
+
shown.add(address);
|
|
35964
|
+
}
|
|
35965
|
+
return [...shown];
|
|
35966
|
+
}
|
|
35967
|
+
|
|
35388
35968
|
// src/dispatch-first.ts
|
|
35389
35969
|
function withDispatchFirst(messages, context) {
|
|
35390
35970
|
let index = 0;
|
|
@@ -35742,8 +36322,8 @@ function subagentSessionCheck() {
|
|
|
35742
36322
|
}
|
|
35743
36323
|
|
|
35744
36324
|
// src/tool-result.ts
|
|
35745
|
-
function toolSuccess(text, details = {}) {
|
|
35746
|
-
return { content: [{ type: "text", text }], details };
|
|
36325
|
+
function toolSuccess(text, details = {}, images = []) {
|
|
36326
|
+
return { content: [{ type: "text", text }, ...imageBlocks(images)], details };
|
|
35747
36327
|
}
|
|
35748
36328
|
function toolFailure(error48) {
|
|
35749
36329
|
return { content: [{ type: "text", text: messageFor(error48) }], details: {}, isError: true };
|
|
@@ -35760,7 +36340,8 @@ function renderEnvoyMessage(message, options, theme) {
|
|
|
35760
36340
|
box.setIgnoreTight(true);
|
|
35761
36341
|
box.addChild(new Text(theme.fg("customMessageLabel", theme.bold(message.customType)), 0, 0));
|
|
35762
36342
|
box.addChild(new Spacer(1));
|
|
35763
|
-
let text = message.content
|
|
36343
|
+
let text = typeof message.content === "string" ? message.content : message.content.map((block) => block.type === "text" ? block.text : `[picture: ${block.mimeType}]`).join(`
|
|
36344
|
+
`);
|
|
35764
36345
|
if (!options.expanded) {
|
|
35765
36346
|
const lines = text.split(`
|
|
35766
36347
|
`);
|
|
@@ -35805,6 +36386,11 @@ async function copySessionID(copyToClipboard2, sessionID) {
|
|
|
35805
36386
|
// extensions/envoy.ts
|
|
35806
36387
|
var codec2 = import_nats2.StringCodec();
|
|
35807
36388
|
var NATS_RETRY_INTERVAL_MS = 15000;
|
|
36389
|
+
var DELIVERY_PICTURES_TIMEOUT_MS = 20000;
|
|
36390
|
+
function timedDispatchClient(timeoutMs) {
|
|
36391
|
+
const config2 = activeDispatchConfig(process.env, { cwd: process.cwd() });
|
|
36392
|
+
return config2 === null ? undefined : new DispatchClient(config2.url, config2.token, fetch, AbortSignal.timeout(timeoutMs));
|
|
36393
|
+
}
|
|
35808
36394
|
var CAPABILITIES_WITHOUT_BTW = DELIVERY_CAPABILITIES.filter((capability) => capability !== "btw");
|
|
35809
36395
|
var ClaimIdentitySchema = DedupeIdentitySchema.extend({
|
|
35810
36396
|
event_id: DedupeIdentitySchema.shape.event_id.catch(undefined),
|
|
@@ -35965,12 +36551,12 @@ function envoyExtension(pi) {
|
|
|
35965
36551
|
});
|
|
35966
36552
|
};
|
|
35967
36553
|
const queryOpenAsks = async (requestedSessionID, since) => {
|
|
35968
|
-
const
|
|
35969
|
-
if (
|
|
36554
|
+
const dispatch = timedDispatchClient(OPEN_ASKS_TIMEOUT_MS);
|
|
36555
|
+
if (dispatch === undefined)
|
|
35970
36556
|
return null;
|
|
35971
|
-
const snapshot = await
|
|
36557
|
+
const snapshot = await dispatch.openAsks(requestedSessionID, since);
|
|
35972
36558
|
availabilityWarningSessionIDs.delete(requestedSessionID);
|
|
35973
|
-
return
|
|
36559
|
+
return snapshot;
|
|
35974
36560
|
};
|
|
35975
36561
|
const markLegionManagedSession = (targetSessionID) => {
|
|
35976
36562
|
legionRoleClaimBridge().managedSessions.add(targetSessionID);
|
|
@@ -35979,7 +36565,12 @@ function envoyExtension(pi) {
|
|
|
35979
36565
|
legionManagedTranscript = true;
|
|
35980
36566
|
pi.appendEntry(LEGION_MANAGED_ENTRY, { session_id: targetSessionID });
|
|
35981
36567
|
};
|
|
36568
|
+
const markPicturesShown = (shown, addresses) => {
|
|
36569
|
+
for (const address of addresses)
|
|
36570
|
+
shown.add(address);
|
|
36571
|
+
};
|
|
35982
36572
|
const restoreLocalSessionState = (context) => {
|
|
36573
|
+
const previousSessionID = sessionID;
|
|
35983
36574
|
sessionDirectory = context.cwd;
|
|
35984
36575
|
sessionID = context.sessionManager.getSessionId();
|
|
35985
36576
|
recordedSessionKey = recordEnvoySession({
|
|
@@ -36002,6 +36593,9 @@ function envoyExtension(pi) {
|
|
|
36002
36593
|
}
|
|
36003
36594
|
legionManagedTranscript = branch.some(isLegionManagedEntry);
|
|
36004
36595
|
handledDispatchAttempts = handledAttempts(context.sessionManager.getEntries());
|
|
36596
|
+
markPicturesShown(shownPictures(sessionID), transcriptPictures(branch));
|
|
36597
|
+
if (previousSessionID !== sessionID)
|
|
36598
|
+
forgetShownPictures(previousSessionID);
|
|
36005
36599
|
};
|
|
36006
36600
|
pi.on("resources_discover", async () => ({ skillPaths: [SKILLS_DIRECTORY] }));
|
|
36007
36601
|
registerEnvoyMessageRenderer(pi);
|
|
@@ -36033,10 +36627,10 @@ function envoyExtension(pi) {
|
|
|
36033
36627
|
handledDispatchAttempts.add(key);
|
|
36034
36628
|
pi.appendEntry(HANDLED_ATTEMPT_ENTRY, { attempt: delivery.attempt, message_id: delivery.id });
|
|
36035
36629
|
try {
|
|
36036
|
-
const
|
|
36037
|
-
if (
|
|
36630
|
+
const dispatch = timedDispatchClient(USER_TURN_ACCEPT_TIMEOUT_MS);
|
|
36631
|
+
if (dispatch === undefined)
|
|
36038
36632
|
return;
|
|
36039
|
-
const accepted = await
|
|
36633
|
+
const accepted = await dispatch.acceptMessageDelivery(delivery.id, delivery.attempt, {
|
|
36040
36634
|
actor: { id: sessionID, kind: "session" }
|
|
36041
36635
|
});
|
|
36042
36636
|
const turn = turnFromAccept(accepted);
|
|
@@ -36106,14 +36700,19 @@ function envoyExtension(pi) {
|
|
|
36106
36700
|
}
|
|
36107
36701
|
} else {
|
|
36108
36702
|
const turn = await acceptedUserTurn(rendered);
|
|
36703
|
+
const shown = shownPictures(sessionID);
|
|
36109
36704
|
if (turn === undefined) {
|
|
36110
|
-
|
|
36705
|
+
const card = await withDeliveredPictures(rendered.content, rendered.pictures ?? [], () => timedDispatchClient(DELIVERY_PICTURES_TIMEOUT_MS), true, shown);
|
|
36706
|
+
pi.sendMessage({ customType: "envoy-message", content: card.content, display: true }, {
|
|
36111
36707
|
deliverAs: rendered.delivery?.mode === "aside" ? "aside" : "steer",
|
|
36112
36708
|
triggerTurn: true
|
|
36113
36709
|
});
|
|
36710
|
+
markPicturesShown(shown, card.shown);
|
|
36114
36711
|
} else {
|
|
36712
|
+
const delivery = await withDeliveredPictures(turn.body, pictureAddresses(turn.body), () => timedDispatchClient(DELIVERY_PICTURES_TIMEOUT_MS), false, shown);
|
|
36115
36713
|
noteInjectedUserTurn(sessionID, turn.body, turn.messageId);
|
|
36116
|
-
pi.sendUserMessage(
|
|
36714
|
+
pi.sendUserMessage(delivery.content, turn.mode === "aside" ? { deliverAs: "aside" } : undefined);
|
|
36715
|
+
markPicturesShown(shown, delivery.shown);
|
|
36117
36716
|
}
|
|
36118
36717
|
}
|
|
36119
36718
|
} catch (error48) {
|
|
@@ -36603,7 +37202,7 @@ function envoyExtension(pi) {
|
|
|
36603
37202
|
pi.on("session_switch", (event, context) => rebind(event.reason, context));
|
|
36604
37203
|
pi.on("session_branch", (_event, context) => rebind(undefined, context));
|
|
36605
37204
|
pi.on("session_tree", (_event, context) => rebind(undefined, context));
|
|
36606
|
-
pi.on("session_shutdown", async () => {
|
|
37205
|
+
pi.on("session_shutdown", async (_event, context) => {
|
|
36607
37206
|
shuttingDown = true;
|
|
36608
37207
|
const bound = bridge.instances.indexOf(claimInstance);
|
|
36609
37208
|
if (bound !== -1)
|
|
@@ -36637,6 +37236,10 @@ function envoyExtension(pi) {
|
|
|
36637
37236
|
awaitingRetry.clear();
|
|
36638
37237
|
agentStreamControl = undefined;
|
|
36639
37238
|
agentStreamControlSession = "";
|
|
37239
|
+
const liveSessionID = context.sessionManager.getSessionId();
|
|
37240
|
+
forgetShownPictures(sessionID);
|
|
37241
|
+
if (liveSessionID !== sessionID)
|
|
37242
|
+
forgetShownPictures(liveSessionID);
|
|
36640
37243
|
}
|
|
36641
37244
|
});
|
|
36642
37245
|
for (const spec of envoyToolSpecs) {
|
|
@@ -36670,7 +37273,7 @@ function envoyExtension(pi) {
|
|
|
36670
37273
|
signal,
|
|
36671
37274
|
env: process.env
|
|
36672
37275
|
});
|
|
36673
|
-
return toolSuccess(result.text, result.details);
|
|
37276
|
+
return toolSuccess(result.text, result.details, result.images);
|
|
36674
37277
|
} catch (error48) {
|
|
36675
37278
|
return toolFailure(error48);
|
|
36676
37279
|
}
|
|
@@ -36695,7 +37298,7 @@ function envoyExtension(pi) {
|
|
|
36695
37298
|
try {
|
|
36696
37299
|
const open = await queryOpenAsks(id);
|
|
36697
37300
|
if (open !== null)
|
|
36698
|
-
armAskAwareness(id, open.
|
|
37301
|
+
armAskAwareness(id, open.as_of);
|
|
36699
37302
|
} catch (error48) {
|
|
36700
37303
|
warnAskAvailability(context, error48);
|
|
36701
37304
|
}
|
|
@@ -36766,7 +37369,7 @@ function envoyExtension(pi) {
|
|
|
36766
37369
|
const abort = new AbortController;
|
|
36767
37370
|
askCheckAbort = abort;
|
|
36768
37371
|
const answered = ask({
|
|
36769
|
-
prompt: ASK_SELF_CHECK_PROMPT(open.
|
|
37372
|
+
prompt: ASK_SELF_CHECK_PROMPT(open.asks),
|
|
36770
37373
|
signal: abort.signal
|
|
36771
37374
|
}).then((reply) => reply.replyText, (error48) => {
|
|
36772
37375
|
if (!abort.signal.aborted)
|
|
@@ -36797,7 +37400,7 @@ function envoyExtension(pi) {
|
|
|
36797
37400
|
...askAwareness,
|
|
36798
37401
|
check_due: superseded,
|
|
36799
37402
|
checks: askAwareness.checks + 1,
|
|
36800
|
-
baseline_as_of: open.
|
|
37403
|
+
baseline_as_of: open.as_of
|
|
36801
37404
|
};
|
|
36802
37405
|
if (superseded)
|
|
36803
37406
|
return;
|