@sjawhar/pi-legion-envoy 0.10.0 → 0.12.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/README.md CHANGED
@@ -44,9 +44,10 @@ set -g set-clipboard on
44
44
 
45
45
  This package declares two OMP extension entries in `package.json`: `extensions/envoy.ts`
46
46
  (Envoy messaging, subscriptions, and steering delivery) and `extensions/legion.ts` (the
47
- Legion lifecycle: root bootstrap, worker spawning, budgets, and daemon capabilities).
48
- `extensions/legion.ts` is inert without `LEGION_TREE`, `LEGION_ROLE`, or `LEGION_CONTROLLER`
49
- set in the environment, so loading both alongside each other is safe for every session.
47
+ Legion lifecycle: root and phase-worker bootstrap from the daemon's environment, and
48
+ daemon capabilities). The Legion daemon spawns every root and phase-worker process with
49
+ the installed plugin already active, so `extensions/legion.ts` loads for them automatically;
50
+ it is inert without `LEGION_TREE`/`LEGION_ROLE`/`LEGION_CONTROLLER` in the environment.
50
51
 
51
52
  For local development of the messaging extension alone, link the entry into OMP:
52
53
 
package/dist/envoy.js CHANGED
@@ -29783,9 +29783,7 @@ var LEGION_ROLES = [
29783
29783
  "reviewer",
29784
29784
  "merger"
29785
29785
  ];
29786
- var ENVOY_ROLE_TOKEN = /^[a-z0-9][a-z0-9_-]*$/;
29787
29786
  var PROJECT_TOKEN = /^[a-z0-9]+$/;
29788
- var ISSUE_PART = /^[a-z0-9._-]+$/;
29789
29787
  function isLegionRole(role) {
29790
29788
  return LEGION_ROLES.some((candidate) => candidate === role);
29791
29789
  }
@@ -29797,91 +29795,10 @@ function assertLegionProjectToken(project) {
29797
29795
  throw new Error(`Invalid Legion project token: ${project}`);
29798
29796
  }
29799
29797
  }
29800
- function encodeIssuePart(part) {
29801
- const normalized = part.toLowerCase();
29802
- if (!ISSUE_PART.test(normalized)) {
29803
- throw new Error(`Invalid Legion issue token part: ${part}`);
29804
- }
29805
- return normalized.replaceAll("_", "_u").replaceAll(".", "_d").replaceAll("-", "_h");
29806
- }
29807
- function decodeIssuePart(part) {
29808
- let decoded = "";
29809
- for (let index = 0;index < part.length; index += 1) {
29810
- const character = part[index];
29811
- if (character !== "_") {
29812
- decoded += character;
29813
- continue;
29814
- }
29815
- const escapeCode = part[index + 1];
29816
- if (escapeCode === "u")
29817
- decoded += "_";
29818
- else if (escapeCode === "d")
29819
- decoded += ".";
29820
- else if (escapeCode === "h")
29821
- decoded += "-";
29822
- else
29823
- return;
29824
- index += 1;
29825
- }
29826
- return decoded;
29827
- }
29828
- function formatIssueKey(owner, repo, number) {
29829
- return `${owner}/${repo}#${number}`;
29830
- }
29831
- function parseIssueKey(s) {
29832
- const match = /^([^/#]+)\/([^/#]+)#(\d+)$/.exec(s);
29833
- if (!match)
29834
- return;
29835
- const owner = match[1];
29836
- const repo = match[2];
29837
- const numberPart = match[3];
29838
- if (!owner || !repo || !numberPart)
29839
- return;
29840
- const number = Number(numberPart);
29841
- if (!Number.isSafeInteger(number))
29842
- return;
29843
- return { owner, repo, number };
29844
- }
29845
- function roleToken(project, issue, role) {
29846
- assertLegionProjectToken(project);
29847
- const parsedIssue = parseIssueKey(issue);
29848
- if (!parsedIssue)
29849
- throw new Error(`Invalid IssueKey: ${issue}`);
29850
- return `legion-${project}-${encodeIssuePart(parsedIssue.owner)}__${encodeIssuePart(parsedIssue.repo)}-${parsedIssue.number}-${role}`;
29851
- }
29852
29798
  function controllerToken(project) {
29853
29799
  assertLegionProjectToken(project);
29854
29800
  return `legion-${project}-controller`;
29855
29801
  }
29856
- function parseRoleToken(project, token) {
29857
- if (!isLegionProjectToken(project))
29858
- return;
29859
- const prefix = `legion-${project}-`;
29860
- if (!ENVOY_ROLE_TOKEN.test(token) || !token.startsWith(prefix))
29861
- return;
29862
- const rest = token.slice(prefix.length);
29863
- if (rest === "controller")
29864
- return { controller: true };
29865
- const match = /^([a-z0-9_]+)__([a-z0-9_]+)-(\d+)-(architect|planner|implementer|tester|reviewer|merger)$/.exec(rest);
29866
- if (!match)
29867
- return;
29868
- const ownerPart = match[1];
29869
- const repoPart = match[2];
29870
- const numberPart = match[3];
29871
- const role = match[4];
29872
- if (!ownerPart || !repoPart || !numberPart || !role || !isLegionRole(role))
29873
- return;
29874
- const owner = decodeIssuePart(ownerPart);
29875
- const repo = decodeIssuePart(repoPart);
29876
- const number = Number(numberPart);
29877
- if (!owner || !repo || !Number.isSafeInteger(number))
29878
- return;
29879
- return {
29880
- project,
29881
- issue: formatIssueKey(owner, repo, number),
29882
- role
29883
- };
29884
- }
29885
29802
 
29886
29803
  // ../contracts/src/legion-daemon-api.ts
29887
29804
  var nonEmptyString = string2().min(1);
@@ -30003,6 +29920,45 @@ var LegionDaemonApi = {
30003
29920
  }),
30004
29921
  response: object({})
30005
29922
  },
