@sjawhar/pi-legion-envoy 0.19.0 → 0.20.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 +4 -0
- package/dist/envoy.js +44 -14
- package/dist/legion.js +44 -14
- package/dist/skills/dispatch/SKILL.md +11 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,6 +118,10 @@ The shared contract supplies the model-facing schemas and descriptions. The
|
|
|
118
118
|
`dispatch` skill describes when to use each operation for issues, asks, review
|
|
119
119
|
feedback, documents, artifacts, and status reads.
|
|
120
120
|
|
|
121
|
+
`dispatch_artifact` accepts exactly one upload source: a local `path`, or inline `content`.
|
|
122
|
+
For example, an architect can post its primary specification with
|
|
123
|
+
`{ issue, name: "spec.md", content: "# Design", primary: true }`.
|
|
124
|
+
|
|
121
125
|
Lifecycle and scope decisions between Legion roles go through `envoy_publish` to the owning
|
|
122
126
|
architect's role topic; Dispatch is for durable questions to the human and the shared
|
|
123
127
|
document, not for coordination between roles.
|
package/dist/envoy.js
CHANGED
|
@@ -29687,6 +29687,10 @@ var ChildStatusEventPayloadSchema = object({
|
|
|
29687
29687
|
to: string2().optional()
|
|
29688
29688
|
});
|
|
29689
29689
|
// ../contracts/src/dispatch-tools.ts
|
|
29690
|
+
function dispatchToolSchema(spec, z, opts) {
|
|
29691
|
+
const shape = spec.arguments(z);
|
|
29692
|
+
return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
|
|
29693
|
+
}
|
|
29690
29694
|
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).";
|
|
29691
29695
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
29692
29696
|
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
@@ -29783,14 +29787,22 @@ var dispatchToolSpecs = [
|
|
|
29783
29787
|
},
|
|
29784
29788
|
{
|
|
29785
29789
|
name: "dispatch_artifact",
|
|
29786
|
-
description: "
|
|
29790
|
+
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}`,
|
|
29787
29791
|
arguments: (z) => ({
|
|
29788
29792
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29789
29793
|
name: z.string().describe("Artifact filename shown in Dispatch."),
|
|
29790
|
-
path: z.string().describe("Local path to the file to upload."),
|
|
29794
|
+
path: z.string().describe("Local path to the file to upload.").optional(),
|
|
29795
|
+
content: z.string().describe("Inline text to store as a Markdown document.").optional(),
|
|
29791
29796
|
primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
|
|
29792
29797
|
summary: z.string().describe("Optional version summary.").optional()
|
|
29793
|
-
})
|
|
29798
|
+
}),
|
|
29799
|
+
validation: {
|
|
29800
|
+
check: (value) => {
|
|
29801
|
+
const input = value;
|
|
29802
|
+
return typeof input.path === "string" !== (typeof input.content === "string");
|
|
29803
|
+
},
|
|
29804
|
+
message: "Exactly one of path or content is required."
|
|
29805
|
+
}
|
|
29794
29806
|
},
|
|
29795
29807
|
{
|
|
29796
29808
|
name: "dispatch_read",
|
|
@@ -30198,7 +30210,18 @@ function zodSchemaApi(zod) {
|
|
|
30198
30210
|
schema = schema.max(opts.max);
|
|
30199
30211
|
return schema;
|
|
30200
30212
|
},
|
|
30201
|
-
object: (shape) =>
|
|
30213
|
+
object: (shape, opts = {}) => {
|
|
30214
|
+
let schema = api.object(shape);
|
|
30215
|
+
if (opts.strict)
|
|
30216
|
+
schema = schema.strict();
|
|
30217
|
+
return schema;
|
|
30218
|
+
},
|
|
30219
|
+
refineObject: (shape, check, message, opts = {}) => {
|
|
30220
|
+
let schema = api.object(shape);
|
|
30221
|
+
if (opts.strict)
|
|
30222
|
+
schema = schema.strict();
|
|
30223
|
+
return schema.refine(check, message);
|
|
30224
|
+
}
|
|
30202
30225
|
};
|
|
30203
30226
|
}
|
|
30204
30227
|
// ../envoy-client/src/defaults.ts
|
|
@@ -30943,9 +30966,6 @@ var DEFAULT_SERVER_URL = "http://localhost:8766";
|
|
|
30943
30966
|
function normalizeDispatchUrl(url2) {
|
|
30944
30967
|
return url2.replace(/\/+$/, "");
|
|
30945
30968
|
}
|
|
30946
|
-
function deprecatedMcpUrl(url2) {
|
|
30947
|
-
return normalizeDispatchUrl(url2).replace(/\/mcp$/, "");
|
|
30948
|
-
}
|
|
30949
30969
|
function parsedDispatchUrl(value, source) {
|
|
30950
30970
|
const url2 = normalizeDispatchUrl(value);
|
|
30951
30971
|
try {
|
|
@@ -30995,7 +31015,6 @@ function readEnvoyFile(filePath) {
|
|
|
30995
31015
|
}
|
|
30996
31016
|
function resolveDispatchConfig(env, options = {}) {
|
|
30997
31017
|
const explicitUrl = env.DISPATCH_URL;
|
|
30998
|
-
const deprecatedUrl = explicitUrl ? undefined : env.DISPATCH_MCP_URL;
|
|
30999
31018
|
const home = options.home ?? env.HOME ?? homedir();
|
|
31000
31019
|
const cwd = options.cwd ?? process.cwd();
|
|
31001
31020
|
const userFile = readEnvoyFile(path.join(home, ".config", "opencode", "envoy.json"));
|
|
@@ -31009,7 +31028,7 @@ function resolveDispatchConfig(env, options = {}) {
|
|
|
31009
31028
|
...userFile.kind === "valid" ? userFile.settings : null,
|
|
31010
31029
|
...repoFile.kind === "valid" ? repoFile.settings : null
|
|
31011
31030
|
};
|
|
31012
|
-
const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } :
|
|
31031
|
+
const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
|
|
31013
31032
|
const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
|
|
31014
31033
|
const token = env.DISPATCH_TOKEN ?? merged.token ?? null;
|
|
31015
31034
|
const tokenSource = env.DISPATCH_TOKEN === undefined ? "dispatch.token" : "DISPATCH_TOKEN";
|
|
@@ -31176,6 +31195,9 @@ class DispatchClient {
|
|
|
31176
31195
|
return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "messages"], input);
|
|
31177
31196
|
}
|
|
31178
31197
|
async artifact(issue, input) {
|
|
31198
|
+
const artifactPath = ["api", "v1", "issues", await this.#resolveIssue(issue), "artifacts"];
|
|
31199
|
+
if ("content" in input)
|
|
31200
|
+
return this.#json("POST", artifactPath, input);
|
|
31179
31201
|
const form = new FormData;
|
|
31180
31202
|
form.set("name", input.name);
|
|
31181
31203
|
if (input.primary !== undefined)
|
|
@@ -31185,7 +31207,7 @@ class DispatchClient {
|
|
|
31185
31207
|
if (input.actor !== undefined)
|
|
31186
31208
|
form.set("actor", JSON.stringify(input.actor));
|
|
31187
31209
|
form.set("file", input.file, input.name);
|
|
31188
|
-
return this.#form("POST",
|
|
31210
|
+
return this.#form("POST", artifactPath, form);
|
|
31189
31211
|
}
|
|
31190
31212
|
async getArtifact(id) {
|
|
31191
31213
|
return this.#json("GET", ["api", "v1", "artifacts", id]);
|
|
@@ -31370,7 +31392,7 @@ function toolSchema(tool) {
|
|
|
31370
31392
|
const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
|
|
31371
31393
|
if (!spec)
|
|
31372
31394
|
throw new Error(`Unknown Dispatch tool: ${tool}`);
|
|
31373
|
-
return
|
|
31395
|
+
return dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
|
|
31374
31396
|
}
|
|
31375
31397
|
async function resolveIssueArguments(tool, args, cwd, env, exec) {
|
|
31376
31398
|
if (tool === "dispatch_issue")
|
|
@@ -31652,12 +31674,20 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
31652
31674
|
case "dispatch_artifact": {
|
|
31653
31675
|
const primary = optionalBoolean(args, "primary");
|
|
31654
31676
|
const summary = optionalString(args, "summary");
|
|
31655
|
-
const
|
|
31656
|
-
|
|
31677
|
+
const name = stringArg(args, "name");
|
|
31678
|
+
const content = optionalString(args, "content");
|
|
31679
|
+
const result = await client.artifact(issue(), content === undefined ? {
|
|
31680
|
+
name,
|
|
31657
31681
|
file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
|
|
31658
31682
|
...primary === undefined ? {} : { primary },
|
|
31659
31683
|
...summary === undefined ? {} : { summary },
|
|
31660
31684
|
actor
|
|
31685
|
+
} : {
|
|
31686
|
+
name,
|
|
31687
|
+
content,
|
|
31688
|
+
...primary === undefined ? {} : { primary },
|
|
31689
|
+
...summary === undefined ? {} : { summary },
|
|
31690
|
+
actor
|
|
31661
31691
|
});
|
|
31662
31692
|
return {
|
|
31663
31693
|
text: `Uploaded ${result.artifact.name} as version ${result.version.number}`,
|
|
@@ -32570,7 +32600,7 @@ function envoyExtension(pi) {
|
|
|
32570
32600
|
name: spec.name,
|
|
32571
32601
|
label: spec.name,
|
|
32572
32602
|
description: spec.description,
|
|
32573
|
-
parameters:
|
|
32603
|
+
parameters: dispatchToolSchema(spec, zodSchemaApi(pi.zod)),
|
|
32574
32604
|
execute: async (_id, params, _signal, _onUpdate, context) => {
|
|
32575
32605
|
try {
|
|
32576
32606
|
const result = await executeDispatchTool({
|
package/dist/legion.js
CHANGED
|
@@ -29685,6 +29685,10 @@ var ChildStatusEventPayloadSchema = object({
|
|
|
29685
29685
|
to: string2().optional()
|
|
29686
29686
|
});
|
|
29687
29687
|
// ../contracts/src/dispatch-tools.ts
|
|
29688
|
+
function dispatchToolSchema(spec, z, opts) {
|
|
29689
|
+
const shape = spec.arguments(z);
|
|
29690
|
+
return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
|
|
29691
|
+
}
|
|
29688
29692
|
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).";
|
|
29689
29693
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
29690
29694
|
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
@@ -29781,14 +29785,22 @@ var dispatchToolSpecs = [
|
|
|
29781
29785
|
},
|
|
29782
29786
|
{
|
|
29783
29787
|
name: "dispatch_artifact",
|
|
29784
|
-
description: "
|
|
29788
|
+
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}`,
|
|
29785
29789
|
arguments: (z) => ({
|
|
29786
29790
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29787
29791
|
name: z.string().describe("Artifact filename shown in Dispatch."),
|
|
29788
|
-
path: z.string().describe("Local path to the file to upload."),
|
|
29792
|
+
path: z.string().describe("Local path to the file to upload.").optional(),
|
|
29793
|
+
content: z.string().describe("Inline text to store as a Markdown document.").optional(),
|
|
29789
29794
|
primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
|
|
29790
29795
|
summary: z.string().describe("Optional version summary.").optional()
|
|
29791
|
-
})
|
|
29796
|
+
}),
|
|
29797
|
+
validation: {
|
|
29798
|
+
check: (value) => {
|
|
29799
|
+
const input = value;
|
|
29800
|
+
return typeof input.path === "string" !== (typeof input.content === "string");
|
|
29801
|
+
},
|
|
29802
|
+
message: "Exactly one of path or content is required."
|
|
29803
|
+
}
|
|
29792
29804
|
},
|
|
29793
29805
|
{
|
|
29794
29806
|
name: "dispatch_read",
|
|
@@ -30196,7 +30208,18 @@ function zodSchemaApi(zod) {
|
|
|
30196
30208
|
schema = schema.max(opts.max);
|
|
30197
30209
|
return schema;
|
|
30198
30210
|
},
|
|
30199
|
-
object: (shape) =>
|
|
30211
|
+
object: (shape, opts = {}) => {
|
|
30212
|
+
let schema = api.object(shape);
|
|
30213
|
+
if (opts.strict)
|
|
30214
|
+
schema = schema.strict();
|
|
30215
|
+
return schema;
|
|
30216
|
+
},
|
|
30217
|
+
refineObject: (shape, check, message, opts = {}) => {
|
|
30218
|
+
let schema = api.object(shape);
|
|
30219
|
+
if (opts.strict)
|
|
30220
|
+
schema = schema.strict();
|
|
30221
|
+
return schema.refine(check, message);
|
|
30222
|
+
}
|
|
30200
30223
|
};
|
|
30201
30224
|
}
|
|
30202
30225
|
// ../envoy-client/src/defaults.ts
|
|
@@ -31428,9 +31451,6 @@ var DEFAULT_SERVER_URL = "http://localhost:8766";
|
|
|
31428
31451
|
function normalizeDispatchUrl(url2) {
|
|
31429
31452
|
return url2.replace(/\/+$/, "");
|
|
31430
31453
|
}
|
|
31431
|
-
function deprecatedMcpUrl(url2) {
|
|
31432
|
-
return normalizeDispatchUrl(url2).replace(/\/mcp$/, "");
|
|
31433
|
-
}
|
|
31434
31454
|
function parsedDispatchUrl(value, source) {
|
|
31435
31455
|
const url2 = normalizeDispatchUrl(value);
|
|
31436
31456
|
try {
|
|
@@ -31480,7 +31500,6 @@ function readEnvoyFile(filePath) {
|
|
|
31480
31500
|
}
|
|
31481
31501
|
function resolveDispatchConfig(env, options = {}) {
|
|
31482
31502
|
const explicitUrl = env.DISPATCH_URL;
|
|
31483
|
-
const deprecatedUrl = explicitUrl ? undefined : env.DISPATCH_MCP_URL;
|
|
31484
31503
|
const home = options.home ?? env.HOME ?? homedir();
|
|
31485
31504
|
const cwd = options.cwd ?? process.cwd();
|
|
31486
31505
|
const userFile = readEnvoyFile(path3.join(home, ".config", "opencode", "envoy.json"));
|
|
@@ -31494,7 +31513,7 @@ function resolveDispatchConfig(env, options = {}) {
|
|
|
31494
31513
|
...userFile.kind === "valid" ? userFile.settings : null,
|
|
31495
31514
|
...repoFile.kind === "valid" ? repoFile.settings : null
|
|
31496
31515
|
};
|
|
31497
|
-
const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } :
|
|
31516
|
+
const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
|
|
31498
31517
|
const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
|
|
31499
31518
|
const token = env.DISPATCH_TOKEN ?? merged.token ?? null;
|
|
31500
31519
|
const tokenSource = env.DISPATCH_TOKEN === undefined ? "dispatch.token" : "DISPATCH_TOKEN";
|
|
@@ -31661,6 +31680,9 @@ class DispatchClient {
|
|
|
31661
31680
|
return this.#json("POST", ["api", "v1", "issues", await this.#resolveIssue(issue), "messages"], input);
|
|
31662
31681
|
}
|
|
31663
31682
|
async artifact(issue, input) {
|
|
31683
|
+
const artifactPath = ["api", "v1", "issues", await this.#resolveIssue(issue), "artifacts"];
|
|
31684
|
+
if ("content" in input)
|
|
31685
|
+
return this.#json("POST", artifactPath, input);
|
|
31664
31686
|
const form = new FormData;
|
|
31665
31687
|
form.set("name", input.name);
|
|
31666
31688
|
if (input.primary !== undefined)
|
|
@@ -31670,7 +31692,7 @@ class DispatchClient {
|
|
|
31670
31692
|
if (input.actor !== undefined)
|
|
31671
31693
|
form.set("actor", JSON.stringify(input.actor));
|
|
31672
31694
|
form.set("file", input.file, input.name);
|
|
31673
|
-
return this.#form("POST",
|
|
31695
|
+
return this.#form("POST", artifactPath, form);
|
|
31674
31696
|
}
|
|
31675
31697
|
async getArtifact(id) {
|
|
31676
31698
|
return this.#json("GET", ["api", "v1", "artifacts", id]);
|
|
@@ -31855,7 +31877,7 @@ function toolSchema(tool) {
|
|
|
31855
31877
|
const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
|
|
31856
31878
|
if (!spec)
|
|
31857
31879
|
throw new Error(`Unknown Dispatch tool: ${tool}`);
|
|
31858
|
-
return
|
|
31880
|
+
return dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
|
|
31859
31881
|
}
|
|
31860
31882
|
async function resolveIssueArguments(tool, args, cwd, env, exec) {
|
|
31861
31883
|
if (tool === "dispatch_issue")
|
|
@@ -32137,12 +32159,20 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
32137
32159
|
case "dispatch_artifact": {
|
|
32138
32160
|
const primary = optionalBoolean(args, "primary");
|
|
32139
32161
|
const summary = optionalString(args, "summary");
|
|
32140
|
-
const
|
|
32141
|
-
|
|
32162
|
+
const name = stringArg(args, "name");
|
|
32163
|
+
const content = optionalString(args, "content");
|
|
32164
|
+
const result = await client.artifact(issue(), content === undefined ? {
|
|
32165
|
+
name,
|
|
32142
32166
|
file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
|
|
32143
32167
|
...primary === undefined ? {} : { primary },
|
|
32144
32168
|
...summary === undefined ? {} : { summary },
|
|
32145
32169
|
actor
|
|
32170
|
+
} : {
|
|
32171
|
+
name,
|
|
32172
|
+
content,
|
|
32173
|
+
...primary === undefined ? {} : { primary },
|
|
32174
|
+
...summary === undefined ? {} : { summary },
|
|
32175
|
+
actor
|
|
32146
32176
|
});
|
|
32147
32177
|
return {
|
|
32148
32178
|
text: `Uploaded ${result.artifact.name} as version ${result.version.number}`,
|
|
@@ -33047,7 +33077,7 @@ function envoyExtension(pi) {
|
|
|
33047
33077
|
name: spec.name,
|
|
33048
33078
|
label: spec.name,
|
|
33049
33079
|
description: spec.description,
|
|
33050
|
-
parameters:
|
|
33080
|
+
parameters: dispatchToolSchema(spec, zodSchemaApi(pi.zod)),
|
|
33051
33081
|
execute: async (_id, params, _signal, _onUpdate, context) => {
|
|
33052
33082
|
try {
|
|
33053
33083
|
const result = await executeDispatchTool({
|
|
@@ -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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|