@sjawhar/opencode-legion-envoy 1.0.0 → 1.2.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.
package/dist/src/server.js
CHANGED
|
@@ -13611,6 +13611,7 @@ var CommentEventPayloadSchema = object({
|
|
|
13611
13611
|
reply_to: string2().nullish(),
|
|
13612
13612
|
ask_id: string2().nullish(),
|
|
13613
13613
|
ask_question: string2().optional(),
|
|
13614
|
+
ask_state: _enum2(["open", "answered", "resolved"]).optional(),
|
|
13614
13615
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
13615
13616
|
suggestion: object({ replace_with: string2().optional() }).nullish(),
|
|
13616
13617
|
author: object({ kind: string2(), id: string2() }).optional(),
|
|
@@ -14311,7 +14312,7 @@ function normalizeEnvoyUrl(value) {
|
|
|
14311
14312
|
}
|
|
14312
14313
|
|
|
14313
14314
|
// ../envoy-client/src/dispatch-config.ts
|
|
14314
|
-
import { readFileSync } from "fs";
|
|
14315
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
14315
14316
|
import { homedir } from "os";
|
|
14316
14317
|
import * as path from "path";
|
|
14317
14318
|
|
|
@@ -14320,6 +14321,21 @@ function messageFor(error) {
|
|
|
14320
14321
|
return error instanceof Error ? error.message : String(error);
|
|
14321
14322
|
}
|
|
14322
14323
|
|
|
14324
|
+
// ../envoy-client/src/secret-file.ts
|
|
14325
|
+
import { readFileSync } from "fs";
|
|
14326
|
+
function readSecretFile(variable, filePath) {
|
|
14327
|
+
let contents;
|
|
14328
|
+
try {
|
|
14329
|
+
contents = readFileSync(filePath, "utf8");
|
|
14330
|
+
} catch (error) {
|
|
14331
|
+
throw new Error(`${variable} names ${filePath}, which could not be read: ${messageFor(error)}`);
|
|
14332
|
+
}
|
|
14333
|
+
const value = contents.trim();
|
|
14334
|
+
if (value.length === 0)
|
|
14335
|
+
throw new Error(`${variable} names ${filePath}, which is empty`);
|
|
14336
|
+
return value;
|
|
14337
|
+
}
|
|
14338
|
+
|
|
14323
14339
|
// ../envoy-client/src/dispatch-config.ts
|
|
14324
14340
|
var DEFAULT_SERVER_URL = "http://localhost:8766";
|
|
14325
14341
|
function normalizeDispatchUrl(url2) {
|
|
@@ -14356,7 +14372,7 @@ function describeSchemaIssue(filePath, error) {
|
|
|
14356
14372
|
function readEnvoyFile(filePath) {
|
|
14357
14373
|
let raw;
|
|
14358
14374
|
try {
|
|
14359
|
-
raw =
|
|
14375
|
+
raw = readFileSync2(filePath, "utf-8");
|
|
14360
14376
|
} catch {
|
|
14361
14377
|
return { kind: "absent" };
|
|
14362
14378
|
}
|
|
@@ -14389,8 +14405,22 @@ function resolveDispatchConfig(env, options = {}) {
|
|
|
14389
14405
|
};
|
|
14390
14406
|
const rawUrl = explicitUrl !== undefined ? { value: explicitUrl, source: "DISPATCH_URL" } : merged.enabled === true ? { value: merged.serverUrl ?? DEFAULT_SERVER_URL, source: "dispatch.serverUrl" } : null;
|
|
14391
14407
|
const url2 = rawUrl ? parsedDispatchUrl(rawUrl.value, rawUrl.source) : { url: null, error: null };
|
|
14392
|
-
|
|
14393
|
-
|
|
14408
|
+
let token;
|
|
14409
|
+
let tokenSource;
|
|
14410
|
+
if (env.DISPATCH_TOKEN_FILE !== undefined) {
|
|
14411
|
+
tokenSource = "DISPATCH_TOKEN_FILE";
|
|
14412
|
+
try {
|
|
14413
|
+
token = readSecretFile(tokenSource, env.DISPATCH_TOKEN_FILE);
|
|
14414
|
+
} catch (error) {
|
|
14415
|
+
return { enabled: false, url: url2.url, token: null, error: messageFor(error) };
|
|
14416
|
+
}
|
|
14417
|
+
} else if (env.DISPATCH_TOKEN !== undefined) {
|
|
14418
|
+
tokenSource = "DISPATCH_TOKEN";
|
|
14419
|
+
token = env.DISPATCH_TOKEN;
|
|
14420
|
+
} else {
|
|
14421
|
+
tokenSource = "dispatch.token";
|
|
14422
|
+
token = merged.token ?? null;
|
|
14423
|
+
}
|
|
14394
14424
|
if (url2.error !== null)
|
|
14395
14425
|
return { enabled: false, url: null, token, error: url2.error };
|
|
14396
14426
|
if (url2.url === null)
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -118,6 +118,11 @@ in its Conversation card and reply thread. Resolution is not an answer: it never
|
|
|
118
118
|
resolved. A human may reply to an open or answered ask; so may you, e.g. after finding the answer — use `reply_to_ask` on
|
|
119
119
|
`dispatch_comment` (mutually exclusive with `reply_to`).
|
|
120
120
|
|
|
121
|
+
A human reply while your ask is still open (the delivered `comment.created` carries `ask_state: open`) is a request for
|
|
122
|
+
clarification, not an answer: the human did not understand the question or needs more before choosing. The ask now waits on you
|
|
123
|
+
in their Inbox. Answer in the same thread with `dispatch_comment({ reply_to_ask })`, or reword the question itself with
|
|
124
|
+
`dispatch_edit_ask` when the wording was the problem; either puts the ask back in front of them. Do not open a second ask.
|
|
125
|
+
|
|
121
126
|
## The Spec
|
|
122
127
|
|
|
123
128
|
The spec holds requirements, design, acceptance, decisions, and rejected alternatives, structured per [Writing a spec](#writing-a-spec).
|
|
@@ -218,6 +218,7 @@ corresponding lifecycle procedure.
|
|
|
218
218
|
| `pr-ready` | Verify the live PR head, green status, and review state. Continue the review/retro/merger order only for that current head. |
|
|
219
219
|
| `pr-review` | Payload `{type:"pr-review", state, author, body}`. Delivered to whichever role is currently active for the issue, falling back to you when no worker phase is active. Follows the same verdict rule as a reviewer's `phase-complete`: `state: "changes_requested"` sends the implementer back in with the review findings, then tester, then reviewer — never the reviewer again and never retro; `state: "approved"` proceeds toward retro (step 5) once the step 6 integration/merge-gate conditions are met. |
|
|
220
220
|
| `pr-blocked` | Read the failed CI evidence and recovery attempts. Assign a focused implementer or corrective child, then return it through testing and review; do not treat the blocked PR as final. |
|
|
221
|
+
| `pr-merged` | Payload `{type:"pr-merged", pr, mergeCommitSha}`. The merge queue landed the PR. This is your cue for step 7: post the sign-off comment naming that merge commit and set the issue `done`. Nothing else follows a merge. |
|
|
221
222
|
| `pr-closed-unmerged` | Decide from current scope whether to reopen the work, send a fresh implementer, or cancel it with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
|
|
222
223
|
| `issue-comment` | Interpret the comment in the issue's design context. Answer it, adjust the plan, or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
|
|
223
224
|
| `catchup-overseer` | Verify its gates, child counts, and PR verdicts against current artifacts, then resume the applicable numbered lifecycle step. It is a current-state snapshot, not a raw-event replay. For each entry in its `phaseCompletions` (`{issue, role, summary, at}`, phases that completed while you were not live), handle it exactly as a `phase-complete` wake. |
|
|
@@ -18,8 +18,9 @@ until that PR merges, this skill's contract is not yet runnable on `main`.
|
|
|
18
18
|
The Legion extension claims `legion-<project>-controller` and registers controller readiness
|
|
19
19
|
with the daemon during session startup. Do not handle a wake unless that startup succeeded.
|
|
20
20
|
|
|
21
|
-
For an interactive takeover, start OMP with `LEGION_CONTROLLER_SECRET`
|
|
22
|
-
`LEGION_DAEMON_URL` in its
|
|
21
|
+
For an interactive takeover, start OMP with `LEGION_CONTROLLER_SECRET` (or
|
|
22
|
+
`LEGION_CONTROLLER_SECRET_FILE`, a path to a file holding it) and `LEGION_DAEMON_URL` in its
|
|
23
|
+
environment, then run:
|
|
23
24
|
|
|
24
25
|
```text
|
|
25
26
|
/legion-claim-controller
|
|
@@ -19,8 +19,9 @@ until that PR merges, this skill's contract is not yet runnable on `main`.
|
|
|
19
19
|
|
|
20
20
|
The daemon spawns you as a separate `omp --mode rpc` process (behind `legion worker-shim`,
|
|
21
21
|
in a tmux pane) with `LEGION_TREE`, `LEGION_ISSUE`, `LEGION_ROLE`, `LEGION_GENERATION`,
|
|
22
|
-
`
|
|
23
|
-
|
|
22
|
+
`LEGION_BOOT_TOKEN_FILE` (a 0600 file under `$LEGION_STATE_DIR/secrets` holding your boot token;
|
|
23
|
+
the extension reads it for you), `LEGION_DAEMON_URL`, `LEGION_STATE_DIR`, and `LEGION_WORKSPACE`
|
|
24
|
+
in your environment (`LEGION_PROJECT` is also supplied, but nothing reads it). The extension
|
|
24
25
|
completes the boot handshake for you at session start — it registers with the daemon, claims
|
|
25
26
|
your role, and signals readiness. You never call `envoy_role_set` yourself.
|
|
26
27
|
|