@sjawhar/opencode-legion-envoy 0.27.0 → 0.29.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_resolve_ask`
17
18
  - `dispatch_comment`
18
19
  - `dispatch_suggest`
19
20
  - `dispatch_message`
@@ -13574,7 +13574,13 @@ var ArtifactVersionEventPayloadSchema = object({
13574
13574
  var AskEventPayloadSchema = object({
13575
13575
  question: string2().optional(),
13576
13576
  options: array(object({ label: string2().optional() })).nullish(),
13577
- answer: object({ selected: array(string2()).nullish(), text: string2().nullish() }).nullish()
13577
+ answer: object({ selected: array(string2()).nullish(), text: string2().nullish() }).nullish(),
13578
+ resolution: object({
13579
+ kind: _enum2(["retracted", "resolved"]).optional(),
13580
+ reason: string2().optional(),
13581
+ actor: object({ kind: string2().optional(), id: string2().optional() }).passthrough().optional(),
13582
+ at: string2().optional()
13583
+ }).nullish()
13578
13584
  });
13579
13585
  var CommentEventPayloadSchema = object({
13580
13586
  artifact_name: string2().optional(),
@@ -13630,6 +13636,15 @@ var dispatchToolSpecs = [
13630
13636
  }).describe("Optional document location for the question.").optional()
13631
13637
  })
13632
13638
  },
13639
+ {
13640
+ name: "dispatch_resolve_ask",
13641
+ description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
13642
+ arguments: (z) => ({
13643
+ ask: z.string().describe("Ask id to close."),
13644
+ kind: z.enum(["retracted", "resolved"]).describe("Whether the ask is retracted or self-resolved."),
13645
+ reason: z.string({ min: 1 }).describe("Why the open ask no longer needs a human answer.")
13646
+ })
13647
+ },
13633
13648
  {
13634
13649
  name: "dispatch_comment",
13635
13650
  description: "Add review feedback to an issue or document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
@@ -14340,6 +14355,9 @@ class DispatchClient {
14340
14355
  async ask(issue, input) {
14341
14356
  return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "asks"], input);
14342
14357
  }
14358
+ async resolveAsk(id, input) {
14359
+ return this.#json("POST", ["api", "v1", "asks", id, "resolve"], input);
14360
+ }
14343
14361
  async comment(issue, input) {
14344
14362
  return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "comments"], input);
14345
14363
  }
@@ -14558,7 +14576,7 @@ function toolSchema(tool) {
14558
14576
  return dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
14559
14577
  }
14560
14578
  async function resolveIssueArguments(tool, args, cwd, env, exec) {
14561
- if (tool === "dispatch_issue")
14579
+ if (tool === "dispatch_issue" || tool === "dispatch_resolve_ask")
14562
14580
  return { args, ref: null };
14563
14581
  const refArgument = args.ref;
14564
14582
  const ref = typeof refArgument === "string" ? parseDispatchRef(refArgument) ?? (() => {
@@ -14665,6 +14683,12 @@ function askSummary({ ask, replies }) {
14665
14683
  `- Selected: ${answer.selected.length === 0 ? "none" : answer.selected.join(", ")}`,
14666
14684
  ...answer.text === null ? [] : [`- Text: ${answer.text}`]
14667
14685
  ],
14686
+ ...ask.resolution === undefined ? [] : [
14687
+ "Resolution:",
14688
+ `- By: ${ask.resolution.actor.id}`,
14689
+ `- Kind: ${ask.resolution.kind}`,
14690
+ `- Reason: ${ask.resolution.reason}`
14691
+ ],
14668
14692
  "Replies:",
14669
14693
  ...chain.length === 0 ? ["- none"] : chain
14670
14694
  ].join(`
@@ -14709,7 +14733,7 @@ async function executeDispatchTool(input) {
14709
14733
  const args = toolSchema(input.tool).parse(issueArguments.args);
14710
14734
  const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
14711
14735
  const client = new DispatchClient(input.config.url, input.config.token, input.fetchImpl);
14712
- const issueKey = input.tool === "dispatch_issue" ? null : await ensureIssue(client, stringArg(args, "issue"), actor);
14736
+ const issueKey = input.tool === "dispatch_issue" || input.tool === "dispatch_resolve_ask" ? null : await ensureIssue(client, stringArg(args, "issue"), actor);
14713
14737
  const issue = () => {
14714
14738
  if (issueKey === null)
14715
14739
  throw new Error("issue is required");
@@ -14733,6 +14757,26 @@ async function executeDispatchTool(input) {
14733
14757
  details: { issue: created.key, topic: dispatchIssueSubject(created.key, ">") }
14734
14758
  };
14735
14759
  }
14760
+ case "dispatch_resolve_ask": {
14761
+ const kind = stringArg(args, "kind");
14762
+ if (kind !== "retracted" && kind !== "resolved")
14763
+ throw new Error("kind must be retracted or resolved");
14764
+ const ask = await client.resolveAsk(stringArg(args, "ask"), {
14765
+ kind,
14766
+ reason: stringArg(args, "reason"),
14767
+ actor
14768
+ });
14769
+ if (ask.resolution === undefined)
14770
+ throw new Error("resolved ask is missing its resolution");
14771
+ return {
14772
+ text: `${kind === "retracted" ? "Retracted" : "Resolved"} ask ${ask.id}: ${ask.resolution.reason}`,
14773
+ details: {
14774
+ issue: ask.issue_key,
14775
+ topic: dispatchIssueSubject(ask.issue_key, ">"),
14776
+ ask: ask.id
14777
+ }
14778
+ };
14779
+ }
14736
14780
  case "dispatch_ask": {
14737
14781
  const anchorArgs = asObject(args.anchor);
14738
14782
  const resolved = anchorArgs ? await resolveArtifact(client, issue(), stringArg(anchorArgs, "artifact")) : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -44,8 +44,20 @@ It returns `details` `{ issue, topic, ask }`. Options are buttons: never enumera
44
44
  prose. Put the recommendation in `question`, and put each selectable choice in `options`.
45
45
  Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occurrence` is
46
46
  zero-based and selects a repeated quote. An anchor whose quote disappears becomes orphaned but
47
- remains readable against its original document version. An ask stays open until a human answers;
48
- you cannot withdraw it. Ask once and well. The host steers you when a human answers.
47
+ remains readable against its original document version.
48
+
49
+ An ask stays open until a human answers, unless its question no longer needs that answer. Retract a
50
+ moot or superseded question, or self-resolve one after finding the answer:
51
+ ```ts
52
+ dispatch_resolve_ask({
53
+ ask,
54
+ kind: "retracted",
55
+ reason: "A newer ask supersedes this question.",
56
+ })
57
+ ```
58
+ Use `retracted` when the question is obsolete and `resolved` when you found the answer. Include the
59
+ reason because the question remains in its issue log and reply thread. Resolution is not an answer:
60
+ it never records a human decision, and an answered ask cannot be resolved.
49
61
 
50
62
  An ask is a thread, not a dead end: a human can reply to it before or after answering, and you
51
63
  (the asker) can reply too — e.g. acknowledging a clarifying question, or following up after the