@sjawhar/pi-envoy 10.18.0 → 10.20.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/dispatch.js CHANGED
@@ -13948,9 +13948,10 @@ function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false)
13948
13948
  message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
13949
13949
  };
13950
13950
  }
13951
+ var documentEditOwner = documentOwnerValidation(true);
13951
13952
  var documentEditValidation = {
13952
13953
  check: (value) => {
13953
- if (!documentOwnerValidation(true, true).check(value))
13954
+ if (!documentEditOwner.check(value))
13954
13955
  return false;
13955
13956
  const input = value;
13956
13957
  if (input.precondition === undefined)
@@ -13960,7 +13961,7 @@ var documentEditValidation = {
13960
13961
  const precondition = input.precondition;
13961
13962
  return typeof precondition.document === "string" !== Array.isArray(precondition.blocks);
13962
13963
  },
13963
- message: "Exactly one of issue and project is required; artifact or ref must name the document. A precondition selects exactly one of document or blocks."
13964
+ message: `${documentEditOwner.message} A precondition selects exactly one of document or blocks.`
13964
13965
  };
13965
13966
  var commentOwner = documentOwnerValidation(true);
13966
13967
  var commentValidation = {
@@ -14060,7 +14061,7 @@ var dispatchToolSpecs = [
14060
14061
  spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
14061
14062
  labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
14062
14063
  priority: z2.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority, a number from 0 to 3: 0 is highest and 3 is lowest.").optional(),
14063
- assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional(),
14064
+ assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned.").optional(),
14064
14065
  components: componentsArgument(z2).optional(),
14065
14066
  repo: z2.string().describe("The repository the issue targets, owner/name, mapped to the issue's project in Settings; a child inherits its parent's when unset, and an issue with none on its parent chain takes the project's default.").optional()
14066
14067
  })
@@ -14099,7 +14100,7 @@ var dispatchToolSpecs = [
14099
14100
  {
14100
14101
  name: "dispatch_claim",
14101
14102
  example: { issue: "DSP-1" },
14102
- description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, so also move the issue to in_progress with " + "dispatch issue-update when you start. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
14103
+ description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, and the result names who moves the status: you, with " + "dispatch issue-update, or Legion's daemon on an issue it runs, where a status written by anyone " + "else is undone. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
14103
14104
  arguments: (z2) => ({
14104
14105
  issue: z2.string().describe(ISSUE_REFERENCE),
14105
14106
  release: z2.boolean().describe("Give up your claim instead of taking it; the issue's status does not change.").optional()
@@ -14276,7 +14277,7 @@ var dispatchToolSpecs = [
14276
14277
  arguments: (z2) => ({
14277
14278
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14278
14279
  project: z2.string().describe("Project key owning the document.").optional(),
14279
- artifact: z2.string().describe("Project document artifact id, slug, or filename; optional when ref names it.").optional(),
14280
+ artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
14280
14281
  ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
14281
14282
  ops: z2.array(z2.object({
14282
14283
  op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
@@ -14438,7 +14439,7 @@ var dispatchToolSpecs = [
14438
14439
  {
14439
14440
  name: "dispatch_whoami",
14440
14441
  example: {},
14441
- description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
14442
+ description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the first choice of assignee for issues you create), or null under the shared token, in which case a create with no assignee falls back to the parent's assignee and then to the project's default assignee.",
14442
14443
  arguments: () => ({}),
14443
14444
  strict: true
14444
14445
  }
@@ -16263,7 +16264,7 @@ function renderAdvice(tool, key, advice, opts) {
16263
16264
  if (advice.decision_blocks === 0 && opts.isPrimarySpec === true && (tool === "dispatch_issue" || tool === "dispatch_artifact")) {
16264
16265
  lines.push('This spec holds no ask blocks, so nothing here reaches a human\'s inbox. Want human feedback? See the `dispatch` skill, "Decision blocks".');
16265
16266
  }
16266
- if (advice.issue_status === "triage" && tool !== "dispatch_issue" && !(tool === "dispatch_issue_update" && opts.setsStatus === true) && !triageAdviceShown.has(key)) {
16267
+ if (advice.issue_status === "triage" && advice.legion_managed_status !== true && tool !== "dispatch_issue" && !(tool === "dispatch_issue_update" && opts.setsStatus === true) && !triageAdviceShown.has(key)) {
16267
16268
  triageAdviceShown.add(key);
16268
16269
  lines.push(`${key} is still in triage \u2014 nobody can see its development status. See the \`dispatch\` skill, "Issue status is yours to move".`);
16269
16270
  }
@@ -16420,6 +16421,7 @@ function componentsChange(input, after) {
16420
16421
  }
16421
16422
  }
16422
16423
  var architectureComponentsAction = 'Review the `dispatch` skill, "Architecture components", to attach it to the parts it changes or mark it as non-architectural with a reason.';
16424
+ var LEGION_OWNS_STATUS = "Legion's daemon moves this issue's status on an issue it runs, so leave it alone \u2014 a status written here is undone.";
16423
16425
  async function architectureGuidance(client, project, components) {
16424
16426
  if (components.mode === "none" || components.ids.length > 0)
16425
16427
  return;
@@ -16737,8 +16739,8 @@ function innerRefProblem(tool, ref, refText) {
16737
16739
  if (tool === "dispatch_read" || tool === "dispatch_ask")
16738
16740
  return;
16739
16741
  const owner = refOwnerName(ref);
16740
- const documentTool = tool === "dispatch_suggest" || tool === "dispatch_doc_edit";
16741
- const ownerArguments = ref.owner.kind === "issue" ? `issue ${ref.owner.issue}${documentTool ? " and artifact <the document's slug>" : ""}` : `project ${ref.owner.project} and artifact ${ref.artifact}`;
16742
+ const suggestTool = tool === "dispatch_suggest";
16743
+ const ownerArguments = ref.owner.kind === "issue" ? `issue ${ref.owner.issue}${suggestTool ? " and artifact <the document's slug>" : ""}` : `project ${ref.owner.project} and artifact ${ref.artifact}`;
16742
16744
  const fix = tool === "dispatch_comment" && ref.kind === "ask" ? `reply under it with reply_to_ask ${ref.id} and ${ownerArguments}` : tool === "dispatch_comment" && ref.kind === "comment" ? `reply in its thread with reply_to ${ref.id} and ${ownerArguments}` : `read it with dispatch read, or name ${owner} with ${ownerArguments}`;
16743
16745
  const item = ref.kind === "ask" ? "an ask" : ref.kind === "log" ? "the event log" : ref.kind === "children" ? "the children" : `a ${ref.kind}`;
16744
16746
  return `ref ${refText} names ${item} on ${owner}; dispatch ${commandName(tool)} would act on ${owner} and drop the ${ref.kind}: ${fix}`;
@@ -17683,7 +17685,7 @@ async function executeDispatchTool(input) {
17683
17685
  const identity = await client2.whoami();
17684
17686
  const owner2 = identity.kind === "agent" ? identity.owner : identity.login.toLowerCase();
17685
17687
  const service = identity.kind === "agent" ? identity.service ?? null : null;
17686
- const unowned = "no owner, so issues you create without an assignee are unassigned (or inherit their parent's).";
17688
+ const unowned = "no owner, so an issue you create without an assignee takes its parent's assignee, else the project's default assignee, else nobody.";
17687
17689
  return {
17688
17690
  text: owner2 !== null ? `Session ${sessionId} acts for ${owner2}: issues you create without an assignee are assigned to ${owner2}.` : service !== null ? `Session ${sessionId} runs as service ${serviceSubjectLabel(service)}: ${unowned}` : `Session ${sessionId} runs under the shared token: ${unowned}`,
17689
17691
  details: { session: sessionId, owner: owner2, service }
@@ -17924,14 +17926,17 @@ async function executeDispatchTool(input) {
17924
17926
  throw refusalWithCode(error48);
17925
17927
  }
17926
17928
  const held = after.claim;
17929
+ const legionManaged = after.legion_managed_status === true;
17927
17930
  let text;
17928
17931
  if (release) {
17929
- text = `${issueKey}: claim released; nobody is working it now. Its status is still ${after.status} \u2014 move it yourself if that is no longer where the work is.`;
17932
+ const released = `${issueKey}: claim released; nobody is working it now. Its status is still ${after.status}`;
17933
+ text = legionManaged ? `${released}; ${LEGION_OWNS_STATUS}` : `${released} \u2014 move it yourself if that is no longer where the work is.`;
17930
17934
  } else {
17931
17935
  if (held === null) {
17932
17936
  throw new Error(`Dispatch claimed ${issueKey} but answered with no claim`);
17933
17937
  }
17934
- text = `${issueKey}: claimed by you since ${held.at}. Its status is ${after.status}; a claim moves nothing, so move it to in_progress with dispatch issue-update when you start, and release the claim when you stop.`;
17938
+ const claimed = `${issueKey}: claimed by you since ${held.at}. Its status is ${after.status}`;
17939
+ text = legionManaged ? `${claimed}; ${LEGION_OWNS_STATUS} Release the claim when you stop.` : `${claimed}; a claim moves nothing, so move it to in_progress with dispatch issue-update when you start, and release the claim when you stop.`;
17935
17940
  }
17936
17941
  return {
17937
17942
  text: `${text}${await notSubscribed(issueTopic(issueKey), input.subscriptions)}`,
@@ -18289,7 +18294,7 @@ ${await followsAsk(askOwner, input.subscriptions)}`,
18289
18294
  };
18290
18295
  }
18291
18296
  case "dispatch_doc_edit": {
18292
- const resolved = await resolveDocument(documentOwner(), stringArg(args, "artifact"));
18297
+ const resolved = await resolveDocument(documentOwner(), optionalString(args, "artifact"));
18293
18298
  const ops = args.ops;
18294
18299
  const summary = optionalString(args, "summary");
18295
18300
  const { precondition: rawPrecondition } = args;
package/dist/envoy.js CHANGED
@@ -29935,9 +29935,10 @@ function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false)
29935
29935
  message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
29936
29936
  };
29937
29937
  }
29938
+ var documentEditOwner = documentOwnerValidation(true);
29938
29939
  var documentEditValidation = {
29939
29940
  check: (value) => {
29940
- if (!documentOwnerValidation(true, true).check(value))
29941
+ if (!documentEditOwner.check(value))
29941
29942
  return false;
29942
29943
  const input = value;
29943
29944
  if (input.precondition === undefined)
@@ -29947,7 +29948,7 @@ var documentEditValidation = {
29947
29948
  const precondition = input.precondition;
29948
29949
  return typeof precondition.document === "string" !== Array.isArray(precondition.blocks);
29949
29950
  },
29950
- message: "Exactly one of issue and project is required; artifact or ref must name the document. A precondition selects exactly one of document or blocks."
29951
+ message: `${documentEditOwner.message} A precondition selects exactly one of document or blocks.`
29951
29952
  };
29952
29953
  var commentOwner = documentOwnerValidation(true);
29953
29954
  var commentValidation = {
@@ -30047,7 +30048,7 @@ var dispatchToolSpecs = [
30047
30048
  spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
30048
30049
  labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
30049
30050
  priority: z2.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority, a number from 0 to 3: 0 is highest and 3 is lowest.").optional(),
30050
- assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional(),
30051
+ assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned.").optional(),
30051
30052
  components: componentsArgument(z2).optional(),
30052
30053
  repo: z2.string().describe("The repository the issue targets, owner/name, mapped to the issue's project in Settings; a child inherits its parent's when unset, and an issue with none on its parent chain takes the project's default.").optional()
30053
30054
  })
@@ -30086,7 +30087,7 @@ var dispatchToolSpecs = [
30086
30087
  {
30087
30088
  name: "dispatch_claim",
30088
30089
  example: { issue: "DSP-1" },
30089
- description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, so also move the issue to in_progress with " + "dispatch issue-update when you start. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
30090
+ description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, and the result names who moves the status: you, with " + "dispatch issue-update, or Legion's daemon on an issue it runs, where a status written by anyone " + "else is undone. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
30090
30091
  arguments: (z2) => ({
30091
30092
  issue: z2.string().describe(ISSUE_REFERENCE),
30092
30093
  release: z2.boolean().describe("Give up your claim instead of taking it; the issue's status does not change.").optional()
@@ -30263,7 +30264,7 @@ var dispatchToolSpecs = [
30263
30264
  arguments: (z2) => ({
30264
30265
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
30265
30266
  project: z2.string().describe("Project key owning the document.").optional(),
30266
- artifact: z2.string().describe("Project document artifact id, slug, or filename; optional when ref names it.").optional(),
30267
+ artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
30267
30268
  ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
30268
30269
  ops: z2.array(z2.object({
30269
30270
  op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
@@ -30425,7 +30426,7 @@ var dispatchToolSpecs = [
30425
30426
  {
30426
30427
  name: "dispatch_whoami",
30427
30428
  example: {},
30428
- description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
30429
+ description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the first choice of assignee for issues you create), or null under the shared token, in which case a create with no assignee falls back to the parent's assignee and then to the project's default assignee.",
30429
30430
  arguments: () => ({}),
30430
30431
  strict: true
30431
30432
  }
@@ -4,8 +4,8 @@ Read this once an ask is open. Whether to ask, and how to write it, is "Before y
4
4
 
5
5
  ## Who answers an ask
6
6
 
7
- - An ask goes to the issue's assignee's Inbox. On an unassigned issue it is in nobody's default Inbox or `Needs you` count.
8
- - `dispatch whoami` prints whom you act for: your token owner's email, or no owner (a service token shows as `<namespace>/<name>`, as your writes do). An issue you create goes to your owner, else inherits its parent's assignee, else stays unassigned.
7
+ - An ask goes to the issue's assignee's Inbox: the `Assignee:` that `dispatch read` prints decides whose, not the project's default. On an unassigned issue it is in nobody's default Inbox or `Needs you` count. A project's default assignee fills in only when an issue is created with nobody named and nothing earlier in the chain below names someone; an issue created with an explicit nobody, set to nobody later, or created before the default was set stays unassigned.
8
+ - `dispatch whoami` prints whom you act for: your token owner's email, or no owner (a service token shows as `<namespace>/<name>`, as your writes do). An issue you create goes to your owner, else inherits its parent's assignee, else goes to the project's default assignee when a person set one on the Settings page, else stays unassigned.
9
9
  - On an unassigned issue where the answer matters, assign it to your owner, or name in the question who should answer. Assign with `PATCH /api/v1/issues/{key}` and `{"assignee": "<email>"}`. An email nobody signed in with gets `ASSIGNEE_NOT_ALLOWED`.
10
10
  - Never reassign an issue a human holds to get a faster answer. That is the human's call.
11
11
 
@@ -27,12 +27,12 @@ dispatch claim --issue LEGION-234 --release # I have stopped; it is free
27
27
 
28
28
  ## Issue status is yours to move
29
29
 
30
- - Move it as a person moves a card, root and owned children: `in_progress`, `testing`, `needs_review`, then `done` once driven in production. A merge is not `done`. An issue left in `triage` while work runs is a defect. Waiting for the deploy lane is not a status and is never announced.
30
+ - Move it as a person moves a card, root and owned children: `in_progress`, `testing`, `needs_review`, then `done` once driven in production. A merge is not `done`. An issue left in `triage` while work runs is a defect. Waiting for the deploy lane is not a status and is never announced. Never the status of an issue with the `legion` label or under one: the daemon writes those, and a status you write there is undone.
31
31
  - `--status done` needs `--reason`. Dispatch posts it as a message, then closes. If the close fails after the post, read the status first. A retry points its reason at the posted message.
32
32
  - Clears differ: `--clear-priority`, `--parent ''` / `--route ''` / `--repo ''`, `--clear-external-links` / `--clear-blocked-by`. See `dispatch issue-update --help`.
33
33
  - Repository: own `--repo`, else nearest ancestor's, else the project default, else none. Unmapped is `REPO_NOT_MAPPED`. `dispatch read` shows `Repository:`. `Repository: unknown (…)` is an older server: a `--repo` write there fails.
34
34
  - Link the delivering PR with `--external-link` when you open it.
35
- - `… is still in triage …` on a write: move the status once work starts.
35
+ - `… is still in triage …` on a write: move the status once work starts. It never appears on an issue with the `legion` label or under one, since the daemon moves those out of triage itself.
36
36
 
37
37
  ## Priority is yours to set
38
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-envoy",
3
- "version": "10.18.0",
3
+ "version": "10.20.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "omp": {