@sjawhar/pi-legion-envoy 1.26.0 → 1.27.1
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 +18 -2
- package/dist/legion.js +18 -2
- package/dist/skills/dispatch/SKILL.md +21 -0
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29883,7 +29883,7 @@ var dispatchToolSpecs = [
|
|
|
29883
29883
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29884
29884
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29885
29885
|
artifact: z.string().describe("Project document artifact id, slug, or filename.").optional(),
|
|
29886
|
-
ref: z.string().describe("Optional dispatch:// issue or
|
|
29886
|
+
ref: z.string().describe("Optional dispatch:// reference (issue, document, message, or ask); appended to the question and rendered as a link.").optional(),
|
|
29887
29887
|
question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
|
|
29888
29888
|
kind: z.enum(["action"]).describe("Optional human to-do ask kind.").optional(),
|
|
29889
29889
|
options: z.array(z.object({
|
|
@@ -32015,6 +32015,19 @@ function askKind(args) {
|
|
|
32015
32015
|
return value;
|
|
32016
32016
|
throw new Error("kind must be action");
|
|
32017
32017
|
}
|
|
32018
|
+
function askQuestionWithRef(args) {
|
|
32019
|
+
const question = stringArg(args, "question");
|
|
32020
|
+
const ref = optionalString(args, "ref");
|
|
32021
|
+
if (ref === undefined || question.includes(ref))
|
|
32022
|
+
return question;
|
|
32023
|
+
const withRef = `${question}
|
|
32024
|
+
|
|
32025
|
+
Ref: ${ref}`;
|
|
32026
|
+
if (withRef.length > 800) {
|
|
32027
|
+
throw new Error("question plus ref must be at most 800 characters");
|
|
32028
|
+
}
|
|
32029
|
+
return withRef;
|
|
32030
|
+
}
|
|
32018
32031
|
function parseDispatchRef(ref) {
|
|
32019
32032
|
const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
|
|
32020
32033
|
if (projectDocument) {
|
|
@@ -32097,6 +32110,9 @@ async function resolveOwnerArguments(tool, args, cwd, env, exec) {
|
|
|
32097
32110
|
if (issueFreeTools[tool] === true)
|
|
32098
32111
|
return { args, ref: null, owner: null };
|
|
32099
32112
|
const refArgument = args.ref;
|
|
32113
|
+
if (tool === "dispatch_ask" && typeof refArgument === "string" && !refArgument.startsWith("dispatch://")) {
|
|
32114
|
+
throw new Error("ref must be a dispatch:// reference");
|
|
32115
|
+
}
|
|
32100
32116
|
const ref = typeof refArgument === "string" ? parseDispatchRef(refArgument) ?? (() => {
|
|
32101
32117
|
throw new Error("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>, or " + "dispatch://PROJECT/artifact/<document-ref> (an artifact id, slug, or filename)");
|
|
32102
32118
|
})() : null;
|
|
@@ -32506,7 +32522,7 @@ async function executeDispatchTool(input) {
|
|
|
32506
32522
|
const kind = askKind(args);
|
|
32507
32523
|
const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
|
|
32508
32524
|
const askInput = {
|
|
32509
|
-
question:
|
|
32525
|
+
question: askQuestionWithRef(args),
|
|
32510
32526
|
...kind === undefined ? {} : { kind },
|
|
32511
32527
|
...Array.isArray(options) ? { options } : {},
|
|
32512
32528
|
...multiple === undefined ? {} : { multiple },
|
package/dist/legion.js
CHANGED
|
@@ -29883,7 +29883,7 @@ var dispatchToolSpecs = [
|
|
|
29883
29883
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29884
29884
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29885
29885
|
artifact: z.string().describe("Project document artifact id, slug, or filename.").optional(),
|
|
29886
|
-
ref: z.string().describe("Optional dispatch:// issue or
|
|
29886
|
+
ref: z.string().describe("Optional dispatch:// reference (issue, document, message, or ask); appended to the question and rendered as a link.").optional(),
|
|
29887
29887
|
question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
|
|
29888
29888
|
kind: z.enum(["action"]).describe("Optional human to-do ask kind.").optional(),
|
|
29889
29889
|
options: z.array(z.object({
|
|
@@ -32446,6 +32446,19 @@ function askKind(args) {
|
|
|
32446
32446
|
return value;
|
|
32447
32447
|
throw new Error("kind must be action");
|
|
32448
32448
|
}
|
|
32449
|
+
function askQuestionWithRef(args) {
|
|
32450
|
+
const question = stringArg(args, "question");
|
|
32451
|
+
const ref = optionalString(args, "ref");
|
|
32452
|
+
if (ref === undefined || question.includes(ref))
|
|
32453
|
+
return question;
|
|
32454
|
+
const withRef = `${question}
|
|
32455
|
+
|
|
32456
|
+
Ref: ${ref}`;
|
|
32457
|
+
if (withRef.length > 800) {
|
|
32458
|
+
throw new Error("question plus ref must be at most 800 characters");
|
|
32459
|
+
}
|
|
32460
|
+
return withRef;
|
|
32461
|
+
}
|
|
32449
32462
|
function parseDispatchRef(ref) {
|
|
32450
32463
|
const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
|
|
32451
32464
|
if (projectDocument) {
|
|
@@ -32528,6 +32541,9 @@ async function resolveOwnerArguments(tool, args, cwd, env, exec) {
|
|
|
32528
32541
|
if (issueFreeTools[tool] === true)
|
|
32529
32542
|
return { args, ref: null, owner: null };
|
|
32530
32543
|
const refArgument = args.ref;
|
|
32544
|
+
if (tool === "dispatch_ask" && typeof refArgument === "string" && !refArgument.startsWith("dispatch://")) {
|
|
32545
|
+
throw new Error("ref must be a dispatch:// reference");
|
|
32546
|
+
}
|
|
32531
32547
|
const ref = typeof refArgument === "string" ? parseDispatchRef(refArgument) ?? (() => {
|
|
32532
32548
|
throw new Error("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>, or " + "dispatch://PROJECT/artifact/<document-ref> (an artifact id, slug, or filename)");
|
|
32533
32549
|
})() : null;
|
|
@@ -32937,7 +32953,7 @@ async function executeDispatchTool(input) {
|
|
|
32937
32953
|
const kind = askKind(args);
|
|
32938
32954
|
const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
|
|
32939
32955
|
const askInput = {
|
|
32940
|
-
question:
|
|
32956
|
+
question: askQuestionWithRef(args),
|
|
32941
32957
|
...kind === undefined ? {} : { kind },
|
|
32942
32958
|
...Array.isArray(options) ? { options } : {},
|
|
32943
32959
|
...multiple === undefined ? {} : { multiple },
|
|
@@ -111,6 +111,7 @@ dispatch_ask({
|
|
|
111
111
|
issue?,
|
|
112
112
|
project?,
|
|
113
113
|
artifact?,
|
|
114
|
+
ref?,
|
|
114
115
|
question,
|
|
115
116
|
options?: { label, description? }[],
|
|
116
117
|
multiple?,
|
|
@@ -120,6 +121,8 @@ dispatch_ask({
|
|
|
120
121
|
```
|
|
121
122
|
It returns `details` `{ issue, topic, ask }` for an issue or `{ project, artifact, document, topic, ask }` for a project document.
|
|
122
123
|
|
|
124
|
+
References belong in the question text; `ref` is sugar that appends its `dispatch://` value to the question as a rendered link.
|
|
125
|
+
|
|
123
126
|
An ask is read on a phone by someone who has not read the code. Open with one or two plain
|
|
124
127
|
sentences: what needs deciding and why it matters now. Each option is a button with a label and
|
|
125
128
|
one sentence saying what happens if it is chosen; never enumerate choices in prose. Put the
|
|
@@ -341,6 +344,24 @@ with your text. Never do that: the spec is edited in place with `dispatch_doc_ed
|
|
|
341
344
|
artifact by the slug shown in the upload result or by its filename, and a project document by its artifact id, slug, or filename; the
|
|
342
345
|
slug also arrives on `artifact.created` events.
|
|
343
346
|
|
|
347
|
+
## Structure over stream
|
|
348
|
+
|
|
349
|
+
Dispatch is a structured workspace, never a message stream (Sami, 2026-09-13, verbatim: "strange
|
|
350
|
+
to me that agents keep trying to use dispatch as a giant stream of messages instead of
|
|
351
|
+
high-signal, structured conversation"). The structure IS the product:
|
|
352
|
+
|
|
353
|
+
- **One issue per piece of work.** A new deliverable — an email to send, a document to review, a
|
|
354
|
+
decision with its own lifecycle — gets its own issue with the content as the issue's document
|
|
355
|
+
(spec artifact), not a message pile on an existing issue. If you are about to post a message
|
|
356
|
+
carrying a draft, a spec, or anything over a couple of paragraphs, stop: that is an issue with a
|
|
357
|
+
document, or an artifact on the issue it belongs to.
|
|
358
|
+
- **Content lives in documents; decisions live in asks; messages only announce.** A draft the
|
|
359
|
+
human must read goes in a document artifact the dashboard renders with versions and margins; the
|
|
360
|
+
ask that needs their word references it (`ref`, or the `dispatch://` link inline) instead of
|
|
361
|
+
restating it. A message never carries a body a human has to scroll.
|
|
362
|
+
- **Never split one deliverable across a message + an ask that points at it.** Ask the question
|
|
363
|
+
with the document reference in the question text; the reader lands on the content in one click.
|
|
364
|
+
|
|
344
365
|
## Messages
|
|
345
366
|
|
|
346
367
|
Dispatch is a high-signal record for humans, not a log of what you are doing. A message is a reply to a human's message, or a
|