@sjawhar/opencode-legion-envoy 3.9.0 → 3.10.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.
@@ -16041,13 +16041,23 @@ async function resolveOwnerArguments(tool, input, cwd, env, exec, serverUrl, pro
16041
16041
  const issue2 = `${repo2}#${legionIssue}`;
16042
16042
  return { args: { ...args, issue: issue2 }, ref, owner: { kind: "issue", issue: issue2 } };
16043
16043
  }
16044
- function artifactByReference(artifacts, artifactReference, owner) {
16045
- const byIdOrSlug = artifacts.find((candidate) => candidate.id === artifactReference) ?? artifacts.find((candidate) => candidate.slug === artifactReference);
16046
- if (byIdOrSlug !== undefined)
16047
- return byIdOrSlug;
16044
+ function artifactByReference(artifacts, artifactReference, owner, canonical) {
16045
+ const bySlug = artifacts.find((candidate) => candidate.slug === artifactReference);
16046
+ if (canonical && bySlug !== undefined)
16047
+ return bySlug;
16048
+ const byId = artifacts.find((candidate) => candidate.id === artifactReference);
16049
+ if (byId !== undefined)
16050
+ return byId;
16048
16051
  const byName = artifacts.filter((candidate) => candidate.name === artifactReference);
16052
+ if (bySlug !== undefined) {
16053
+ const named = byName.filter((candidate) => candidate.id !== bySlug.id && candidate.kind === "doc");
16054
+ if (named.length > 0) {
16055
+ throw new Error(documentReferenceProblem(artifactReference, [bySlug, ...named], owner, "id"));
16056
+ }
16057
+ return bySlug;
16058
+ }
16049
16059
  if (byName.length > 1) {
16050
- throw new Error(documentReferenceProblem(artifactReference, byName, owner, true));
16060
+ throw new Error(documentReferenceProblem(artifactReference, byName, owner, "slug"));
16051
16061
  }
16052
16062
  const [artifact] = byName;
16053
16063
  if (artifact === undefined) {
@@ -16055,32 +16065,31 @@ function artifactByReference(artifacts, artifactReference, owner) {
16055
16065
  }
16056
16066
  return artifact;
16057
16067
  }
16058
- async function resolveArtifact(client, owner, artifactReference) {
16068
+ async function resolveArtifact(client, owner, artifactReference, { canonical = false } = {}) {
16059
16069
  if (owner.kind === "project") {
16060
16070
  if (artifactReference === undefined) {
16061
16071
  throw new Error("artifact is required for a project document");
16062
16072
  }
16063
- try {
16064
- return {
16065
- owner,
16066
- artifact: await client.getProjectArtifact(owner.project, artifactReference)
16067
- };
16068
- } catch (error48) {
16073
+ const routed = await client.getProjectArtifact(owner.project, artifactReference).catch((error48) => {
16069
16074
  if (!(error48 instanceof DispatchServiceError) || error48.status !== 404)
16070
16075
  throw error48;
16071
- const artifacts = await client.listProjectArtifacts(owner.project, true);
16072
- return {
16073
- owner,
16074
- artifact: artifactByReference(artifacts, artifactReference, "project")
16075
- };
16076
- }
16076
+ return;
16077
+ });
16078
+ if (canonical && routed !== undefined)
16079
+ return { owner, artifact: routed };
16080
+ const unlinked = await client.listProjectArtifacts(owner.project, true);
16081
+ const artifacts = routed === undefined ? unlinked : [routed, ...unlinked.filter((candidate) => candidate.id !== routed.id)];
16082
+ return {
16083
+ owner,
16084
+ artifact: artifactByReference(artifacts, artifactReference, "project", canonical)
16085
+ };
16077
16086
  }
16078
16087
  const issue2 = await client.getIssue(owner.issue);
16079
16088
  let artifact;
16080
16089
  if (artifactReference === undefined || artifactReference === "spec") {
16081
16090
  artifact = issue2.artifacts.find((candidate) => candidate.primary || candidate.id === issue2.primary_artifact_id);
16082
16091
  } else {
16083
- artifact = artifactByReference(issue2.artifacts, artifactReference, "issue");
16092
+ artifact = artifactByReference(issue2.artifacts, artifactReference, "issue", canonical);
16084
16093
  }
16085
16094
  if (!artifact) {
16086
16095
  throw new Error(documentReferenceProblem(artifactReference ?? "spec", issue2.artifacts, "issue"));
@@ -16088,10 +16097,10 @@ async function resolveArtifact(client, owner, artifactReference) {
16088
16097
  return { owner, issue: issue2, artifact };
16089
16098
  }
16090
16099
  var documentHintLimit = 8;
16091
- function documentReferenceProblem(reference, documents, owner, ambiguous = false) {
16092
- const hints = documents.slice(0, documentHintLimit).map((document) => `${document.slug} (${document.name})`);
16100
+ function documentReferenceProblem(reference, documents, owner, use) {
16101
+ const hints = documents.slice(0, documentHintLimit).map((document) => use === "id" ? `${document.id} (${document.slug}, ${document.name})` : `${document.slug} (${document.name})`);
16093
16102
  const list = hints.length === 0 ? "none" : hints.join(", ");
16094
- return ambiguous ? `"${reference}" names ${documents.length} documents on this ${owner}; use a slug: ${list}` : `document "${reference}" not found by slug; this ${owner}'s documents: ${list}`;
16103
+ return use === undefined ? `document "${reference}" not found by slug; this ${owner}'s documents: ${list}` : `"${reference}" names ${documents.length} documents on this ${owner}; use ${use === "id" ? "the id" : "a slug"}: ${list}`;
16095
16104
  }
16096
16105
  var askHintLimit = 8;
16097
16106
  function askIDInputProblem(asks, scope) {
@@ -16481,6 +16490,10 @@ async function executeDispatchTool(input) {
16481
16490
  const args = parsed.success ? parsed.data : ownerArguments.args;
16482
16491
  const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
16483
16492
  const client = dispatchClient();
16493
+ const refDocument = ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : ownerArguments.ref?.artifact;
16494
+ const resolveDocument = (documentOwner2, reference) => resolveArtifact(client, documentOwner2, reference, {
16495
+ canonical: reference !== undefined && reference === refDocument
16496
+ });
16484
16497
  const owner = ownerArguments.owner?.kind === "issue" ? {
16485
16498
  kind: "issue",
16486
16499
  issue: await resolveExistingIssue(client, ownerArguments.owner.issue)
@@ -16771,7 +16784,7 @@ async function executeDispatchTool(input) {
16771
16784
  const issueKey = ref.owner.issue;
16772
16785
  id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), () => client.getComments(issueKey));
16773
16786
  } else if (ref !== null) {
16774
- const artifact = (await resolveArtifact(client, ref.owner, ref.artifact)).artifact;
16787
+ const artifact = (await resolveArtifact(client, ref.owner, ref.artifact, { canonical: true })).artifact;
16775
16788
  document = artifact;
16776
16789
  id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), () => client.getArtifactComments(artifact.id));
16777
16790
  }
@@ -16783,7 +16796,7 @@ async function executeDispatchTool(input) {
16783
16796
  const anchorArgs = asObject(args.anchor);
16784
16797
  const owner2 = documentOwner();
16785
16798
  const artifactReference = optionalString(args, "artifact") ?? (anchorArgs === null ? undefined : optionalString(anchorArgs, "artifact"));
16786
- const resolved = owner2.kind === "project" || artifactReference === undefined ? owner2.kind === "project" ? await resolveArtifact(client, owner2, artifactReference) : undefined : await resolveArtifact(client, owner2, artifactReference);
16799
+ const resolved = owner2.kind === "project" || artifactReference === undefined ? owner2.kind === "project" ? await resolveDocument(owner2, artifactReference) : undefined : await resolveDocument(owner2, artifactReference);
16787
16800
  const options = args.options;
16788
16801
  const multiple = optionalBoolean(args, "multiple");
16789
16802
  const urgency = askUrgency(args);
@@ -16833,7 +16846,7 @@ ${followsAsk(askOwner)}`,
16833
16846
  case "dispatch_comment": {
16834
16847
  const artifactReference = optionalString(args, "artifact");
16835
16848
  const owner2 = documentOwner();
16836
- const resolved = owner2.kind === "project" || artifactReference === undefined ? owner2.kind === "project" ? await resolveArtifact(client, owner2, artifactReference) : undefined : await resolveArtifact(client, owner2, artifactReference);
16849
+ const resolved = owner2.kind === "project" || artifactReference === undefined ? owner2.kind === "project" ? await resolveDocument(owner2, artifactReference) : undefined : await resolveDocument(owner2, artifactReference);
16837
16850
  const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
16838
16851
  const replyTo = optionalString(args, "reply_to");
16839
16852
  const replyToAskReference = optionalString(args, "reply_to_ask");
@@ -16886,7 +16899,7 @@ ${followsAsk(askOwner)}`,
16886
16899
  };
16887
16900
  }
16888
16901
  case "dispatch_suggest": {
16889
- const resolved = await resolveArtifact(client, documentOwner(), stringArg(args, "artifact"));
16902
+ const resolved = await resolveDocument(documentOwner(), stringArg(args, "artifact"));
16890
16903
  const anchored = anchor(resolved.artifact, args);
16891
16904
  if (anchored === undefined)
16892
16905
  throw new Error("quote is required");
@@ -16941,7 +16954,7 @@ ${followsAsk(askOwner)}`,
16941
16954
  };
16942
16955
  }
16943
16956
  case "dispatch_doc_edit": {
16944
- const resolved = await resolveArtifact(client, documentOwner(), stringArg(args, "artifact"));
16957
+ const resolved = await resolveDocument(documentOwner(), stringArg(args, "artifact"));
16945
16958
  const ops = args.ops;
16946
16959
  const summary = optionalString(args, "summary");
16947
16960
  const { precondition: rawPrecondition } = args;
@@ -16983,7 +16996,7 @@ ${followsAsk(askOwner)}`,
16983
16996
  }
16984
16997
  case "dispatch_doc_read": {
16985
16998
  const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
16986
- const resolved = await resolveArtifact(client, documentOwner(), artifactReference);
16999
+ const resolved = await resolveDocument(documentOwner(), artifactReference);
16987
17000
  const version2 = optionalNumber(args, "version") ?? ownerArguments.ref?.version;
16988
17001
  const documentPromise = client.docRead(resolved.artifact.id, version2);
16989
17002
  const marksPromise = openArtifactMarks(client, resolved);
@@ -17012,7 +17025,7 @@ ${trailer.join(`
17012
17025
  }
17013
17026
  case "dispatch_request_approval": {
17014
17027
  const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
17015
- const resolved = await resolveArtifact(client, documentOwner(), artifactReference);
17028
+ const resolved = await resolveDocument(documentOwner(), artifactReference);
17016
17029
  const result = await client.requestApproval(resolved.artifact.id, { actor });
17017
17030
  if (result.ask === null) {
17018
17031
  return {
@@ -17090,7 +17103,7 @@ ${trailer.join(`
17090
17103
  case "dispatch_read": {
17091
17104
  if (ownerArguments.ref?.kind === "ask") {
17092
17105
  const ref = ownerArguments.ref;
17093
- 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"));
17106
+ const id = await resolveIdPrefix(input.tool, "ask", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.listIssueAsks(ref.owner.issue) : client.getArtifactAsks((await resolveDocument(ref.owner, ref.artifact)).artifact.id, "all"));
17094
17107
  const askRead = await client.getAsk(id);
17095
17108
  const askRef = refTarget(ref, "ask", id);
17096
17109
  return {
@@ -17100,7 +17113,7 @@ ${trailer.join(`
17100
17113
  }
17101
17114
  if (ownerArguments.ref?.kind === "comment") {
17102
17115
  const ref = ownerArguments.ref;
17103
- 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));
17116
+ const id = await resolveIdPrefix(input.tool, "comment", ref.id, refOwnerName(ref), async () => ref.owner.kind === "issue" ? client.getComments(ref.owner.issue) : client.getArtifactComments((await resolveDocument(ref.owner, ref.artifact)).artifact.id));
17104
17117
  const comment = await client.getComment(id);
17105
17118
  const commentRef = refTarget(ref, "comment", id);
17106
17119
  return {
@@ -17120,7 +17133,7 @@ ${trailer.join(`
17120
17133
  };
17121
17134
  }
17122
17135
  if (documentOwner().kind === "project") {
17123
- const resolved = await resolveArtifact(client, documentOwner(), stringArg(args, "artifact"));
17136
+ const resolved = await resolveDocument(documentOwner(), stringArg(args, "artifact"));
17124
17137
  const documentRef = dispatchDocumentRef(resolved.artifact.project, resolved.artifact.slug);
17125
17138
  return {
17126
17139
  text: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -603,7 +603,8 @@ dispatch_doc_read({ issue?, project?, artifact?, version?, ref? })
603
603
  ```
604
604
  It returns live or versioned markdown with open marks. A live read ends with a document token; `issue` with an
605
605
  omitted `artifact` reads the issue specification; a project needs `artifact`; and a
606
- `dispatch://PROJECT/artifact/<document-ref>` ref supplies both, where `document-ref` is the id, slug, or filename.
606
+ `dispatch://PROJECT/artifact/<document-ref>` ref supplies both, where `document-ref` is the slug (an id or a
607
+ filename resolves when no document has that slug).
607
608
 
608
609
  Editing one is [Editing a document](references/document-edits.md): the shape of `dispatch_doc_edit`,
609
610
  how to quote the text you mean, one `replace` per paragraph, preconditions against a stale edit, and
@@ -741,7 +742,9 @@ Exactly one of `path` and `content` is required. It returns issue or project-doc
741
742
  Uploading the same `name` creates its next version — so uploading `spec.md` **replaces the issue's own specification**
742
743
  with your text. Never do that: the spec is edited in place with `dispatch_doc_edit` (see [Editing a document](references/document-edits.md)). Address an existing
743
744
  artifact by the slug shown in the upload result or by its filename, and a project document by its artifact id, slug, or filename; the
744
- slug also arrives on `artifact.created` events.
745
+ slug also arrives on `artifact.created` events. Dispatch suffixes a slug two documents would share, so one document's
746
+ filename can be another's slug (`plan v2` takes `plan-v2`, then a document named `plan-v2` takes `plan-v2-2`): a bare
747
+ `artifact` that names both is refused with each one's id, while a `dispatch://` reference's document part is always the slug.
745
748
 
746
749
  **Where a deliverable goes.** Text the human must read to decide — a draft message, a proposal,
747
750
  a summary — goes in the spec as a section: the spec is the one document they open. A separate