@sjawhar/opencode-legion-envoy 1.14.0 → 1.14.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/dist/src/server.js
CHANGED
|
@@ -13663,7 +13663,9 @@ var ChildStatusEventPayloadSchema = object({
|
|
|
13663
13663
|
var SubscriptionRemovedEventPayloadSchema = object({
|
|
13664
13664
|
session_id: string2().optional(),
|
|
13665
13665
|
by: object({ kind: string2(), id: string2().optional() }).passthrough().optional(),
|
|
13666
|
-
topics: array(string2()).optional()
|
|
13666
|
+
topics: array(string2()).optional(),
|
|
13667
|
+
pending: boolean2().optional(),
|
|
13668
|
+
request_event_id: number2().int().positive().optional()
|
|
13667
13669
|
});
|
|
13668
13670
|
// ../contracts/src/dispatch-snippet.ts
|
|
13669
13671
|
var HTML_ENTITIES = [
|
|
@@ -14616,6 +14618,11 @@ function asErrorShape(value) {
|
|
|
14616
14618
|
function isJson(response) {
|
|
14617
14619
|
return response.headers.get("content-type")?.includes("application/json") ?? false;
|
|
14618
14620
|
}
|
|
14621
|
+
var DISPATCH_TOOL_DEADLINE_MS = 60000;
|
|
14622
|
+
function requestSignal(signal) {
|
|
14623
|
+
const deadline = AbortSignal.timeout(DISPATCH_TOOL_DEADLINE_MS);
|
|
14624
|
+
return signal === undefined ? deadline : AbortSignal.any([signal, deadline]);
|
|
14625
|
+
}
|
|
14619
14626
|
|
|
14620
14627
|
class DispatchClient {
|
|
14621
14628
|
token;
|
|
@@ -14623,10 +14630,12 @@ class DispatchClient {
|
|
|
14623
14630
|
#baseUrl;
|
|
14624
14631
|
#resolvedIssues = new Map;
|
|
14625
14632
|
#creatingIssues = new Map;
|
|
14626
|
-
|
|
14633
|
+
#signal;
|
|
14634
|
+
constructor(baseUrl, token, fetchImpl = fetch, signal) {
|
|
14627
14635
|
this.token = token;
|
|
14628
14636
|
this.fetchImpl = fetchImpl;
|
|
14629
14637
|
this.#baseUrl = baseUrl.replace(/\/+$/, "");
|
|
14638
|
+
this.#signal = requestSignal(signal);
|
|
14630
14639
|
}
|
|
14631
14640
|
async issue(input) {
|
|
14632
14641
|
return this.#json("POST", ["api", "v1", "issues"], input);
|
|
@@ -14838,6 +14847,7 @@ class DispatchClient {
|
|
|
14838
14847
|
const response = await this.fetchImpl(this.#url(path, query), {
|
|
14839
14848
|
method,
|
|
14840
14849
|
headers,
|
|
14850
|
+
signal: this.#signal,
|
|
14841
14851
|
...body === undefined ? {} : { body: JSON.stringify(body) }
|
|
14842
14852
|
});
|
|
14843
14853
|
return this.#response(response);
|
|
@@ -14846,7 +14856,8 @@ class DispatchClient {
|
|
|
14846
14856
|
const response = await this.fetchImpl(this.#url(path), {
|
|
14847
14857
|
method,
|
|
14848
14858
|
headers: { Accept: "application/json", Authorization: `Bearer ${this.token}` },
|
|
14849
|
-
body
|
|
14859
|
+
body,
|
|
14860
|
+
signal: this.#signal
|
|
14850
14861
|
});
|
|
14851
14862
|
return this.#response(response);
|
|
14852
14863
|
}
|
|
@@ -15311,7 +15322,7 @@ async function executeDispatchTool(input) {
|
|
|
15311
15322
|
const ownerArguments = await resolveOwnerArguments(input.tool, input.args, input.cwd, env, exec);
|
|
15312
15323
|
const args = toolSchema(input.tool).parse(ownerArguments.args);
|
|
15313
15324
|
const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
|
|
15314
|
-
const client = new DispatchClient(configUrl, configToken, input.fetchImpl);
|
|
15325
|
+
const client = new DispatchClient(configUrl, configToken, input.fetchImpl, input.signal);
|
|
15315
15326
|
const owner = ownerArguments.owner?.kind === "issue" ? {
|
|
15316
15327
|
kind: "issue",
|
|
15317
15328
|
issue: await ensureIssue(client, ownerArguments.owner.issue, actor)
|
|
@@ -15894,7 +15905,7 @@ class EnvoyApiError extends Error {
|
|
|
15894
15905
|
function createEnvoyClient(config) {
|
|
15895
15906
|
const baseUrl = normalizeEnvoyUrl(config.baseUrl);
|
|
15896
15907
|
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15897
|
-
const apiToken = process.env
|
|
15908
|
+
const { ENVOY_TOKEN: apiToken } = process.env;
|
|
15898
15909
|
const request = async (path, init) => {
|
|
15899
15910
|
const url = `${baseUrl}${path}`;
|
|
15900
15911
|
for (let attempt = 0;attempt < 2; attempt += 1) {
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -165,7 +165,8 @@ dispatch_edit_ask({
|
|
|
165
165
|
})
|
|
166
166
|
```
|
|
167
167
|
At least one field besides `ask` is required. Use this only while the same decision remains open: it keeps the prior text in the event
|
|
168
|
-
log
|
|
168
|
+
log and invalidates any answer draft against the prior `edited_at` revision, so the human sees the new wording and explicitly reconfirms.
|
|
169
|
+
An answered or resolved ask cannot be edited. If the decision is moot or superseded, retract the old ask and open a new one.
|
|
169
170
|
|
|
170
171
|
An ask stays open until a human answers, unless its question no longer needs that answer. Retract a moot or superseded question, or
|
|
171
172
|
self-resolve one after finding the answer:
|
|
@@ -127,10 +127,12 @@ legion({
|
|
|
127
127
|
|
|
128
128
|
The daemon spawns that sub-architect as its own process with the child's context already
|
|
129
129
|
in its environment; a resume of an existing role continues the same process instead of
|
|
130
|
-
starting a fresh one. Keep the returned session identifiers
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
starting a fresh one. Keep the returned session identifiers; retro and adjustment resume
|
|
131
|
+
those same sessions through `spawn_worker` (a finished worker is retired after
|
|
132
|
+
`worker_idle_retire_seconds` and comes back from its session file). Park while children are
|
|
133
|
+
in flight. On each child closure, re-scope open work, close obsolete work with a reason, and
|
|
134
|
+
release the next wave only when it now makes sense. There is no inter-child dependency
|
|
135
|
+
mechanism to encode.
|
|
134
136
|
|
|
135
137
|
## 3. Children complete
|
|
136
138
|
|
|
@@ -160,18 +162,24 @@ review and the merge-gate sequence.
|
|
|
160
162
|
|
|
161
163
|
## 5. Retro
|
|
162
164
|
|
|
163
|
-
Retro is mandatory for every issue that passed review, before merge.
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
Retro is mandatory for every issue that passed review, before merge. Send the implementer
|
|
166
|
+
back in through the daemon — `spawn_worker` on the implementer carrying the retro task. This
|
|
167
|
+
resumes the same agent whether its pane is still live or the daemon has already retired it
|
|
168
|
+
idle (a finished worker is retired after `worker_idle_retire_seconds`, default 600 s, and
|
|
169
|
+
resumed from its session file on its next assignment). Never `envoy_publish` to a finished
|
|
170
|
+
worker's role topic for this: a retired role has no live holder and the publish is rejected
|
|
171
|
+
with 404.
|
|
166
172
|
|
|
167
173
|
```text
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
174
|
+
legion({
|
|
175
|
+
op: "spawn_worker",
|
|
176
|
+
issue: "LEGION-40",
|
|
177
|
+
role: "implementer",
|
|
178
|
+
task: "Run the legion-retro skill now. Capture durable learnings and post the issue comment; do not create a .legion handoff file."
|
|
171
179
|
})
|
|
172
180
|
```
|
|
173
181
|
|
|
174
|
-
Wait for the
|
|
182
|
+
Wait for the implementer to report its durable retro result. Retro output is
|
|
175
183
|
`docs/solutions/` plus an issue comment; it must not create a `.legion` file or change
|
|
176
184
|
the reviewer-approved head after cleanup.
|
|
177
185
|
|
|
@@ -189,7 +197,8 @@ Preserve this order exactly:
|
|
|
189
197
|
head. The deletion must land before that approval, which is head-pinned. An implementer
|
|
190
198
|
completion always writes the issue's status as `testing`; this one is not a test round,
|
|
191
199
|
so on its `phase-complete` wake call `legion({ op: "set_status", issue, status: "retro" })`
|
|
192
|
-
before
|
|
200
|
+
before `spawn_worker` on the reviewer to approve that head (a finished reviewer may already
|
|
201
|
+
be retired; `spawn_worker` resumes it);
|
|
193
202
|
3. retro completes without dirtying the branch beyond `docs/solutions/`;
|
|
194
203
|
4. the merger verifies the current head is the reviewer-approved head plus only the retro
|
|
195
204
|
commits and publishes `READY #<n> at <sha>` to `notifications.role.pr-queue`; it never
|
|
@@ -61,7 +61,9 @@ Reach any live role on this issue the same way you reach the architect: `envoy_p
|
|
|
61
61
|
`notifications.role.` followed by that role's encoded token. Use it when you need context an
|
|
62
62
|
earlier phase has that its handoff doesn't cover — ask the planner why a constraint was
|
|
63
63
|
scoped that way, ask the implementer what a commit actually did. A role that finished its
|
|
64
|
-
phase
|
|
64
|
+
phase stays idle in its pane for the daemon's idle-retire window and answers; once retired (no
|
|
65
|
+
live holder, a publish is rejected 404), read its committed handoff or ask the architect to
|
|
66
|
+
`spawn_worker` it.
|
|
65
67
|
|
|
66
68
|
## Workspace and handoff precedence
|
|
67
69
|
|
|
@@ -316,7 +318,9 @@ record of this issue's active phase. Do not add pipeline labels, run a controlle
|
|
|
316
318
|
invent a different completion protocol — this is the whole contract.
|
|
317
319
|
|
|
318
320
|
**Stay in this session afterward.** Your process does not exit when your phase completes;
|
|
319
|
-
it goes idle in its pane
|
|
321
|
+
it goes idle in its pane, and after `worker_idle_retire_seconds` (default 600 s) idle with no
|
|
322
|
+
active phase the daemon retires it — your next assignment resumes this same session from its
|
|
323
|
+
session file, so it is still you. Other roles on this issue may reach you through Envoy with
|
|
320
324
|
questions about the work you did — answer them, reading `$LEGION_WORKSPACE` and your own
|
|
321
325
|
committed handoff as needed, without mutating anything (see Workspace and handoff
|
|
322
326
|
precedence above). You will also be the one resumed, with a new prompt in this same
|