@sjawhar/pi-legion-envoy 1.69.0 → 2.0.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
@@ -16111,7 +16111,7 @@ var require_mod4 = __commonJS((exports) => {
16111
16111
 
16112
16112
  // extensions/legion.ts
16113
16113
  import { randomUUID as randomUUID3 } from "crypto";
16114
- import path5 from "path";
16114
+ import path4 from "path";
16115
16115
 
16116
16116
  // ../envoy-client/src/defaults.ts
16117
16117
  var DEFAULT_ENVOY_URL = "http://127.0.0.1:9020";
@@ -16142,7 +16142,7 @@ import { logger } from "@oh-my-pi/pi-utils";
16142
16142
  // package.json
16143
16143
  var package_default = {
16144
16144
  name: "@sjawhar/pi-legion-envoy",
16145
- version: "1.69.0",
16145
+ version: "2.0.0",
16146
16146
  type: "module",
16147
16147
  omp: {
16148
16148
  extensions: [
@@ -16155,7 +16155,7 @@ var package_default = {
16155
16155
  },
16156
16156
  legion: {
16157
16157
  daemonApiVersion: 8,
16158
- goDaemonApiVersion: 4
16158
+ goDaemonApiVersion: 5
16159
16159
  },
16160
16160
  repository: {
16161
16161
  type: "git",
@@ -31308,7 +31308,7 @@ function createControllerSession(persistedTranscript, checkSubagentSession, daem
31308
31308
 
31309
31309
  // src/legion/grant-file.ts
31310
31310
  import { randomUUID } from "crypto";
31311
- import { chmod, rename, rm, writeFile } from "fs/promises";
31311
+ import { chmod, mkdir, rename, rm, writeFile } from "fs/promises";
31312
31312
  import path from "path";
31313
31313
  async function writeGrantFile(file2, grantId) {
31314
31314
  if (!path.isAbsolute(file2)) {
@@ -31316,6 +31316,7 @@ async function writeGrantFile(file2, grantId) {
31316
31316
  }
31317
31317
  const temp = `${file2}.${process.pid}.${randomUUID()}`;
31318
31318
  try {
31319
+ await mkdir(path.dirname(file2), { recursive: true, mode: 448 });
31319
31320
  await writeFile(temp, grantId, { encoding: "utf8", mode: 384 });
31320
31321
  await chmod(temp, 384);
31321
31322
  await rename(temp, file2);
@@ -31332,9 +31333,6 @@ async function writeMintedGrant(mint) {
31332
31333
  await writeGrantFile(file2, await mint());
31333
31334
  }
31334
31335
 
31335
- // src/legion/go-bootstrap.ts
31336
- import path3 from "path";
31337
-
31338
31336
  // ../contracts/src/legion-go-api.ts
31339
31337
  var nonEmptyString2 = exports_external.string().min(1);
31340
31338
  var timestamp = exports_external.iso.datetime({ offset: true });
@@ -31662,7 +31660,7 @@ function createLegionGoDaemonClient(baseUrl, fetchFn = fetch) {
31662
31660
 
31663
31661
  // src/legion/jj-attribution.ts
31664
31662
  import { execFile } from "child_process";
31665
- import { mkdir, rename as rename2, writeFile as writeFile2 } from "fs/promises";
31663
+ import { mkdir as mkdir2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
31666
31664
  import path2 from "path";
31667
31665
  import { promisify } from "util";
31668
31666
  var UUID = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
@@ -31695,7 +31693,7 @@ async function exportJjSessionAttribution(sessionFile, stateDir, env = process.e
31695
31693
  if (!base)
31696
31694
  return;
31697
31695
  try {
31698
- await mkdir(stateDir, { recursive: true });
31696
+ await mkdir2(stateDir, { recursive: true });
31699
31697
  const tmp = `${overlay}.${process.pid}.tmp`;
31700
31698
  await writeFile2(tmp, `[templates]
31701
31699
  commit_trailers = '"Omp-Session: ${id}"'
@@ -31799,7 +31797,6 @@ async function bootstrapGoClaim(context, state) {
31799
31797
  try {
31800
31798
  const stateDir = requiredEnvironment(process.env, "LEGION_STATE_DIR");
31801
31799
  await exportJjSessionAttribution(sessionFile, stateDir);
31802
- process.env.LEGION_GRANT_FILE = path3.join(stateDir, "secrets", `${claim.claimToken}-grant`);
31803
31800
  state.setCapability({
31804
31801
  kind: "phase-worker",
31805
31802
  sessionID,
@@ -32369,7 +32366,7 @@ function createLegionTool(deps) {
32369
32366
 
32370
32367
  // src/subagent-session.ts
32371
32368
  import fs from "fs";
32372
- import path4 from "path";
32369
+ import path3 from "path";
32373
32370
  import * as host from "@oh-my-pi/pi-coding-agent";
32374
32371
  var LEGION_BOOTSTRAPPED_SESSION = Symbol.for("legion.pi-envoy.bootstrapped-session");
32375
32372
  var bootstrappedSessionStore = globalThis;
@@ -32380,7 +32377,7 @@ async function isSubagentSession(context) {
32380
32377
  await context.sessionManager.ensureOnDisk();
32381
32378
  const sessionFile = context.sessionManager.getSessionFile();
32382
32379
  if (sessionFile !== undefined && fs.existsSync(sessionFile)) {
32383
- return fs.existsSync(`${path4.dirname(sessionFile)}.jsonl`);
32380
+ return fs.existsSync(`${path3.dirname(sessionFile)}.jsonl`);
32384
32381
  }
32385
32382
  const bootstrapped = bootstrappedSessionStore[LEGION_BOOTSTRAPPED_SESSION];
32386
32383
  return bootstrapped !== undefined && bootstrapped !== sessionFile;
@@ -32444,7 +32441,16 @@ var callReadyWithRetry = async (label, call) => {
32444
32441
  }
32445
32442
  }
32446
32443
  };
32447
- async function wrapBashWithGrant(mint) {
32444
+ var GH_RESOLVED_URL = /(?:^|[\s;,"])(?:pr|issue):\/\//i;
32445
+ function needsGrant({ toolName, input }) {
32446
+ if (toolName === "bash")
32447
+ return typeof input.command === "string";
32448
+ if (toolName === "github")
32449
+ return true;
32450
+ const paths = Array.isArray(input.paths) ? input.paths : [input.path];
32451
+ return paths.some((entry) => typeof entry === "string" && GH_RESOLVED_URL.test(entry));
32452
+ }
32453
+ async function wrapWithGrant(mint) {
32448
32454
  try {
32449
32455
  await writeMintedGrant(async () => (await mint()).grantId);
32450
32456
  return;
@@ -32458,7 +32464,7 @@ async function persistedTranscript(context) {
32458
32464
  if (!sessionFile?.endsWith(".jsonl")) {
32459
32465
  throw new Error("Legion session must have a persisted transcript");
32460
32466
  }
32461
- const agentId = path5.basename(sessionFile, ".jsonl");
32467
+ const agentId = path4.basename(sessionFile, ".jsonl");
32462
32468
  if (!agentId)
32463
32469
  throw new Error("Legion session transcript has no agent id");
32464
32470
  return { sessionFile, agentId };
@@ -32970,11 +32976,11 @@ function legionExtension(pi) {
32970
32976
  return { block: true, reason: "the merger only verifies and reports" };
32971
32977
  }
32972
32978
  }
32973
- if (toolCall.toolName !== "bash" || typeof toolCall.input.command !== "string")
32979
+ if (!needsGrant(toolCall))
32974
32980
  return;
32975
32981
  if (active === undefined) {
32976
32982
  if (controllerSession.isClaimedSession(sessionID)) {
32977
- return wrapBashWithGrant(() => controllerSession.mintGrant(sessionID));
32983
+ return wrapWithGrant(() => controllerSession.mintGrant(sessionID));
32978
32984
  }
32979
32985
  if (process.env.LEGION_ROLE !== undefined && process.env.LEGION_CONTROLLER !== "1") {
32980
32986
  return {
@@ -32984,7 +32990,7 @@ function legionExtension(pi) {
32984
32990
  }
32985
32991
  return;
32986
32992
  }
32987
- return wrapBashWithGrant(() => process.env.LEGION_DAEMON_API === "go" ? goRoleDaemon().grant({
32993
+ return wrapWithGrant(() => process.env.LEGION_DAEMON_API === "go" ? goRoleDaemon().grant({
32988
32994
  tree: active.tree,
32989
32995
  issue: active.issue,
32990
32996
  sessionId: sessionID,
@@ -189,14 +189,17 @@ comments live on that path too, so edit them with `gh pr comment`) — printing
189
189
  `Legion issues live on Dispatch; use dispatch_message or dispatch_comment on <your LEGION_ISSUE>`:
190
190
  Legion never reads or writes a GitHub issue (LEGION-78). `pr comment`, `pr review`,
191
191
  `api …/pulls/…`, `api graphql`, and issue reads are unaffected. The credential reaches `legion`
192
- through the file `$LEGION_GRANT_FILE` names, written before each of your bash commands by the
193
- extension (and by the `legion` tool before its `handoff_complete`); never `cat`, `echo`, copy, or
192
+ through the file `$LEGION_GRANT_FILE` names, written by the extension before each of your bash
193
+ commands, each `github` tool call, and each `read`/`grep` of a `pr://` or `issue://` URL (and by
194
+ the `legion` tool before its `handoff_complete`); never `cat`, `echo`, copy, or
194
195
  `export` it — `legion credential`, `legion gh`, `jj git push`, and `handoff_complete` read it
195
- themselves. The file is the pane's, not the
196
- command's: a `task` subagent, an `eval` subprocess, or a background job in your pane reads the
197
- grant your last bash command minted, so its `legion gh` or `jj git push` succeeds only within 60
198
- seconds of that call and 403s afterwards — a timing artifact, not a broken credential; run
199
- credentialed commands from your own bash calls.
196
+ themselves. The file is the pane's, not the command's, and a grant lives 60 seconds: a `task`
197
+ subagent, an `eval` subprocess, or a background job in your pane reads the grant your last such
198
+ call wrote, and a `github` tool `run_watch` keeps polling `gh` on the one written when the call
199
+ began, so each succeeds only within 60 seconds of that call and 403s afterwards — a timing
200
+ artifact, not a broken credential. Run credentialed commands from your own bash calls, and watch a
201
+ run that may outlast a minute with `gh run watch` in bash, which redeems once and then runs on the
202
+ token it got.
200
203
 
201
204
  ## GitHub PR comment attribution
202
205
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "1.69.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "legion": {
15
15
  "daemonApiVersion": 8,
16
- "goDaemonApiVersion": 4
16
+ "goDaemonApiVersion": 5
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",