@sjawhar/pi-legion-envoy 0.30.0 → 0.32.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/legion.js CHANGED
@@ -33279,6 +33279,36 @@ var exitProcess = (code) => process.exit(code);
33279
33279
  function setLegionBootstrapExitForTests(hook) {
33280
33280
  exitProcess = hook;
33281
33281
  }
33282
+ var READY_RETRY_ATTEMPTS = 3;
33283
+ var READY_RETRY_DELAY_MS = 1000;
33284
+ function isNetworkOrTimeoutError(error) {
33285
+ if (!(error instanceof Error))
33286
+ return false;
33287
+ if (error.name === "NetworkError" || error.name === "TimeoutError")
33288
+ return true;
33289
+ const code = "code" in error && typeof error.code === "string" ? error.code : undefined;
33290
+ return code === "ConnectionRefused" || code === "ConnectionTimeout" || code === "EAI_AGAIN" || code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EHOSTUNREACH" || code === "ENETUNREACH" || code === "ENOTFOUND" || code === "ETIMEDOUT" || error instanceof TypeError && (error.message === "Failed to fetch" || error.message === "fetch failed");
33291
+ }
33292
+ var callReadyWithRetry = async (label, call) => {
33293
+ for (let attempt = 1;attempt <= READY_RETRY_ATTEMPTS; attempt++) {
33294
+ try {
33295
+ await call();
33296
+ return;
33297
+ } catch (error) {
33298
+ const retryable = error instanceof LegionDaemonApiError ? error.status >= 500 && error.status < 600 : isNetworkOrTimeoutError(error);
33299
+ if (!retryable)
33300
+ throw error;
33301
+ if (attempt === READY_RETRY_ATTEMPTS) {
33302
+ console.error(`[legion] ${label} failed after ${attempt} attempts: ${messageFor(error)}`);
33303
+ throw error;
33304
+ }
33305
+ console.error(`[legion] ${label} failed (attempt ${attempt}/${READY_RETRY_ATTEMPTS}), retrying: ${messageFor(error)}`);
33306
+ const retryDelay = Promise.withResolvers();
33307
+ setTimeout(retryDelay.resolve, READY_RETRY_DELAY_MS);
33308
+ await retryDelay.promise;
33309
+ }
33310
+ }
33311
+ };
33282
33312
  async function persistedTranscript(context) {
33283
33313
  await context.sessionManager.ensureOnDisk();
33284
33314
  const sessionFile = context.sessionManager.getSessionFile();
@@ -33453,15 +33483,19 @@ function legionExtension(pi) {
33453
33483
  };
33454
33484
  await claimEnvoyRole(sessionID, roleToken, context);
33455
33485
  await startControlSubscription(sessionID);
33456
- await roleDaemon().processReady({
33486
+ await callReadyWithRetry("root process/ready", () => roleDaemon().processReady({
33457
33487
  tree,
33458
33488
  sessionId: sessionID,
33459
33489
  secret: started.secret
33460
- });
33490
+ }));
33461
33491
  registerArchitectTools();
33462
33492
  await activateLegionTool();
33463
33493
  } catch (error) {
33464
- console.error(`[legion] root bootstrap failed after process/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33494
+ if (error instanceof LegionDaemonApiError && (error.status === 401 || error.status === 403)) {
33495
+ console.error(`[legion] root bootstrap authorization failed after process/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33496
+ } else {
33497
+ console.error(`[legion] root bootstrap failed after process/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33498
+ }
33465
33499
  exitProcess(1);
33466
33500
  }
33467
33501
  })();
@@ -33518,16 +33552,20 @@ function legionExtension(pi) {
33518
33552
  registerArchitectTools();
33519
33553
  await activateLegionTool();
33520
33554
  }
33521
- await roleDaemon().workerReady({
33555
+ await callReadyWithRetry("worker/ready", () => roleDaemon().workerReady({
33522
33556
  tree,
33523
33557
  issue,
33524
33558
  role,
33525
33559
  sessionId: sessionID,
33526
33560
  secret: started.secret,
33527
33561
  generation: generation(process.env)
33528
- });
33562
+ }));
33529
33563
  } catch (error) {
33530
- console.error(`[legion] worker bootstrap failed after worker/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33564
+ if (error instanceof LegionDaemonApiError && (error.status === 401 || error.status === 403)) {
33565
+ console.error(`[legion] worker bootstrap authorization failed after worker/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33566
+ } else {
33567
+ console.error(`[legion] worker bootstrap failed after worker/started registered a role; exiting so the daemon respawns a fresh attempt: ${messageFor(error)}`);
33568
+ }
33531
33569
  exitProcess(1);
33532
33570
  }
33533
33571
  })();
@@ -76,8 +76,10 @@ dispatch_ask({
76
76
  It returns `details` `{ issue, topic, ask }`. Options are buttons: never enumerate choices in
77
77
  prose. Put the recommendation in `question`, and put each selectable choice in `options`.
78
78
  Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occurrence` is
79
- zero-based and selects a repeated quote. An anchor whose quote disappears becomes orphaned but
80
- remains readable against its original document version.
79
+ zero-based and selects a repeated quote. The server writes the resulting mark. The HTTP API also
80
+ accepts `{ artifact, mark_id }` from a browser that has already written its mark; Dispatch tools
81
+ use the quote form. An anchor whose quote disappears becomes orphaned but remains readable against
82
+ its original document version.
81
83
 
82
84
  An ask stays open until a human answers, unless its question no longer needs that answer. Retract a
83
85
  moot or superseded question, or self-resolve one after finding the answer:
@@ -141,7 +143,8 @@ dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to?, reply
141
143
  ```
142
144
 
143
145
  It returns `details` `{ issue, topic, comment }`. `quote` requires `artifact`; omit both for a
144
- floating issue comment. Use `reply_to` to continue a comment thread; use `reply_to_ask` to reply
146
+ floating issue comment. A reply (`reply_to`/`reply_to_ask`) takes no `quote`; it belongs to its
147
+ parent's anchor. Use `reply_to` to continue a comment thread; use `reply_to_ask` to reply
145
148
  directly under a question asked with `dispatch_ask`. The two are mutually exclusive.
146
149
 
147
150
  Propose an exact replacement instead of describing it:
@@ -152,8 +155,12 @@ dispatch_suggest({ issue, artifact, quote, replace_with, body?, occurrence? })
152
155
 
153
156
  It returns `details` `{ issue, topic, comment }`. A human accepts or rejects a suggestion. On
154
157
  `TARGET_AMBIGUOUS`, add zero-based `occurrence`. On `TARGET_NOT_FOUND`, re-read the document
155
- before retrying. `INVALID_OP` names a malformed edit; `CAP_EXCEEDED` never truncates;
156
- `ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID` reject an invalid actor or route.
158
+ before retrying. `INVALID_ANCHOR` requires exactly one nonempty anchor `quote` or `mark_id`;
159
+ `ANCHOR_MISSING` means a browser mark was not observed in the live tree, and
160
+ `ANCHOR_ORPHANED` means its marked text no longer exists. `INVALID_MARKDOWN` and `DOC_SCHEMA`
161
+ reject Markdown or a live tree outside the Proof schema. `INVALID_OP` names a malformed edit;
162
+ `CAP_EXCEEDED` never truncates; `ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID`
163
+ reject an invalid actor or route.
157
164
 
158
165
  ## Artifacts
159
166
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [