@sjawhar/opencode-legion-envoy 1.45.5 → 1.46.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.
@@ -13952,7 +13952,7 @@ var dispatchToolSpecs = [
13952
13952
  },
13953
13953
  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.",
13954
13954
  arguments: (z) => ({
13955
- ask: z.string().describe("Ask id to edit."),
13955
+ ask: z.string().describe("Ask id (uuid); an 8+ hex prefix unique among this session's own open asks works too."),
13956
13956
  question: z.string({ max: ASK_QUESTION_MAX }).describe(`Replacement decision question, at most ${ASK_QUESTION_MAX} characters.`).optional(),
13957
13957
  options: z.array(z.object({
13958
13958
  label: z.string().describe("Selectable option label."),
@@ -13978,7 +13978,7 @@ var dispatchToolSpecs = [
13978
13978
  },
13979
13979
  description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
13980
13980
  arguments: (z) => ({
13981
- ask: z.string().describe("Ask id to close."),
13981
+ ask: z.string().describe("Ask id (uuid) to close; an 8+ hex prefix unique among this session's own open asks " + "works too."),
13982
13982
  kind: z.enum(["retracted", "resolved"]).describe("Whether the ask is retracted or self-resolved."),
13983
13983
  reason: z.string({ min: 1 }).describe("Why the open ask no longer needs a human answer.")
13984
13984
  })
@@ -13997,7 +13997,7 @@ var dispatchToolSpecs = [
13997
13997
  example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
13998
13998
  description: "Follow or unfollow an ask. Every session that opens or replies to an ask follows it: its answer, " + "edits, resolution, and replies reach that session directly. Unfollow to stop; follow to rejoin or " + "to hear an ask you never wrote to. Whole-issue subscription is separate: envoy_subscribe " + "notifications.dispatch.issue.<KEY>.>",
13999
13999
  arguments: (z) => ({
14000
- ask: z.string().describe("Full ask id (uuid)."),
14000
+ ask: z.string().describe("Ask id (uuid); an 8+ hex prefix unique among this session's own open asks works too."),
14001
14001
  action: z.enum(["follow", "unfollow"]).describe("follow | unfollow")
14002
14002
  }),
14003
14003
  strict: true
@@ -15688,24 +15688,51 @@ function messageIdOf(value) {
15688
15688
  return id === undefined ? undefined : normalizeUUID(id);
15689
15689
  }
15690
15690
  var idPrefixPattern = /^[0-9a-f][0-9a-f-]{7,}$/i;
15691
- async function resolveIdPrefix(tool, kind, ref, list) {
15692
- const fullID = normalizeUUID(ref.id);
15691
+ function refOwnerName(ref) {
15692
+ return ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact}`;
15693
+ }
15694
+ async function resolveIdPrefix(tool, kind, id, ownerName, list) {
15695
+ const fullID = normalizeUUID(id);
15693
15696
  if (fullID !== undefined)
15694
15697
  return fullID;
15695
- const ownerName = ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact}`;
15696
- if (!idPrefixPattern.test(ref.id)) {
15698
+ if (!idPrefixPattern.test(id)) {
15697
15699
  throw new ToolInputError(tool, [
15698
- `${kind} id ${ref.id} must be a full uuid or a prefix of at least 8 hex characters`
15700
+ `${kind} id ${id} must be a full uuid or a prefix of at least 8 hex characters`
15699
15701
  ]);
15700
15702
  }
15701
- const prefix = ref.id.toLowerCase();
15703
+ const prefix = id.toLowerCase();
15702
15704
  const matches = (await list()).filter((item) => item.id.toLowerCase().startsWith(prefix));
15703
15705
  if (matches.length === 1 && matches[0] !== undefined)
15704
15706
  return matches[0].id;
15705
15707
  throw new ToolInputError(tool, [
15706
- matches.length === 0 ? `${kind} id ${ref.id} matches none of the ${kind}s on ${ownerName}; use the full id` : `${kind} id ${ref.id} matches ${matches.length} ${kind}s on ${ownerName}; use the full id`
15708
+ matches.length === 0 ? `${kind} id ${id} matches none of the ${kind}s on ${ownerName}; use the full id` : `${kind} id ${id} matches ${matches.length} ${kind}s on ${ownerName}; use the full id`
15707
15709
  ]);
15708
15710
  }
15711
+ var askIdShapeProblem = "ask ids are uuids (a prefix of at least 8 hex characters works)";
15712
+ async function sessionOpenAsks(client, sessionId) {
15713
+ const session = sessionId?.trim();
15714
+ if (!session)
15715
+ throw new Error("host session id is required to read this session's open asks");
15716
+ return (await client.openAsks(session)).asks;
15717
+ }
15718
+ function openAskHints(asks) {
15719
+ if (asks.length === 0)
15720
+ return "you have no open asks";
15721
+ const hints = asks.slice(0, askHintLimit).map((ask) => `${ask.id.slice(0, 8)} \u2014 ${textHead(ask.question)}`);
15722
+ return `your open asks: ${hints.join("; ")}`;
15723
+ }
15724
+ async function resolveAskArgument(tool, args, client, sessionId) {
15725
+ const id = askId(args);
15726
+ if (normalizeUUID(id) === undefined && !idPrefixPattern.test(id)) {
15727
+ const asks = await sessionOpenAsks(client, sessionId).catch(() => {
15728
+ return;
15729
+ });
15730
+ throw new ToolInputError(tool, [
15731
+ asks === undefined ? askIdShapeProblem : `${askIdShapeProblem}; ${openAskHints(asks)}`
15732
+ ]);
15733
+ }
15734
+ return resolveIdPrefix(tool, "ask", id, "this session", () => sessionOpenAsks(client, sessionId));
15735
+ }
15709
15736
  function messageInReplyTo(args) {
15710
15737
  const inReplyTo = optionalString(args, "in_reply_to");
15711
15738
  return inReplyTo === undefined ? undefined : messageIdOf(inReplyTo);
@@ -15724,7 +15751,8 @@ function argumentProblems(tool, args) {
15724
15751
  break;
15725
15752
  }
15726
15753
  case "dispatch_edit_ask":
15727
- case "dispatch_resolve_ask": {
15754
+ case "dispatch_resolve_ask":
15755
+ case "dispatch_follow": {
15728
15756
  const ask = optionalString(args, "ask");
15729
15757
  if (ask?.startsWith("dispatch://") && parseDispatchRef(ask)?.kind !== "ask") {
15730
15758
  problems.push(askIdProblem);
@@ -16518,7 +16546,8 @@ async function executeDispatchTool(input) {
16518
16546
  }
16519
16547
  case "dispatch_resolve_ask": {
16520
16548
  const kind = stringArg(args, "kind");
16521
- const ask = await client.resolveAsk(stringArg(args, "ask"), {
16549
+ const id = await resolveAskArgument(input.tool, args, client, input.sessionId);
16550
+ const ask = await client.resolveAsk(id, {
16522
16551
  kind,
16523
16552
  reason: stringArg(args, "reason"),
16524
16553
  actor
@@ -16537,11 +16566,11 @@ async function executeDispatchTool(input) {
16537
16566
  let document;
16538
16567
  if (ref?.owner.kind === "issue") {
16539
16568
  const issueKey = ref.owner.issue;
16540
- id = await resolveIdPrefix(input.tool, "comment", ref, () => client.getComments(issueKey));
16569
+ id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), () => client.getComments(issueKey));
16541
16570
  } else if (ref !== null) {
16542
16571
  const artifact = (await resolveArtifact(client, ref.owner, ref.artifact)).artifact;
16543
16572
  document = artifact;
16544
- id = await resolveIdPrefix(input.tool, "comment", ref, () => client.getArtifactComments(artifact.id));
16573
+ id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), () => client.getArtifactComments(artifact.id));
16545
16574
  }
16546
16575
  const comment = await client.resolveComment(id, actor);
16547
16576
  const { label, details } = await commentOwnerResult(client, comment, document);
@@ -16585,7 +16614,8 @@ ${followsAsk(askOwner)}`,
16585
16614
  const options = args.options;
16586
16615
  const multiple = optionalBoolean(args, "multiple");
16587
16616
  const urgency = askUrgency(args);
16588
- const ask = await client.editAsk(askId(args), {
16617
+ const id = await resolveAskArgument(input.tool, args, client, input.sessionId);
16618
+ const ask = await client.editAsk(id, {
16589
16619
  ...question === undefined ? {} : { question },
16590
16620
  ...Array.isArray(options) ? { options } : {},
16591
16621
  ...multiple === undefined ? {} : { multiple },
@@ -16812,7 +16842,7 @@ ${trailer.join(`
16812
16842
  const sessionId = input.sessionId?.trim();
16813
16843
  if (!sessionId)
16814
16844
  throw new Error("host session id is required for dispatch_follow");
16815
- const ask = askId(args);
16845
+ const ask = await resolveAskArgument(input.tool, args, client, sessionId);
16816
16846
  const action = stringArg(args, "action");
16817
16847
  if (action === "unfollow") {
16818
16848
  await client.unfollowAsk(ask, sessionId, actor);
@@ -16828,7 +16858,7 @@ ${trailer.join(`
16828
16858
  case "dispatch_read": {
16829
16859
  if (ownerArguments.ref?.kind === "ask") {
16830
16860
  const ref = ownerArguments.ref;
16831
- const id = await resolveIdPrefix(input.tool, "ask", ref, async () => ref.owner.kind === "issue" ? client.listIssueAsks(ref.owner.issue) : client.getArtifactAsks((await resolveArtifact(client, ref.owner, ref.artifact)).artifact.id, "all"));
16861
+ const id = await resolveIdPrefix(input.tool, "ask", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.listIssueAsks(ref.owner.issue) : client.getArtifactAsks((await resolveArtifact(client, ref.owner, ref.artifact)).artifact.id, "all"));
16832
16862
  const askRead = await client.getAsk(id);
16833
16863
  const askRef = refTarget(ref, "ask", id);
16834
16864
  return {
@@ -16838,7 +16868,7 @@ ${trailer.join(`
16838
16868
  }
16839
16869
  if (ownerArguments.ref?.kind === "comment") {
16840
16870
  const ref = ownerArguments.ref;
16841
- const id = await resolveIdPrefix(input.tool, "comment", ref, async () => ref.owner.kind === "issue" ? client.getComments(ref.owner.issue) : client.getArtifactComments((await resolveArtifact(client, ref.owner, ref.artifact)).artifact.id));
16871
+ const id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.getComments(ref.owner.issue) : client.getArtifactComments((await resolveArtifact(client, ref.owner, ref.artifact)).artifact.id));
16842
16872
  const comment = await client.getComment(id);
16843
16873
  const commentRef = refTarget(ref, "comment", id);
16844
16874
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "1.45.5",
3
+ "version": "1.46.1",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -708,7 +708,9 @@ the host tells you once per ask. Leave a thread you no longer need, or rejoin on
708
708
  dispatch_follow({ ask, action: "follow" | "unfollow" })
709
709
  ```
710
710
 
711
- `ask` is the full ask id or a `dispatch://KEY/ask/<id>` reference. A human may also remove you from the ask card; either way you are
711
+ `ask` is the full ask id or a `dispatch://KEY/ask/<id>` reference; `dispatch_follow`, `dispatch_edit_ask`, and
712
+ `dispatch_resolve_ask` also take an 8+ hex prefix that is unique among your own open asks, and refuse anything shorter by naming
713
+ those asks. A human may also remove you from the ask card; either way you are
712
714
  told with an `ask.follower_removed` notice, and a human adding you arrives as `ask.follower_added`.
713
715
 
714
716
  No write subscribes you to an issue or document. Following covers your own asks and the threads you joined; everything else on the