@sjawhar/opencode-legion-envoy 0.38.0 → 0.39.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/README.md CHANGED
@@ -14,6 +14,7 @@ This package exposes:
14
14
  - `envoy_sessions`
15
15
  - `dispatch_issue`
16
16
  - `dispatch_ask`
17
+ - `dispatch_edit_ask`
17
18
  - `dispatch_resolve_ask`
18
19
  - `dispatch_comment`
19
20
  - `dispatch_suggest`
@@ -24,7 +25,7 @@ This package exposes:
24
25
  - `dispatch_read`
25
26
  - `dispatch_search`
26
27
 
27
- The eleven native `dispatch_*` tools create and read Dispatch issues, asks, comments,
28
+ The twelve native `dispatch_*` tools create and read Dispatch issues, asks, comments,
28
29
  documents, and artifacts, or search all of them. They are present when `dispatch.enabled`
29
30
  resolves a server URL and bearer token from envoy.json (`~/.config/opencode/envoy.json`, merged
30
31
  with `<repo>/.opencode/envoy.json`) or the `DISPATCH_URL` and `DISPATCH_TOKEN` environment
@@ -13717,6 +13717,27 @@ var dispatchToolSpecs = [
13717
13717
  }).describe("Optional document location for the question.").optional()
13718
13718
  })
13719
13719
  },
13720
+ {
13721
+ name: "dispatch_edit_ask",
13722
+ description: "Edit an open question in place. Use it to correct or refine the same decision; retract the " + "old ask and open a new one when the decision itself changes. Previous text remains in the " + "event log. Only the asking session can edit it; answered or resolved asks cannot be edited.",
13723
+ arguments: (z) => ({
13724
+ ask: z.string().describe("Ask id to edit."),
13725
+ question: z.string({ max: 800 }).describe("Replacement decision question, at most 800 characters.").optional(),
13726
+ options: z.array(z.object({
13727
+ label: z.string().describe("Selectable option label."),
13728
+ description: z.string().describe("Optional option context.").optional()
13729
+ }), { max: 8 }).describe("Replacement choices, at most 8.").optional(),
13730
+ multiple: z.boolean().describe("Whether multiple choices may be selected.").optional(),
13731
+ urgency: z.enum(ASK_URGENCIES).describe("Replacement decision urgency.").optional()
13732
+ }),
13733
+ validation: {
13734
+ check: (value) => {
13735
+ const input = value;
13736
+ return typeof input.question === "string" || Array.isArray(input.options) || typeof input.multiple === "boolean" || typeof input.urgency === "string";
13737
+ },
13738
+ message: "Ask edit requires at least one field besides ask."
13739
+ }
13740
+ },
13720
13741
  {
13721
13742
  name: "dispatch_resolve_ask",
13722
13743
  description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
@@ -14482,6 +14503,9 @@ class DispatchClient {
14482
14503
  async resolveAsk(id, input) {
14483
14504
  return this.#json("POST", ["api", "v1", "asks", id, "resolve"], input);
14484
14505
  }
14506
+ async editAsk(id, input) {
14507
+ return this.#json("PATCH", ["api", "v1", "asks", id], input);
14508
+ }
14485
14509
  async comment(issue, input) {
14486
14510
  return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "comments"], input);
14487
14511
  }
@@ -14638,7 +14662,12 @@ class DispatchClient {
14638
14662
  var nativeIssueKeyPattern = /^[A-Z][A-Z0-9]{1,9}-[0-9]+$/;
14639
14663
  var externalIssueRefPattern = /^([^/\s]+)\/([^/\s#]+)#([1-9][0-9]*)$/;
14640
14664
  var bareIssueNumberPattern = /^[1-9][0-9]*$/;
14641
- var issueFreeTools = new Set(["dispatch_issue", "dispatch_resolve_ask", "dispatch_search"]);
14665
+ var issueFreeTools = new Set([
14666
+ "dispatch_issue",
14667
+ "dispatch_edit_ask",
14668
+ "dispatch_resolve_ask",
14669
+ "dispatch_search"
14670
+ ]);
14642
14671
  async function askResultDetails(client, ask) {
14643
14672
  if (ask.issue_key !== null) {
14644
14673
  return {
@@ -14726,6 +14755,16 @@ function parseDispatchRef(ref) {
14726
14755
  return { issue, kind: "comment", id: comment };
14727
14756
  return { issue, kind: "issue", id: issue };
14728
14757
  }
14758
+ function askId(args) {
14759
+ const ask = stringArg(args, "ask");
14760
+ if (!ask.startsWith("dispatch://"))
14761
+ return ask;
14762
+ const reference = parseDispatchRef(ask);
14763
+ if (reference?.kind !== "ask") {
14764
+ throw new Error("ask must be a bare ask id or a dispatch://.../ask/<id> reference");
14765
+ }
14766
+ return reference.id;
14767
+ }
14729
14768
  function toolSchema(tool) {
14730
14769
  const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
14731
14770
  if (!spec)
@@ -14994,6 +15033,23 @@ async function executeDispatchTool(input) {
14994
15033
  details: await askResultDetails(client, ask)
14995
15034
  };
14996
15035
  }
15036
+ case "dispatch_edit_ask": {
15037
+ const question = optionalString(args, "question");
15038
+ const options = args.options;
15039
+ const multiple = optionalBoolean(args, "multiple");
15040
+ const urgency = askUrgency(args);
15041
+ const ask = await client.editAsk(askId(args), {
15042
+ ...question === undefined ? {} : { question },
15043
+ ...Array.isArray(options) ? { options } : {},
15044
+ ...multiple === undefined ? {} : { multiple },
15045
+ ...urgency === undefined ? {} : { urgency },
15046
+ actor
15047
+ });
15048
+ return {
15049
+ text: `Ask edited: ${ask.question}`,
15050
+ details: await askResultDetails(client, ask)
15051
+ };
15052
+ }
14997
15053
  case "dispatch_comment": {
14998
15054
  const artifactReference = optionalString(args, "artifact");
14999
15055
  if (optionalString(args, "quote") !== undefined && artifactReference === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -94,6 +94,20 @@ accepts `{ artifact, mark_id }` from a browser that has already written its mark
94
94
  use the quote form. An anchor whose quote disappears becomes orphaned but remains readable against
95
95
  its original document version.
96
96
 
97
+ Correct or refine an open ask in place instead of opening a second question:
98
+ ```ts
99
+ dispatch_edit_ask({
100
+ ask,
101
+ question?,
102
+ options?: { label, description? }[],
103
+ multiple?,
104
+ urgency?,
105
+ })
106
+ ```
107
+ At least one field besides `ask` is required. Use this only while the same decision remains
108
+ open: it keeps the prior text in the event log. An answered or resolved ask cannot be edited.
109
+ If the decision is moot or superseded, retract the old ask and open a new one.
110
+
97
111
  An ask stays open until a human answers, unless its question no longer needs that answer. Retract a
98
112
  moot or superseded question, or self-resolve one after finding the answer:
99
113
  ```ts