@sjawhar/opencode-legion-envoy 3.17.1 → 3.19.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
|
@@ -13701,6 +13701,7 @@ var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
|
|
|
13701
13701
|
body: exports_external.string(),
|
|
13702
13702
|
target: exports_external.string(),
|
|
13703
13703
|
in_reply_to: exports_external.string().nullable(),
|
|
13704
|
+
broadcast_id: exports_external.string().nullish(),
|
|
13704
13705
|
deliveries: exports_external.array(exports_external.unknown()),
|
|
13705
13706
|
created_at: exports_external.string()
|
|
13706
13707
|
});
|
|
@@ -14363,6 +14364,8 @@ var EnvelopeSchema = exports_external.object({
|
|
|
14363
14364
|
// ../contracts/src/handoff-schema.ts
|
|
14364
14365
|
var HANDOFF_SCHEMA_VERSION = 1;
|
|
14365
14366
|
var HANDOFF_PHASES = ["architect", "plan", "implement", "test", "review"];
|
|
14367
|
+
var PLAN_REVIEW_MAX_ROUNDS = 3;
|
|
14368
|
+
var PLAN_REVIEW_VERDICTS = ["approved", "rejected", "failed"];
|
|
14366
14369
|
var isoTimestamp = exports_external.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
14367
14370
|
var handoffPhase = exports_external.enum(HANDOFF_PHASES);
|
|
14368
14371
|
var nonEmpty = exports_external.string().trim().min(1);
|
|
@@ -14399,6 +14402,16 @@ var requiredSkillsSchema = exports_external.object({
|
|
|
14399
14402
|
test: exports_external.array(exports_external.string()).optional(),
|
|
14400
14403
|
review: exports_external.array(exports_external.string()).optional()
|
|
14401
14404
|
}).passthrough().optional();
|
|
14405
|
+
var gapAnalysisSchema = exports_external.object({
|
|
14406
|
+
findings: exports_external.array(exports_external.object({ finding: exports_external.string(), answer: exports_external.string() }).passthrough()).optional(),
|
|
14407
|
+
error: exports_external.string().optional()
|
|
14408
|
+
}).passthrough().optional();
|
|
14409
|
+
var planReviewSchema = exports_external.object({
|
|
14410
|
+
verdict: exports_external.enum(PLAN_REVIEW_VERDICTS),
|
|
14411
|
+
rounds: exports_external.number(),
|
|
14412
|
+
remainingIssues: exports_external.array(exports_external.object({ issue: exports_external.string(), evidence: exports_external.string() }).passthrough()).optional(),
|
|
14413
|
+
error: exports_external.string().optional()
|
|
14414
|
+
}).passthrough().optional();
|
|
14402
14415
|
var planSchema = baseHandoffSchema.extend({
|
|
14403
14416
|
phase: exports_external.literal("plan"),
|
|
14404
14417
|
taskCount: exports_external.number().optional(),
|
|
@@ -14406,7 +14419,9 @@ var planSchema = baseHandoffSchema.extend({
|
|
|
14406
14419
|
routingHints: routingHintsSchema,
|
|
14407
14420
|
concerns: exports_external.array(exports_external.string()).optional(),
|
|
14408
14421
|
workflowRecommendation: exports_external.string().optional(),
|
|
14409
|
-
requiredSkills: requiredSkillsSchema
|
|
14422
|
+
requiredSkills: requiredSkillsSchema,
|
|
14423
|
+
gapAnalysis: gapAnalysisSchema,
|
|
14424
|
+
planReview: planReviewSchema
|
|
14410
14425
|
});
|
|
14411
14426
|
var implementSchema = baseHandoffSchema.extend({
|
|
14412
14427
|
phase: exports_external.literal("implement"),
|
|
@@ -14446,13 +14461,60 @@ var reviewSchema = baseHandoffSchema.extend({
|
|
|
14446
14461
|
verdict: exports_external.enum(["approved", "changes_requested"]).optional(),
|
|
14447
14462
|
keyFindings: exports_external.array(exports_external.object({ severity: exports_external.string(), file: exports_external.string(), description: exports_external.string() }).passthrough()).optional()
|
|
14448
14463
|
});
|
|
14449
|
-
var nonEmptySkillList = exports_external.array(
|
|
14464
|
+
var nonEmptySkillList = exports_external.array(nonEmpty).min(1);
|
|
14465
|
+
var recorded = (shape, whatToRecord) => exports_external.object(shape, {
|
|
14466
|
+
error: (issue2) => issue2.input === undefined ? `missing \u2014 record ${whatToRecord}` : undefined
|
|
14467
|
+
}).passthrough();
|
|
14468
|
+
var gapAnalysisWriteSchema = recorded({
|
|
14469
|
+
findings: exports_external.array(exports_external.object({ finding: nonEmpty, answer: nonEmpty }).passthrough()).optional(),
|
|
14470
|
+
error: nonEmpty.optional()
|
|
14471
|
+
}, "the gap analyst's `findings`, each with how the plan answers it (`[]` when it found none), or its failed call's `error`").refine((analysis) => analysis.findings === undefined !== (analysis.error === undefined), {
|
|
14472
|
+
message: "record exactly one of `findings` or the failed call's `error`"
|
|
14473
|
+
});
|
|
14474
|
+
var planReviewWriteSchema = recorded({
|
|
14475
|
+
verdict: exports_external.enum(PLAN_REVIEW_VERDICTS),
|
|
14476
|
+
rounds: exports_external.number().int().min(1).max(PLAN_REVIEW_MAX_ROUNDS),
|
|
14477
|
+
remainingIssues: exports_external.array(exports_external.object({ issue: nonEmpty, evidence: nonEmpty }).passthrough()).optional(),
|
|
14478
|
+
error: nonEmpty.optional()
|
|
14479
|
+
}, "the plan review's `verdict` and `rounds`, with `remainingIssues` when it was rejected or `error` when a review's call failed").superRefine((review, ctx) => {
|
|
14480
|
+
const remaining = review.remainingIssues?.length ?? 0;
|
|
14481
|
+
if (review.verdict === "rejected" && remaining === 0) {
|
|
14482
|
+
ctx.addIssue({
|
|
14483
|
+
code: "custom",
|
|
14484
|
+
path: ["remainingIssues"],
|
|
14485
|
+
message: "a rejected review records the blocking issues its last round named"
|
|
14486
|
+
});
|
|
14487
|
+
}
|
|
14488
|
+
if (review.verdict === "rejected" && review.rounds < PLAN_REVIEW_MAX_ROUNDS) {
|
|
14489
|
+
ctx.addIssue({
|
|
14490
|
+
code: "custom",
|
|
14491
|
+
path: ["rounds"],
|
|
14492
|
+
message: `a review still rejecting after ${review.rounds} of ${PLAN_REVIEW_MAX_ROUNDS} rounds is revised and reviewed again, not recorded`
|
|
14493
|
+
});
|
|
14494
|
+
}
|
|
14495
|
+
if (review.verdict === "approved" && remaining > 0) {
|
|
14496
|
+
ctx.addIssue({
|
|
14497
|
+
code: "custom",
|
|
14498
|
+
path: ["remainingIssues"],
|
|
14499
|
+
message: "an approved review leaves no blocking issue standing"
|
|
14500
|
+
});
|
|
14501
|
+
}
|
|
14502
|
+
if (review.verdict === "failed" !== (review.error !== undefined)) {
|
|
14503
|
+
ctx.addIssue({
|
|
14504
|
+
code: "custom",
|
|
14505
|
+
path: ["error"],
|
|
14506
|
+
message: "a failed review records its call's error, and only a failed review does"
|
|
14507
|
+
});
|
|
14508
|
+
}
|
|
14509
|
+
});
|
|
14450
14510
|
var planWriteSchema = planSchema.extend({
|
|
14451
14511
|
requiredSkills: exports_external.object({
|
|
14452
14512
|
implement: nonEmptySkillList,
|
|
14453
14513
|
test: nonEmptySkillList,
|
|
14454
14514
|
review: nonEmptySkillList
|
|
14455
|
-
}).passthrough()
|
|
14515
|
+
}).passthrough(),
|
|
14516
|
+
gapAnalysis: gapAnalysisWriteSchema,
|
|
14517
|
+
planReview: planReviewWriteSchema
|
|
14456
14518
|
});
|
|
14457
14519
|
var phaseHandoffSchema = exports_external.discriminatedUnion("phase", [
|
|
14458
14520
|
architectSchema,
|
|
@@ -15274,6 +15336,9 @@ class DispatchClient {
|
|
|
15274
15336
|
async getMessageThread(id, session) {
|
|
15275
15337
|
return this.#json("GET", ["api", "v1", "messages", id], undefined, { session });
|
|
15276
15338
|
}
|
|
15339
|
+
async acceptMessageDelivery(id, attempt, input) {
|
|
15340
|
+
return this.#json("POST", ["api", "v1", "messages", id, "deliveries", String(attempt), "accept"], input);
|
|
15341
|
+
}
|
|
15277
15342
|
async artifact(issue2, input) {
|
|
15278
15343
|
const artifactPath = ["api", "v1", "issues", await this.#resolveIssue(issue2), "artifacts"];
|
|
15279
15344
|
if ("content" in input)
|
package/package.json
CHANGED
|
@@ -6,7 +6,8 @@ human messages you directly from the Agents page.
|
|
|
6
6
|
## Targeted agent messages
|
|
7
7
|
|
|
8
8
|
A human — or any bearer caller over HTTP, such as a test rig — can target the issue message at a
|
|
9
|
-
live Envoy session or role as **BTW**, **Aside**, or **Steer
|
|
9
|
+
live Envoy session or role as **BTW**, **Aside**, or **Steer** (the dashboard calls a steer
|
|
10
|
+
**Send**). The incoming Dispatch frame names
|
|
10
11
|
the issue and includes a `reply_with` hint (`{ tool, args }`, ready to issue on any host); reply on the same open issue with the existing
|
|
11
12
|
tool, never a new targeted send:
|
|
12
13
|
|
|
@@ -32,9 +33,17 @@ target only a session that advertises the mode you want. Sending to a session wi
|
|
|
32
33
|
|
|
33
34
|
## Answering a direct message
|
|
34
35
|
|
|
35
|
-
A human can also message you directly from the **Agents** page, with no issue at all.
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
A human can also message you directly from the **Agents** page, with no issue at all. On Oh My
|
|
37
|
+
Pi, a person's **Send** or **Aside** arrives as their own user message, exactly as if they had
|
|
38
|
+
typed it at your terminal: your Envoy plugin takes it only once Dispatch accepts it as a person's
|
|
39
|
+
own fresh message to you, and injects the text Dispatch stored. Answer it in the conversation as
|
|
40
|
+
you would anything typed, with no `dispatch_message`; the Agents page shows your conversation
|
|
41
|
+
live, so they read your answer there.
|
|
42
|
+
|
|
43
|
+
Everything else still arrives as a Dispatch frame: a **BTW**, a broadcast, a direct message on a
|
|
44
|
+
host that takes no user turn from its plugin (Claude Code), and one Dispatch did not accept.
|
|
45
|
+
That frame names no issue and its `reply_with` hint carries none either; answer it with the
|
|
46
|
+
message's bare id in `in_reply_to`, alone:
|
|
38
47
|
|
|
39
48
|
```ts
|
|
40
49
|
dispatch_message({
|