@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.
@@ -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(exports_external.string().trim().min(1)).min(1);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "3.17.1",
3
+ "version": "3.19.0",
4
4
  "type": "module",
5
5
  "main": "dist/src/server.js",
6
6
  "exports": {
@@ -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**. The incoming Dispatch frame names
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. That frame
36
- names no issue and its `reply_with` hint carries none either; answer it with the message's bare
37
- id in `in_reply_to`, alone:
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({