@sjawhar/pi-legion-envoy 5.12.0 → 5.13.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/envoy.js CHANGED
@@ -32850,12 +32850,11 @@ async function architectureGuidance(client, project, components) {
32850
32850
  if (components.mode === "none" || components.ids.length > 0)
32851
32851
  return;
32852
32852
  try {
32853
- await client.getArchitectureSource(project);
32854
- return `Project ${project} has an architecture model, but this issue is not linked to any of its current components. ${architectureComponentsAction}`;
32855
- } catch (error48) {
32856
- if (error48 instanceof DispatchServiceError && error48.status === 404 && error48.code === "SOURCE_NOT_FOUND") {
32853
+ if (await client.getArchitectureSource(project) === null) {
32857
32854
  return;
32858
32855
  }
32856
+ return `Project ${project} has an architecture model, but this issue is not linked to any of its current components. ${architectureComponentsAction}`;
32857
+ } catch (error48) {
32859
32858
  return `Could not check whether project ${project} has an architecture model: ${messageFor(error48)}. ${architectureComponentsAction}`;
32860
32859
  }
32861
32860
  }
@@ -35188,6 +35187,27 @@ function onEnvoyRoleRegained(listener) {
35188
35187
  legionRoleClaimBridge().regained = listener;
35189
35188
  }
35190
35189
 
35190
+ // src/side-turn.ts
35191
+ function btwPrompt(question) {
35192
+ return [
35193
+ "<btw>",
35194
+ "Ephemeral side question for current interactive session.",
35195
+ "Answer briefly, directly; use conversation context already provided.",
35196
+ "NEVER use tools.",
35197
+ "NEVER ask follow-up questions.",
35198
+ "Question:",
35199
+ question,
35200
+ "</btw>"
35201
+ ].join(`
35202
+ `);
35203
+ }
35204
+ function sideTurn(pi, context) {
35205
+ const runEphemeralTurn = context?.runEphemeralTurn;
35206
+ if (runEphemeralTurn === undefined)
35207
+ return pi.askEphemeral;
35208
+ return ({ prompt, signal }) => runEphemeralTurn({ promptText: btwPrompt(prompt), signal });
35209
+ }
35210
+
35191
35211
  // src/subagent-session.ts
35192
35212
  import fs from "fs";
35193
35213
  import path3 from "path";
@@ -35433,7 +35453,6 @@ function envoyExtension(pi) {
35433
35453
  availabilityWarningSessionIDs.add(sessionID2);
35434
35454
  context.ui.notify(`envoy: Dispatch open-ask check unavailable (${messageFor(error48)}); the stop-time ask reminder is off until it recovers`, "warning");
35435
35455
  };
35436
- const askEphemeral = pi.askEphemeral;
35437
35456
  const overriddenTimeout = Number(process.env.ENVOY_SELF_CHECK_TIMEOUT_MS);
35438
35457
  const selfCheckTimeoutMs = Number.isFinite(overriddenTimeout) && overriddenTimeout > 0 ? overriddenTimeout : ASK_SELF_CHECK_TIMEOUT_MS;
35439
35458
  const selfCheckWarningSessionIDs = new Set;
@@ -35542,13 +35561,18 @@ function envoyExtension(pi) {
35542
35561
  } else if (rendered.malformedDelivery === true) {
35543
35562
  console.warn("[envoy] dropping malformed Dispatch targeted delivery without a reply address");
35544
35563
  } else if (rendered.delivery?.mode === "btw") {
35545
- if (pi.askEphemeral === undefined) {
35564
+ const answer = sideTurn(pi, activeSessionContext);
35565
+ if (shuttingDown) {
35566
+ await postDispatchReply(rendered.delivery, {
35567
+ error: "This OMP session is shutting down"
35568
+ });
35569
+ } else if (answer === undefined) {
35546
35570
  await postDispatchReply(rendered.delivery, {
35547
35571
  error: "This OMP host does not support BTW delivery"
35548
35572
  });
35549
35573
  } else {
35550
35574
  try {
35551
- const reply2 = await pi.askEphemeral({ prompt: rendered.delivery.body });
35575
+ const reply2 = await answer({ prompt: rendered.delivery.body });
35552
35576
  await postDispatchReply(rendered.delivery, { body: reply2.replyText });
35553
35577
  } catch (error48) {
35554
35578
  await postDispatchReply(rendered.delivery, { error: messageFor(error48) });
@@ -35733,7 +35757,7 @@ function envoyExtension(pi) {
35733
35757
  ],
35734
35758
  port: 0,
35735
35759
  title: activeSessionContext?.sessionManager.getSessionName?.() ?? "",
35736
- capabilities: typeof pi.askEphemeral === "function" ? DELIVERY_CAPABILITIES : CAPABILITIES_WITHOUT_BTW,
35760
+ capabilities: sideTurn(pi, activeSessionContext) === undefined ? CAPABILITIES_WITHOUT_BTW : DELIVERY_CAPABILITIES,
35737
35761
  driving: false,
35738
35762
  selfSubscribed: true
35739
35763
  });
@@ -36127,7 +36151,7 @@ function envoyExtension(pi) {
36127
36151
  registerEnvoyWhoamiCommand(pi, replyAddress);
36128
36152
  pi.on("before_agent_start", async (event, context) => {
36129
36153
  const id = context.sessionManager.getSessionId();
36130
- if (id === "" || event.prompt.trim() === "" || !context.hasUI || askEphemeral === undefined || legionManaged(id)) {
36154
+ if (id === "" || event.prompt.trim() === "" || !context.hasUI || sideTurn(pi, context) === undefined || legionManaged(id)) {
36131
36155
  return;
36132
36156
  }
36133
36157
  if (await isSubagent(context))
@@ -36158,39 +36182,40 @@ function envoyExtension(pi) {
36158
36182
  pi.on("agent_end", async (event, context) => {
36159
36183
  const id = context.sessionManager.getSessionId();
36160
36184
  const lastReply = event.messages?.findLast((message) => message.role === "assistant");
36161
- if (event.willContinue === true || lastReply?.stopReason !== "stop" || !context.hasUI || id === "" || !checkOwed(id)) {
36185
+ const ask = sideTurn(pi, context);
36186
+ if (event.willContinue === true || lastReply?.stopReason !== "stop" || !context.hasUI || id === "" || ask === undefined || !checkOwed(id)) {
36162
36187
  return;
36163
36188
  }
36189
+ const settle = { context, ask, seenRun: runSeq, generation: awarenessGeneration };
36164
36190
  if (askCheckInFlight) {
36165
- settledDuringCheck = { context, seenRun: runSeq };
36191
+ settledDuringCheck = settle;
36166
36192
  return;
36167
36193
  }
36168
36194
  askCheckInFlight = true;
36195
+ context.setTimeout(() => checkFromSettle(id, settle), 0);
36196
+ });
36197
+ async function checkFromSettle(id, first) {
36169
36198
  try {
36170
- let settle = { context, seenRun: runSeq };
36199
+ let settle = first;
36171
36200
  while (settle !== undefined) {
36172
36201
  await checkAtSettle(id, settle);
36173
36202
  settle = takeSettledDuringCheck();
36174
- if (settle?.context.sessionManager.getSessionId() !== id || settle.seenRun !== runSeq || !checkOwed(id)) {
36175
- settle = undefined;
36176
- }
36177
36203
  }
36178
36204
  } finally {
36179
36205
  askCheckInFlight = false;
36180
36206
  settledDuringCheck = undefined;
36181
36207
  }
36182
- });
36208
+ }
36183
36209
  function checkOwed(id) {
36184
- return !(shuttingDown || legionManaged(id) || askAwareness.session_id !== id || askAwareness.period === 0 || !askAwareness.check_due || askAwareness.checks >= ASK_CHECKS_PER_PERIOD || askAwareness.baseline_as_of === null || askEphemeral === undefined);
36210
+ return !(shuttingDown || legionManaged(id) || askAwareness.session_id !== id || askAwareness.period === 0 || !askAwareness.check_due || askAwareness.checks >= ASK_CHECKS_PER_PERIOD || askAwareness.baseline_as_of === null);
36185
36211
  }
36186
36212
  async function checkAtSettle(id, settle) {
36187
- const { context, seenRun } = settle;
36213
+ const { context, ask, seenRun, generation } = settle;
36188
36214
  const period = askAwareness.period;
36189
- const generation = awarenessGeneration;
36190
36215
  const baseline = askAwareness.baseline_as_of;
36191
- if (baseline === null || askEphemeral === undefined)
36192
- return;
36193
36216
  const stale = () => shuttingDown || generation !== awarenessGeneration || context.sessionManager.getSessionId() !== id || legionManaged(id) || askAwareness.session_id !== id || askAwareness.period !== period || !askAwareness.check_due;
36217
+ if (baseline === null || !checkOwed(id) || stale() || runSeq !== seenRun)
36218
+ return;
36194
36219
  let open;
36195
36220
  try {
36196
36221
  open = await queryOpenAsks(id, baseline);
@@ -36207,7 +36232,7 @@ function envoyExtension(pi) {
36207
36232
  askCheckAbort = abort;
36208
36233
  let answered;
36209
36234
  try {
36210
- answered = askEphemeral({
36235
+ answered = ask({
36211
36236
  prompt: ASK_SELF_CHECK_PROMPT(open.snapshot.asks),
36212
36237
  signal: abort.signal
36213
36238
  }).then((reply) => reply.replyText, (error48) => {
package/dist/legion.js CHANGED
@@ -16192,7 +16192,7 @@ import { logger } from "@oh-my-pi/pi-utils";
16192
16192
  // package.json
16193
16193
  var package_default = {
16194
16194
  name: "@sjawhar/pi-legion-envoy",
16195
- version: "5.12.0",
16195
+ version: "5.13.1",
16196
16196
  type: "module",
16197
16197
  omp: {
16198
16198
  extensions: [
@@ -277,6 +277,10 @@ start with the issue key; standalone project-document hit lines start with
277
277
 
278
278
  `dispatch_issue` refuses a title that near-duplicates an issue in the same project and returns the candidates (`POSSIBLE_DUPLICATE`).
279
279
  Read them; reference the existing issue, or repeat the call with `force: true` when it is genuinely new work.
280
+ The check compares title words only (shared stemmed terms), never meaning: "four tests that fail a
281
+ merge" pairs with "four CI gates that cannot fail a merge". So when you force past a candidate, give
282
+ the new issue a title that names what differs where you can, and open its spec's Summary with the
283
+ distinction from the named issue, citing it (`dispatch://KEY`), for whoever reads the next pairing.
280
284
 
281
285
  ## Reading a project's backlog
282
286
 
@@ -140,7 +140,7 @@ Each session row from `envoy_sessions` carries `capabilities`, the targeted-deli
140
140
  session's host honours: `aside` (a message queued beside the model's work), `btw` (an ephemeral
141
141
  question the host answers without disturbing the current turn), and `steer` (an interjection at
142
142
  the next tool boundary). An OMP session advertises `aside`, `btw`, and `steer` (`aside` and
143
- `steer` on a host without `askEphemeral`); a Claude Code session advertises `aside` only, because
143
+ `steer` on a host that cannot run a side turn); a Claude Code session advertises `aside` only, because
144
144
  its channel notifications queue for the next turn. Target a session only with a mode it
145
145
  advertises.
146
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "5.12.0",
3
+ "version": "5.13.1",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [