@sjawhar/pi-legion-envoy 5.20.0 → 5.22.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/envoy.js CHANGED
@@ -34517,6 +34517,31 @@ function asObject(value) {
34517
34517
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
34518
34518
  }
34519
34519
 
34520
+ // ../envoy-client/src/dispatch-first.ts
34521
+ import { readFileSync as readFileSync3 } from "fs";
34522
+ import { join as join2 } from "path";
34523
+ var DISPATCH_FIRST_MARKER = "<dispatch-first-skill>";
34524
+ function dispatchFirstSkillFile(skillsDirectory) {
34525
+ return join2(skillsDirectory, "dispatch-first", "SKILL.md");
34526
+ }
34527
+ function readDispatchFirstContext(skillFile) {
34528
+ let skill;
34529
+ try {
34530
+ skill = readFileSync3(skillFile, "utf8");
34531
+ } catch (error48) {
34532
+ throw new Error(`the dispatch-first skill ${skillFile} could not be read: ${messageFor(error48)}`);
34533
+ }
34534
+ const body = skill.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
34535
+ return [
34536
+ DISPATCH_FIRST_MARKER,
34537
+ "This session has Dispatch, so the dispatch-first skill is loaded for it. Follow it.",
34538
+ "",
34539
+ body,
34540
+ "</dispatch-first-skill>"
34541
+ ].join(`
34542
+ `);
34543
+ }
34544
+
34520
34545
  // ../envoy-client/src/dispatch-subscribe.ts
34521
34546
  function dispatchFollowNotice(details) {
34522
34547
  if (typeof details !== "object" || details === null)
@@ -35196,6 +35221,29 @@ class AgentStreamPublisher {
35196
35221
  }
35197
35222
  }
35198
35223
 
35224
+ // src/dispatch-first.ts
35225
+ function withDispatchFirst(messages, context) {
35226
+ let index = 0;
35227
+ for (const message of messages) {
35228
+ const summary = typeof message === "object" && message !== null && "role" in message && message.role === "compactionSummary";
35229
+ if (!summary)
35230
+ break;
35231
+ index += 1;
35232
+ }
35233
+ const present = messages[index];
35234
+ if (typeof present === "object" && present !== null && "role" in present && present.role === "user" && "content" in present && Array.isArray(present.content)) {
35235
+ const [first] = present.content;
35236
+ if (typeof first === "object" && first !== null && "text" in first && typeof first.text === "string" && first.text.startsWith(DISPATCH_FIRST_MARKER)) {
35237
+ return;
35238
+ }
35239
+ }
35240
+ return [
35241
+ ...messages.slice(0, index),
35242
+ { role: "user", content: [{ type: "text", text: context }], timestamp: 0 },
35243
+ ...messages.slice(index)
35244
+ ];
35245
+ }
35246
+
35199
35247
  // src/envoy-session.ts
35200
35248
  import path2 from "path";
35201
35249
  var ENVOY_SESSIONS = Symbol.for("legion.pi-envoy.envoy-session");
@@ -35532,6 +35580,7 @@ function resolveSkillsDirectory() {
35532
35580
  return found;
35533
35581
  }
35534
35582
  var SKILLS_DIRECTORY = resolveSkillsDirectory();
35583
+ var DISPATCH_FIRST_CONTEXT = readDispatchFirstContext(dispatchFirstSkillFile(SKILLS_DIRECTORY));
35535
35584
  function envoyExtension(pi) {
35536
35585
  logger.debug("extension instance loaded", { extension: import.meta.url });
35537
35586
  const defaults = envoyDefaultsFromEnvironment(process.env);
@@ -36282,6 +36331,10 @@ function envoyExtension(pi) {
36282
36331
  }
36283
36332
  });
36284
36333
  }
36334
+ pi.on("context", async (event) => {
36335
+ const messages = withDispatchFirst(event.messages, DISPATCH_FIRST_CONTEXT);
36336
+ return messages === undefined ? undefined : { messages };
36337
+ });
36285
36338
  }
36286
36339
  registerEnvoyWhoamiCommand(pi, replyAddress);
36287
36340
  pi.on("before_agent_start", async (event, context) => {
package/dist/legion.js CHANGED
@@ -33495,7 +33495,7 @@ import { logger } from "@oh-my-pi/pi-utils";
33495
33495
  // package.json
33496
33496
  var package_default = {
33497
33497
  name: "@sjawhar/pi-legion-envoy",
33498
- version: "5.20.0",
33498
+ version: "5.22.0",
33499
33499
  type: "module",
33500
33500
  omp: {
33501
33501
  extensions: [
@@ -8,6 +8,7 @@ event intake, process lifecycle, credentials, and role delivery.
8
8
  | --- | --- | --- |
9
9
  | `ce-simplify-code/` | the implementer, once per pull request | the behaviour-preserving simplify pass before the reviewer's final pass (Legion's copy of the MIT-licensed Compound Engineering skill; `LICENSE` beside it) |
10
10
  | `dispatch/` | every role, and any session writing to Dispatch | specs, asks, comments, artifacts, and messages on native Dispatch |
11
+ | `dispatch-first/` | every session with Dispatch configured, injected by each host plugin on every request (Oh My Pi), on every `SessionStart` (startup, resume, clear, compact, fork) and `SubagentStart` (Claude Code), or as an instruction file (OpenCode) | searching Dispatch before acting, extending the existing issue, citing decisions, closing duplicates; kept under 60 lines and 6,000 characters |
11
12
  | `envoy/` | every role | subscriptions, agent-to-agent messages, and topic formats |
12
13
  | `legion-architect/` | root and sub-architects | tree ownership, decomposition, waves, gates, integration, sign-off |
13
14
  | `legion-controller/` | the controller root process | wake routing, keeping the admission slots full from `todo`, the daily report, escalation |
@@ -19,6 +20,7 @@ event intake, process lifecycle, credentials, and role delivery.
19
20
 
20
21
  The owning skill above is where each contract is defined; a role prompt that needs a contract from its own seat points there or restates only its own step. This file lists and does not restate.
21
22
  A Legion prompt (a skill here, a role prompt, or an agent definition in `packages/pi-envoy/agents/`) names a task agent only as `task(agent="<name>")` and a skill it tells the model to load only as `skill://<name>`. Those are the two forms the Go daemon's boot gate and `legion probe-image` resolve through Oh My Pi, refusing by name one it cannot find; a dispatch or a load written any other way goes unchecked. An agent or skill Legion's prompts name is shipped here or in `packages/pi-envoy/agents/`, unless Oh My Pi bundles it.
23
+ A skill file an agent loads stays under 51,200 bytes, Oh My Pi's spill threshold (a longer one arrives with its middle cut out), and a skill's detail lives in `references/` files linked as `skill://<name>/references/<file>.md`, never by relative path, which a read by path cuts at 300 lines. `packages/pi-envoy/src/skills-guard.test.ts` fails on a file at the threshold, on any `SKILL.md` body of 500 lines or more, on a skill whose frontmatter `name` is not its directory's name, on a `dispatch-first` over its budget, on a `skill://<name>/<path>` link that names a missing file or a missing `#heading`, and on a `legion-worker` reference nothing links.
22
24
  The text a worker boots with (its role prompt) lives in `packages/pi-envoy/roles/` and is
23
25
  composed per role in `packages/daemon/src/daemon/processes.ts`; `packages/pi-envoy/roles/roles.test.ts`
24
26
  holds the structural rules for those parts.