@sjawhar/opencode-legion-envoy 0.21.0 → 0.22.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.
package/README.md CHANGED
@@ -29,6 +29,10 @@ envoy.json (`~/.config/opencode/envoy.json`, merged with `<repo>/.opencode/envoy
29
29
  the session working directory, stamps it with the OpenCode session id and title, and stores
30
30
  `details.topic` as tool metadata so a successful mutation subscribes to that exact Dispatch topic.
31
31
 
32
+ `dispatch_artifact` accepts exactly one upload source: a local `path`, or inline `content`.
33
+ An architect can post a primary specification directly with
34
+ `{ issue, name: "spec.md", content: "# Design", primary: true }`.
35
+
32
36
  It also maintains the live session registry metadata needed for Envoy to discover OpenCode sessions and their API ports.
33
37
 
34
38
  Slack topic examples must use the real Slack `team_id`, for example:
@@ -13590,6 +13590,10 @@ var ChildStatusEventPayloadSchema = object({
13590
13590
  to: string2().optional()
13591
13591
  });
13592
13592
  // ../contracts/src/dispatch-tools.ts
13593
+ function dispatchToolSchema(spec, z, opts) {
13594
+ const shape = spec.arguments(z);
13595
+ return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
13596
+ }
13593
13597
  var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue on first use in the repository's mapped project (DISPATCH_REPO_PROJECTS) or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
13594
13598
  var ASK_URGENCIES = ["low", "med", "high", "blocking"];
13595
13599
  var DOC_EDIT_OPS = ["replace", "delete", "insert"];
@@ -13686,14 +13690,22 @@ var dispatchToolSpecs = [
13686
13690
  },
13687
13691
  {
13688
13692
  name: "dispatch_artifact",
13689
- description: "Upload a local file as an issue artifact. Do not use it to edit a live document; use dispatch_doc_edit " + `instead. Files are limited to 25 MiB. ${ISSUE_REFERENCE}`,
13693
+ description: "Attach a local file or inline text as an issue artifact. Do not use it to edit a live document; use " + `dispatch_doc_edit instead. Exactly one of path or content is required; artifacts are limited to 25 MiB. ${ISSUE_REFERENCE}`,
13690
13694
  arguments: (z) => ({
13691
13695
  issue: z.string().describe(ISSUE_REFERENCE),
13692
13696
  name: z.string().describe("Artifact filename shown in Dispatch."),
13693
- path: z.string().describe("Local path to the file to upload."),
13697
+ path: z.string().describe("Local path to the file to upload.").optional(),
13698
+ content: z.string().describe("Inline text to store as a Markdown document.").optional(),
13694
13699
  primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
13695
13700
  summary: z.string().describe("Optional version summary.").optional()
13696
- })
13701
+ }),
13702
+ validation: {
13703
+ check: (value) => {
13704
+ const input = value;
13705
+ return typeof input.path === "string" !== (typeof input.content === "string");
13706
+ },
13707
+ message: "Exactly one of path or content is required."
13708
+ }
13697
13709
  },
13698
13710
  {
13699
13711
  name: "dispatch_read",
@@ -14081,7 +14093,18 @@ function zodSchemaApi(zod) {
14081
14093
  schema = schema.max(opts.max);
14082
14094
  return schema;
14083
14095
  },
14084
- object: (shape) => api.object(shape)
14096
+ object: (shape, opts = {}) => {
14097
+ let schema = api.object(shape);
14098
+ if (opts.strict)
14099
+ schema = schema.strict();
14100
+ return schema;
14101
+ },
14102
+ refineObject: (shape, check, message, opts = {}) => {
14103
+ let schema = api.object(shape);
14104
+ if (opts.strict)
14105
+ schema = schema.strict();
14106
+ return schema.refine(check, message);
14107
+ }
14085
14108
  };
14086
14109
  }
14087
14110
  // ../envoy-client/src/defaults.ts
@@ -14117,9 +14140,6 @@ var DEFAULT_SERVER_URL = "http://localhost:8766";
14117
14140
  function normalizeDispatchUrl(url2) {
14118
14141
  return url2.replace(/\/+$/, "");
14119
14142
  }
14120
- function deprecatedMcpUrl(url2) {
14121
- return normalizeDispatchUrl(url2).replace(/\/mcp$/, "");
14122
- }
14123
14143
  function parsedDispatchUrl(value, source) {
14124
14144
  const url2 = normalizeDispatchUrl(value);
14125
14145
  try {
@@ -14169,7 +14189,6 @@ function readEnvoyFile(filePath) {
14169
14189
  }
14170
14190
  function resolveDispatchConfig(env, options = {}) {
14171
14191
  const explicitUrl = env.DISPATCH_URL;
14172
- const deprecatedUrl = explicitUrl ? undefined : env.DISPATCH_MCP_URL;
14173
14192
  const home = options.home ?? env.HOME ?? homedir();
14174
14193
  const cwd = options.cwd ?? process.cwd();
14175
14194
  const userFile = readEnvoyFile(path.join(home, ".config", "opencode", "envoy.json"));
@@ -14183,7 +14202,7 @@ function resolveDispatchConfig(env, options = {}) {
14183
14202
  ...userFile.kind === "valid" ? userFile.settings : null,
14184
14203
  ...repoFile.kind === "valid" ? repoFile.settings : null
14185
14204
  };
14186
- const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : deprecatedUrl !== undefined ? { value: deprecatedMcpUrl(deprecatedUrl), source: "DISPATCH_MCP_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
14205
+ const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
14187
14206
  const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
14188
14207
  const token = env.DISPATCH_TOKEN ?? merged.token ?? null;
14189
14208
  const tokenSource = env.DISPATCH_TOKEN === undefined ? "dispatch.token" : "DISPATCH_TOKEN";
@@ -14350,6 +14369,9 @@ class DispatchClient {
14350
14369
  return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "messages"], input);
14351
14370
  }
14352
14371
  async artifact(issue, input) {
14372
+ const artifactPath = ["api", "v1", "issues", await this.#resolveIssue(issue), "artifacts"];
14373
+ if ("content" in input)
14374
+ return this.#json("POST", artifactPath, input);
14353
14375
  const form = new FormData;
14354
14376
  form.set("name", input.name);
14355
14377
  if (input.primary !== undefined)
@@ -14359,7 +14381,7 @@ class DispatchClient {
14359
14381
  if (input.actor !== undefined)
14360
14382
  form.set("actor", JSON.stringify(input.actor));
14361
14383
  form.set("file", input.file, input.name);
14362
- return this.#form("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "artifacts"], form);
14384
+ return this.#form("POST", artifactPath, form);
14363
14385
  }
14364
14386
  async getArtifact(id) {
14365
14387
  return this.#json("GET", ["api", "v1", "artifacts", id]);
@@ -14544,7 +14566,7 @@ function toolSchema(tool) {
14544
14566
  const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
14545
14567
  if (!spec)
14546
14568
  throw new Error(`Unknown Dispatch tool: ${tool}`);
14547
- return object(spec.arguments(zodSchemaApi(exports_external))).strict();
14569
+ return dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
14548
14570
  }
14549
14571
  async function resolveIssueArguments(tool, args, cwd, env, exec) {
14550
14572
  if (tool === "dispatch_issue")
@@ -14826,12 +14848,20 @@ Open anchored asks/comments: ${marks.join(", ")}`,
14826
14848
  case "dispatch_artifact": {
14827
14849
  const primary = optionalBoolean(args, "primary");
14828
14850
  const summary = optionalString(args, "summary");
14829
- const result = await client.artifact(issue(), {
14830
- name: stringArg(args, "name"),
14851
+ const name = stringArg(args, "name");
14852
+ const content = optionalString(args, "content");
14853
+ const result = await client.artifact(issue(), content === undefined ? {
14854
+ name,
14831
14855
  file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
14832
14856
  ...primary === undefined ? {} : { primary },
14833
14857
  ...summary === undefined ? {} : { summary },
14834
14858
  actor
14859
+ } : {
14860
+ name,
14861
+ content,
14862
+ ...primary === undefined ? {} : { primary },
14863
+ ...summary === undefined ? {} : { summary },
14864
+ actor
14835
14865
  });
14836
14866
  return {
14837
14867
  text: `Uploaded ${result.artifact.name} as version ${result.version.number}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -105,15 +105,22 @@ invalid actor, route, or primary artifact.
105
105
 
106
106
  ## Artifacts
107
107
 
108
- Attach an image, diagram, or file with:
108
+ Attach an image, diagram, or local file with:
109
109
 
110
110
  ```ts
111
111
  dispatch_artifact({ issue, name, path, primary?, summary? })
112
112
  ```
113
113
 
114
- It returns `details` `{ issue, topic, artifact, version }`. `path` is the local file to upload.
115
- Uploading the same `name` creates its next version. Set `primary: true` only for a markdown file
116
- to make it the issue's spec.
114
+ Or, when the text is already in the call, post a Markdown document directly:
115
+
116
+ ```ts
117
+ dispatch_artifact({ issue, name: "spec.md", content: "# Design\n...", primary: true })
118
+ ```
119
+
120
+ Exactly one of `path` and `content` is required. The inline form sends JSON with
121
+ `Content-Type: application/json`. It returns `details` `{ issue, topic, artifact, version }`.
122
+ Uploading the same `name` creates its next version. Use `content` for an architect's primary spec;
123
+ set `primary: true` only for Markdown documents.
117
124
 
118
125
  ## Messages
119
126