29923
+ WorkerStarted: {
29924
+ request: strictObject({
29925
+ tree: nonEmptyString,
29926
+ issue: nonEmptyString,
29927
+ role: legionRole,
29928
+ bootToken: nonEmptyString,
29929
+ sessionId: nonEmptyString,
29930
+ agentId: nonEmptyString,
29931
+ ompSessionFile: nonEmptyString
29932
+ }),
29933
+ response: object({
29934
+ roleToken: nonEmptyString,
29935
+ secret: nonEmptyString,
29936
+ gitName: nonEmptyString,
29937
+ gitEmail: nonEmptyString
29938
+ })
29939
+ },
29940
+ WorkerReady: {
29941
+ request: strictObject({
29942
+ tree: nonEmptyString,
29943
+ issue: nonEmptyString,
29944
+ role: legionRole,
29945
+ sessionId: nonEmptyString,
29946
+ generation: number2().int().nonnegative(),
29947
+ secret: nonEmptyString
29948
+ }),
29949
+ response: object({})
29950
+ },
29951
+ SpawnWorker: {
29952
+ request: architectCapability.extend({
29953
+ issue: nonEmptyString,
29954
+ role: legionRole,
29955
+ task: nonEmptyString
29956
+ }),
29957
+ response: object({
29958
+ status: _enum2(["spawned", "resumed"]),
29959
+ roleToken: nonEmptyString
29960
+ })
29961
+ },
30006
29962
  Phase: {
30007
29963
  request: strictObject({
30008
29964
  tree: nonEmptyString,
@@ -31505,15 +31461,6 @@ async function claimEnvoyRole(sessionID, role, context) {
31505
31461
  const bridge = legionRoleClaimBridge();
31506
31462
  await (bridge.claim ?? await bridge.ready.promise)(sessionID, role, context);
31507
31463
  }
31508
- async function deleteEnvoyInterest(baseUrl, sessionID) {
31509
- const response = await fetch(`${baseUrl}/v1/interests/${encodeURIComponent(sessionID)}`, {
31510
- method: "DELETE"
31511
- });
31512
- const responseBody = await response.text();
31513
- if (!response.ok) {
31514
- throw new Error(`DELETE /v1/interests/${sessionID} failed with ${response.status}: ${responseBody}`);
31515
- }
31516
- }
31517
31464
  function resolveSkillsDirectory() {
31518
31465
  const moduleDirectory = dirname(fileURLToPath(import.meta.url));
31519
31466
  const candidates = [
@@ -32034,6 +31981,5 @@ function topicsFor(parameters, fallback = []) {
32034
31981
  }
32035
31982
  export {
32036
31983
  claimEnvoyRole,
32037
- envoyExtension as default,
32038
- deleteEnvoyInterest
31984
+ envoyExtension as default
32039
31985
  };