@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 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 document reference.").optional(),
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: stringArg(args, "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 document reference.").optional(),
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: stringArg(args, "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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "1.26.0",
3
+ "version": "1.27.1",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [