@sjawhar/opencode-legion-envoy 1.11.0 → 1.12.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.
@@ -13590,6 +13590,7 @@ var ArtifactReviewEventPayloadSchema = object({
13590
13590
  var askEventPayloadFields = {
13591
13591
  id: string2().optional(),
13592
13592
  opened_event_id: number2().int().positive(),
13593
+ kind: _enum2(["question", "approval", "action"]).optional(),
13593
13594
  question: string2().optional(),
13594
13595
  options: array(object({ label: string2().optional() })).nullish(),
13595
13596
  answer: object({ selected: array(string2()).nullish(), text: string2().nullish() }).nullish(),
@@ -13757,13 +13758,14 @@ var dispatchToolSpecs = [
13757
13758
  },
13758
13759
  {
13759
13760
  name: "dispatch_ask",
13760
- description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Anchor a document question, 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". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
13761
+ description: "Open a durable, answerable decision or human to-do on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Use kind: action for a to-do a human must complete; it has fixed Done / Can't answers. " + "Anchor a document question, 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". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
13761
13762
  arguments: (z) => ({
13762
13763
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
13763
13764
  project: z.string().describe("Project key owning the document.").optional(),
13764
13765
  artifact: z.string().describe("Project document artifact id, slug, or filename.").optional(),
13765
13766
  ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
13766
13767
  question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
13768
+ kind: z.enum(["action"]).describe("Optional human to-do ask kind.").optional(),
13767
13769
  options: z.array(z.object({
13768
13770
  label: z.string().describe("Selectable option label."),
13769
13771
  description: z.string().describe("Optional option context.").optional()
@@ -14950,6 +14952,12 @@ function askUrgency(args) {
14950
14952
  const value = args.urgency;
14951
14953
  return ASK_URGENCIES.find((urgency) => urgency === value);
14952
14954
  }
14955
+ function askKind(args) {
14956
+ const value = optionalString(args, "kind");
14957
+ if (value === undefined || value === "action")
14958
+ return value;
14959
+ throw new Error("kind must be action");
14960
+ }
14953
14961
  function parseDispatchRef(ref) {
14954
14962
  const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
14955
14963
  if (projectDocument) {
@@ -15385,9 +15393,11 @@ async function executeDispatchTool(input) {
15385
15393
  const options = args.options;
15386
15394
  const multiple = optionalBoolean(args, "multiple");
15387
15395
  const urgency = askUrgency(args);
15396
+ const kind = askKind(args);
15388
15397
  const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
15389
15398
  const askInput = {
15390
15399
  question: stringArg(args, "question"),
15400
+ ...kind === undefined ? {} : { kind },
15391
15401
  ...Array.isArray(options) ? { options } : {},
15392
15402
  ...multiple === undefined ? {} : { multiple },
15393
15403
  ...urgency === undefined ? {} : { urgency },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -128,6 +128,24 @@ An ask must be answerable from its own text and its anchor alone. Anchor a quest
128
128
  about a comment with `reply_to`; thread a follow-up on your own ask with `reply_to_ask`; cite anything else with a `dispatch://`
129
129
  reference (see [References](#references)). Never write "see above", "the message above", or "as attached".
130
130
 
131
+ **Anything that needs the human is an ask, or it does not exist.** An approval, a credential,
132
+ a setting only they can change, a review click, a conflict between two of their own rules - if
133
+ your work waits on it, open a `dispatch_ask` with `kind: "action"` the moment you know, the
134
+ action as the question (the server supplies the fixed `Done` / `Can't` options; `Can't` requires
135
+ an explanation). Never write it into a spec, a comment reply, a message, or a
136
+ pull-request body: nothing in those paths reaches the human's Inbox, and a human who is not
137
+ reading your document does not know they are the blocker. Before asking, try to remove the
138
+ step: a value already on the machine, a permission you already hold, an API that replaces the
139
+ click. One ask per item, `urgency: "high"` when work is stopped on it; while it is open, keep
140
+ working on everything that is not.
141
+
142
+ Use `kind: "action"` for a to-do handed to a human. It has fixed `Done` / `Can't` options;
143
+ `Can't` requires an explanation, while the asker can still correct the action's wording:
144
+ ```ts
145
+ dispatch_ask({ issue: "DSP-42", kind: "action",
146
+ question: "Confirm the deployment is complete." })
147
+ ```
148
+
131
149
  Correct or refine an open ask in place instead of opening a second question:
132
150
  ```ts
133
151
  dispatch_edit_ask({
@@ -25,7 +25,10 @@ environment, then run:
25
25
  The command resolves the project from daemon state, claims the Envoy role for the current
26
26
  session, and posts readiness before controller commands can act. It retains the environment
27
27
  capability for `legion({ op: "set_status", issue, status })`. Never pass a secret as a command argument
28
- or copy it into a transcript.
28
+ or copy it into a transcript. The claim is kept alive automatically afterwards: the Envoy
29
+ registration heartbeat re-asserts it and re-posts readiness whenever the listener loses sight of
30
+ this session, so `/legion-claim-controller` is the manual override, not a routine step after a
31
+ listener restart.
29
32
 
30
33
  This handshake lets the daemon redeliver held controller work. It does not turn the controller
31
34
  into a state holder: daemon state and the Dispatch project remain authoritative.