@h-rig/runtime 0.0.6-alpha.2 → 0.0.6-alpha.4

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.
Files changed (40) hide show
  1. package/dist/bin/rig-agent-dispatch.js +93 -14
  2. package/dist/bin/rig-agent.js +83 -26
  3. package/dist/src/control-plane/agent-wrapper.js +93 -14
  4. package/dist/src/control-plane/harness-main.js +83 -26
  5. package/dist/src/control-plane/hooks/completion-verification.js +85 -28
  6. package/dist/src/control-plane/hooks/inject-context.js +2 -2
  7. package/dist/src/control-plane/hooks/submodule-branch.js +26 -3
  8. package/dist/src/control-plane/hooks/task-runtime-start.js +26 -3
  9. package/dist/src/control-plane/native/git-ops.js +81 -24
  10. package/dist/src/control-plane/native/harness-cli.js +83 -26
  11. package/dist/src/control-plane/native/pr-automation.js +87 -16
  12. package/dist/src/control-plane/native/run-ops.js +23 -6
  13. package/dist/src/control-plane/native/task-ops.js +2 -2
  14. package/dist/src/control-plane/native/validator.js +2 -2
  15. package/dist/src/control-plane/native/verifier.js +2 -2
  16. package/dist/src/control-plane/runtime/index.js +38 -9
  17. package/dist/src/control-plane/runtime/isolation/home.js +31 -6
  18. package/dist/src/control-plane/runtime/isolation/index.js +38 -9
  19. package/dist/src/control-plane/runtime/isolation/runner.js +31 -6
  20. package/dist/src/control-plane/runtime/isolation/shared.js +9 -6
  21. package/dist/src/control-plane/runtime/isolation.js +38 -9
  22. package/dist/src/control-plane/runtime/queue.js +38 -9
  23. package/dist/src/control-plane/tasks/source-aware-task-config-source.js +14 -2
  24. package/dist/src/control-plane/tasks/source-lifecycle.js +2 -2
  25. package/native/darwin-arm64/{bin/rig-git → rig-git} +0 -0
  26. package/native/darwin-arm64/rig-git.build-manifest.json +4 -0
  27. package/native/darwin-arm64/{bin/rig-shell → rig-shell} +0 -0
  28. package/native/darwin-arm64/rig-shell.build-manifest.json +4 -0
  29. package/native/darwin-arm64/{bin/rig-tools → rig-tools} +0 -0
  30. package/native/darwin-arm64/rig-tools.build-manifest.json +4 -0
  31. package/native/darwin-arm64/{lib/runtime-native.dylib → runtime-native.dylib} +0 -0
  32. package/package.json +6 -6
  33. package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
  34. package/native/darwin-arm64/manifest.json +0 -1
  35. package/native/linux-x64/bin/rig-git +0 -0
  36. package/native/linux-x64/bin/rig-shell +0 -0
  37. package/native/linux-x64/bin/rig-tools +0 -0
  38. package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
  39. package/native/linux-x64/lib/runtime-native.so +0 -0
  40. package/native/linux-x64/manifest.json +0 -1
@@ -1203,8 +1203,8 @@ function githubStatusFor(issue) {
1203
1203
  return "open";
1204
1204
  }
1205
1205
  function selectedGitHubEnv() {
1206
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
1207
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
1206
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
1207
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
1208
1208
  }
1209
1209
  function ghSpawnOptions() {
1210
1210
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -5989,12 +5989,12 @@ var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
5989
5989
  "task-result.json",
5990
5990
  "validation-summary.json"
5991
5991
  ]);
5992
- function resolveHostRigBinDir(root) {
5993
- return resolve24(root, ".rig", "bin");
5994
- }
5995
5992
  function isRuntimeGatewayGitPath(candidate) {
5996
5993
  return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
5997
5994
  }
5995
+ function isRuntimeGatewayGhPath(candidate) {
5996
+ return /\/\.rig\/bin\/gh$/.test(candidate.replace(/\\/g, "/"));
5997
+ }
5998
5998
  function resolveOptionalMonorepoRoot(projectRoot) {
5999
5999
  const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
6000
6000
  if (runtimeWorkspace && existsSync21(resolve24(runtimeWorkspace, ".git"))) {
@@ -6029,6 +6029,9 @@ function resolveGitBinary(projectRoot) {
6029
6029
  }
6030
6030
  return "git";
6031
6031
  }
6032
+ function escapeRegExp2(value) {
6033
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6034
+ }
6032
6035
  function safeCurrentTaskId(projectRoot) {
6033
6036
  try {
6034
6037
  const taskId = currentTaskId(projectRoot);
@@ -6187,17 +6190,15 @@ function gitOpenPr(options) {
6187
6190
  const target = options.target || (taskId ? "monorepo" : "project");
6188
6191
  let repoRoot = options.projectRoot;
6189
6192
  let repoLabel = "project-rig";
6190
- let defaultBase = process.env.RIG_PR_BASE_PROJECT || "main";
6193
+ const envBase = target === "monorepo" ? process.env.RIG_PR_BASE_MONOREPO?.trim() || "" : process.env.RIG_PR_BASE_PROJECT?.trim() || "";
6191
6194
  if (target === "monorepo") {
6192
6195
  repoRoot = resolveOptionalMonorepoRoot(options.projectRoot) || resolveMonorepoRoot2(options.projectRoot);
6193
6196
  repoLabel = "monorepo";
6194
- defaultBase = process.env.RIG_PR_BASE_MONOREPO || "main";
6195
6197
  if (taskId) {
6196
6198
  gitSyncBranch(options.projectRoot, taskId, "monorepo");
6197
6199
  }
6198
6200
  } else if (taskId) {
6199
6201
  gitSyncBranch(options.projectRoot, taskId, "project");
6200
- defaultBase = inferProjectBase(options.projectRoot, defaultBase);
6201
6202
  }
6202
6203
  if (!existsSync21(resolve24(repoRoot, ".git"))) {
6203
6204
  throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
@@ -6206,9 +6207,9 @@ function gitOpenPr(options) {
6206
6207
  if (!branch || branch === "HEAD") {
6207
6208
  throw new Error(`Cannot open PR from detached HEAD in ${repoLabel}. Checkout a branch first.`);
6208
6209
  }
6209
- const base = options.base || defaultBase;
6210
6210
  const repoNameWithOwner = resolveRepoNameWithOwner(options.projectRoot, repoRoot);
6211
6211
  const networkRemote = resolveNetworkRemoteName(options.projectRoot, repoRoot, repoNameWithOwner);
6212
+ const base = options.base || envBase || inferRepositoryDefaultBase(options.projectRoot, repoRoot, repoNameWithOwner, networkRemote, target === "project" ? inferProjectBase(options.projectRoot, "main") : "main");
6212
6213
  refreshRemoteBaseRef(options.projectRoot, repoRoot, base);
6213
6214
  let reviewer = (options.reviewer || "").trim();
6214
6215
  let reviewerSource = reviewer ? "flag" : undefined;
@@ -6244,6 +6245,7 @@ function gitOpenPr(options) {
6244
6245
  "",
6245
6246
  "## Task",
6246
6247
  `- beads: ${taskId || "n/a"}`,
6248
+ ...defaultPrRunLines(taskId, repoNameWithOwner),
6247
6249
  "",
6248
6250
  "## Review",
6249
6251
  "- Completion verification will run validation, verifier review, and PR policy checks.",
@@ -6335,6 +6337,29 @@ function gitOpenPr(options) {
6335
6337
  }
6336
6338
  return result;
6337
6339
  }
6340
+ function defaultPrRunLines(taskId, repoNameWithOwner) {
6341
+ const lines = [];
6342
+ const runId = process.env.RIG_SERVER_RUN_ID?.trim();
6343
+ if (runId) {
6344
+ lines.push(`- Run: ${runId}`);
6345
+ }
6346
+ const closeout = defaultPrCloseoutLine(taskId, repoNameWithOwner);
6347
+ if (closeout) {
6348
+ lines.push(`- ${closeout}`);
6349
+ }
6350
+ return lines;
6351
+ }
6352
+ function defaultPrCloseoutLine(taskId, repoNameWithOwner) {
6353
+ const sourceIssueId = loadRuntimeContextFromEnv()?.sourceTask?.sourceIssueId;
6354
+ if (sourceIssueId) {
6355
+ const match = sourceIssueId.match(/^([^#]+)#(\d+)$/);
6356
+ if (match) {
6357
+ const [, sourceRepo, issueNumber] = match;
6358
+ return sourceRepo.toLowerCase() === repoNameWithOwner.toLowerCase() ? `Closes #${issueNumber}` : `Closes ${sourceRepo}#${issueNumber}`;
6359
+ }
6360
+ }
6361
+ return /^\d+$/.test(taskId) ? `Closes #${taskId}` : "";
6362
+ }
6338
6363
  function readPrViewState(gh, repoRoot, repoNameWithOwner, prUrl) {
6339
6364
  const view = runCapture2(withGhRepo([
6340
6365
  gh,
@@ -6485,32 +6510,19 @@ function resolveGithubCliBinary(projectRoot) {
6485
6510
  if (explicit) {
6486
6511
  candidates.add(explicit);
6487
6512
  }
6513
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
6514
+ candidates.add(candidate);
6515
+ }
6488
6516
  const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
6489
6517
  for (const entry of explicitPathEntries) {
6490
6518
  candidates.add(resolve24(entry, "gh"));
6491
6519
  }
6492
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim();
6493
- if (hostProjectRoot) {
6494
- candidates.add(resolve24(resolveHostRigBinDir(hostProjectRoot), "gh"));
6495
- }
6496
- candidates.add(resolve24(resolveHostRigBinDir(projectRoot), "gh"));
6497
- const runtimeContext = loadRuntimeContextFromEnv();
6498
- if (runtimeContext?.binDir) {
6499
- candidates.add(resolve24(runtimeContext.binDir, "gh"));
6500
- }
6501
- const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
6502
- if (runtimeHome) {
6503
- candidates.add(resolve24(runtimeHome, "bin", "gh"));
6504
- }
6505
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
6506
- candidates.add(candidate);
6507
- }
6508
6520
  const bunResolved = Bun.which("gh");
6509
6521
  if (bunResolved) {
6510
6522
  candidates.add(bunResolved);
6511
6523
  }
6512
6524
  for (const candidate of candidates) {
6513
- if (candidate && existsSync21(candidate)) {
6525
+ if (candidate && existsSync21(candidate) && !isRuntimeGatewayGhPath(candidate)) {
6514
6526
  return candidate;
6515
6527
  }
6516
6528
  }
@@ -6659,6 +6671,32 @@ function withGhRepo(command, repoNameWithOwner) {
6659
6671
  }
6660
6672
  return [command[0], command[1], command[2], ...ghRepoArgs(repoNameWithOwner), ...command.slice(3)];
6661
6673
  }
6674
+ function inferRepositoryDefaultBase(projectRoot, repoRoot, repoNameWithOwner, remoteName, fallback) {
6675
+ const remote = remoteName || "origin";
6676
+ const symbolic = runCapture2(gitCmd(projectRoot, repoRoot, "symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`), projectRoot);
6677
+ if (symbolic.exitCode === 0) {
6678
+ const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp2(remote)}/`), "");
6679
+ if (ref && ref !== "HEAD") {
6680
+ return ref;
6681
+ }
6682
+ }
6683
+ const lsRemote = runCapture2(gitCmd(projectRoot, repoRoot, "ls-remote", "--symref", remote, "HEAD"), projectRoot);
6684
+ if (lsRemote.exitCode === 0) {
6685
+ const match = lsRemote.stdout.match(/^ref:\s+refs\/heads\/([^\t\r\n]+)\s+HEAD/m);
6686
+ if (match?.[1]) {
6687
+ return match[1];
6688
+ }
6689
+ }
6690
+ const gh = resolveGithubCliBinary(projectRoot);
6691
+ if (gh && repoNameWithOwner) {
6692
+ const api = runCapture2(withGhRepo([gh, "repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"], repoNameWithOwner), repoRoot);
6693
+ const branch = api.exitCode === 0 ? api.stdout.trim() : "";
6694
+ if (branch) {
6695
+ return branch;
6696
+ }
6697
+ }
6698
+ return fallback;
6699
+ }
6662
6700
  function inferProjectBase(projectRoot, fallback) {
6663
6701
  const containing = runCapture2(gitCmd(projectRoot, projectRoot, "branch", "-r", "--contains", "HEAD"), projectRoot);
6664
6702
  if (containing.exitCode !== 0) {
@@ -7040,6 +7078,10 @@ function runtimeGitEnv(projectRoot) {
7040
7078
  }
7041
7079
  env[key] = value;
7042
7080
  }
7081
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
7082
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
7083
+ env.GITHUB_TOKEN = rigGithubToken;
7084
+ }
7043
7085
  if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
7044
7086
  env.GITHUB_TOKEN = env.GH_TOKEN;
7045
7087
  }
@@ -7063,6 +7105,13 @@ function runtimeGitEnv(projectRoot) {
7063
7105
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
7064
7106
  env.GH_TOKEN = env.GITHUB_TOKEN;
7065
7107
  }
7108
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
7109
+ if (gitHubToken) {
7110
+ env.RIG_GITHUB_TOKEN = gitHubToken;
7111
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
7112
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
7113
+ applyGitHubCredentialHelperEnv(env);
7114
+ }
7066
7115
  if (runtimeKnownHosts && existsSync21(runtimeKnownHosts)) {
7067
7116
  const sshParts = [
7068
7117
  "ssh",
@@ -7079,6 +7128,14 @@ function runtimeGitEnv(projectRoot) {
7079
7128
  }
7080
7129
  return Object.keys(env).length > 0 ? env : undefined;
7081
7130
  }
7131
+ function applyGitHubCredentialHelperEnv(env) {
7132
+ env.GIT_TERMINAL_PROMPT = "0";
7133
+ env.GIT_CONFIG_COUNT = "2";
7134
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
7135
+ env.GIT_CONFIG_VALUE_0 = "";
7136
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
7137
+ env.GIT_CONFIG_VALUE_1 = '!f() { test "$1" = get || exit 0; token="${GITHUB_TOKEN:-${GH_TOKEN:-${RIG_GITHUB_TOKEN:-}}}"; test -n "$token" || exit 0; echo username=x-access-token; echo password="$token"; }; f';
7138
+ }
7082
7139
  function loadPersistedRuntimeSecrets(runtimeRoot) {
7083
7140
  if (!runtimeRoot) {
7084
7141
  return {};
@@ -5,7 +5,7 @@
5
5
  import { appendFileSync as appendFileSync2, existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
6
6
  import { resolve as resolve24 } from "path";
7
7
  import {
8
- escapeRegExp,
8
+ escapeRegExp as escapeRegExp2,
9
9
  resolveBunCli,
10
10
  resolveBunCliInvocation,
11
11
  resolveProjectRoot,
@@ -2645,8 +2645,8 @@ function ensureStatusLabel(bin, repo, spawnFn, label) {
2645
2645
  }
2646
2646
  }
2647
2647
  function selectedGitHubEnv() {
2648
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
2649
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
2648
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
2649
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
2650
2650
  }
2651
2651
  function ghSpawnOptions() {
2652
2652
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -5993,12 +5993,12 @@ var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
5993
5993
  "task-result.json",
5994
5994
  "validation-summary.json"
5995
5995
  ]);
5996
- function resolveHostRigBinDir(root) {
5997
- return resolve23(root, ".rig", "bin");
5998
- }
5999
5996
  function isRuntimeGatewayGitPath(candidate) {
6000
5997
  return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
6001
5998
  }
5999
+ function isRuntimeGatewayGhPath(candidate) {
6000
+ return /\/\.rig\/bin\/gh$/.test(candidate.replace(/\\/g, "/"));
6001
+ }
6002
6002
  function resolveOptionalMonorepoRoot(projectRoot) {
6003
6003
  const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
6004
6004
  if (runtimeWorkspace && existsSync20(resolve23(runtimeWorkspace, ".git"))) {
@@ -6033,6 +6033,9 @@ function resolveGitBinary(projectRoot) {
6033
6033
  }
6034
6034
  return "git";
6035
6035
  }
6036
+ function escapeRegExp(value) {
6037
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6038
+ }
6036
6039
  function safeCurrentTaskId(projectRoot) {
6037
6040
  try {
6038
6041
  const taskId = currentTaskId(projectRoot);
@@ -6101,17 +6104,15 @@ function gitOpenPr(options) {
6101
6104
  const target = options.target || (taskId ? "monorepo" : "project");
6102
6105
  let repoRoot = options.projectRoot;
6103
6106
  let repoLabel = "project-rig";
6104
- let defaultBase = process.env.RIG_PR_BASE_PROJECT || "main";
6107
+ const envBase = target === "monorepo" ? process.env.RIG_PR_BASE_MONOREPO?.trim() || "" : process.env.RIG_PR_BASE_PROJECT?.trim() || "";
6105
6108
  if (target === "monorepo") {
6106
6109
  repoRoot = resolveOptionalMonorepoRoot(options.projectRoot) || resolveMonorepoRoot2(options.projectRoot);
6107
6110
  repoLabel = "monorepo";
6108
- defaultBase = process.env.RIG_PR_BASE_MONOREPO || "main";
6109
6111
  if (taskId) {
6110
6112
  gitSyncBranch(options.projectRoot, taskId, "monorepo");
6111
6113
  }
6112
6114
  } else if (taskId) {
6113
6115
  gitSyncBranch(options.projectRoot, taskId, "project");
6114
- defaultBase = inferProjectBase(options.projectRoot, defaultBase);
6115
6116
  }
6116
6117
  if (!existsSync20(resolve23(repoRoot, ".git"))) {
6117
6118
  throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
@@ -6120,9 +6121,9 @@ function gitOpenPr(options) {
6120
6121
  if (!branch || branch === "HEAD") {
6121
6122
  throw new Error(`Cannot open PR from detached HEAD in ${repoLabel}. Checkout a branch first.`);
6122
6123
  }
6123
- const base = options.base || defaultBase;
6124
6124
  const repoNameWithOwner = resolveRepoNameWithOwner(options.projectRoot, repoRoot);
6125
6125
  const networkRemote = resolveNetworkRemoteName(options.projectRoot, repoRoot, repoNameWithOwner);
6126
+ const base = options.base || envBase || inferRepositoryDefaultBase(options.projectRoot, repoRoot, repoNameWithOwner, networkRemote, target === "project" ? inferProjectBase(options.projectRoot, "main") : "main");
6126
6127
  refreshRemoteBaseRef(options.projectRoot, repoRoot, base);
6127
6128
  let reviewer = (options.reviewer || "").trim();
6128
6129
  let reviewerSource = reviewer ? "flag" : undefined;
@@ -6158,6 +6159,7 @@ function gitOpenPr(options) {
6158
6159
  "",
6159
6160
  "## Task",
6160
6161
  `- beads: ${taskId || "n/a"}`,
6162
+ ...defaultPrRunLines(taskId, repoNameWithOwner),
6161
6163
  "",
6162
6164
  "## Review",
6163
6165
  "- Completion verification will run validation, verifier review, and PR policy checks.",
@@ -6249,6 +6251,29 @@ function gitOpenPr(options) {
6249
6251
  }
6250
6252
  return result;
6251
6253
  }
6254
+ function defaultPrRunLines(taskId, repoNameWithOwner) {
6255
+ const lines = [];
6256
+ const runId = process.env.RIG_SERVER_RUN_ID?.trim();
6257
+ if (runId) {
6258
+ lines.push(`- Run: ${runId}`);
6259
+ }
6260
+ const closeout = defaultPrCloseoutLine(taskId, repoNameWithOwner);
6261
+ if (closeout) {
6262
+ lines.push(`- ${closeout}`);
6263
+ }
6264
+ return lines;
6265
+ }
6266
+ function defaultPrCloseoutLine(taskId, repoNameWithOwner) {
6267
+ const sourceIssueId = loadRuntimeContextFromEnv()?.sourceTask?.sourceIssueId;
6268
+ if (sourceIssueId) {
6269
+ const match = sourceIssueId.match(/^([^#]+)#(\d+)$/);
6270
+ if (match) {
6271
+ const [, sourceRepo, issueNumber] = match;
6272
+ return sourceRepo.toLowerCase() === repoNameWithOwner.toLowerCase() ? `Closes #${issueNumber}` : `Closes ${sourceRepo}#${issueNumber}`;
6273
+ }
6274
+ }
6275
+ return /^\d+$/.test(taskId) ? `Closes #${taskId}` : "";
6276
+ }
6252
6277
  function resolveTaskBranchRef(projectRoot, taskId) {
6253
6278
  return `rig/${resolveTaskBranchId(projectRoot, taskId)}`;
6254
6279
  }
@@ -6495,32 +6520,19 @@ function resolveGithubCliBinary(projectRoot) {
6495
6520
  if (explicit) {
6496
6521
  candidates.add(explicit);
6497
6522
  }
6523
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
6524
+ candidates.add(candidate);
6525
+ }
6498
6526
  const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
6499
6527
  for (const entry of explicitPathEntries) {
6500
6528
  candidates.add(resolve23(entry, "gh"));
6501
6529
  }
6502
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim();
6503
- if (hostProjectRoot) {
6504
- candidates.add(resolve23(resolveHostRigBinDir(hostProjectRoot), "gh"));
6505
- }
6506
- candidates.add(resolve23(resolveHostRigBinDir(projectRoot), "gh"));
6507
- const runtimeContext = loadRuntimeContextFromEnv();
6508
- if (runtimeContext?.binDir) {
6509
- candidates.add(resolve23(runtimeContext.binDir, "gh"));
6510
- }
6511
- const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
6512
- if (runtimeHome) {
6513
- candidates.add(resolve23(runtimeHome, "bin", "gh"));
6514
- }
6515
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
6516
- candidates.add(candidate);
6517
- }
6518
6530
  const bunResolved = Bun.which("gh");
6519
6531
  if (bunResolved) {
6520
6532
  candidates.add(bunResolved);
6521
6533
  }
6522
6534
  for (const candidate of candidates) {
6523
- if (candidate && existsSync20(candidate)) {
6535
+ if (candidate && existsSync20(candidate) && !isRuntimeGatewayGhPath(candidate)) {
6524
6536
  return candidate;
6525
6537
  }
6526
6538
  }
@@ -6669,6 +6681,32 @@ function withGhRepo(command, repoNameWithOwner) {
6669
6681
  }
6670
6682
  return [command[0], command[1], command[2], ...ghRepoArgs(repoNameWithOwner), ...command.slice(3)];
6671
6683
  }
6684
+ function inferRepositoryDefaultBase(projectRoot, repoRoot, repoNameWithOwner, remoteName, fallback) {
6685
+ const remote = remoteName || "origin";
6686
+ const symbolic = runCapture2(gitCmd(projectRoot, repoRoot, "symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`), projectRoot);
6687
+ if (symbolic.exitCode === 0) {
6688
+ const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp(remote)}/`), "");
6689
+ if (ref && ref !== "HEAD") {
6690
+ return ref;
6691
+ }
6692
+ }
6693
+ const lsRemote = runCapture2(gitCmd(projectRoot, repoRoot, "ls-remote", "--symref", remote, "HEAD"), projectRoot);
6694
+ if (lsRemote.exitCode === 0) {
6695
+ const match = lsRemote.stdout.match(/^ref:\s+refs\/heads\/([^\t\r\n]+)\s+HEAD/m);
6696
+ if (match?.[1]) {
6697
+ return match[1];
6698
+ }
6699
+ }
6700
+ const gh = resolveGithubCliBinary(projectRoot);
6701
+ if (gh && repoNameWithOwner) {
6702
+ const api = runCapture2(withGhRepo([gh, "repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"], repoNameWithOwner), repoRoot);
6703
+ const branch = api.exitCode === 0 ? api.stdout.trim() : "";
6704
+ if (branch) {
6705
+ return branch;
6706
+ }
6707
+ }
6708
+ return fallback;
6709
+ }
6672
6710
  function inferProjectBase(projectRoot, fallback) {
6673
6711
  const containing = runCapture2(gitCmd(projectRoot, projectRoot, "branch", "-r", "--contains", "HEAD"), projectRoot);
6674
6712
  if (containing.exitCode !== 0) {
@@ -7013,6 +7051,10 @@ function runtimeGitEnv(projectRoot) {
7013
7051
  }
7014
7052
  env[key] = value;
7015
7053
  }
7054
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
7055
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
7056
+ env.GITHUB_TOKEN = rigGithubToken;
7057
+ }
7016
7058
  if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
7017
7059
  env.GITHUB_TOKEN = env.GH_TOKEN;
7018
7060
  }
@@ -7036,6 +7078,13 @@ function runtimeGitEnv(projectRoot) {
7036
7078
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
7037
7079
  env.GH_TOKEN = env.GITHUB_TOKEN;
7038
7080
  }
7081
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
7082
+ if (gitHubToken) {
7083
+ env.RIG_GITHUB_TOKEN = gitHubToken;
7084
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
7085
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
7086
+ applyGitHubCredentialHelperEnv(env);
7087
+ }
7039
7088
  if (runtimeKnownHosts && existsSync20(runtimeKnownHosts)) {
7040
7089
  const sshParts = [
7041
7090
  "ssh",
@@ -7052,6 +7101,14 @@ function runtimeGitEnv(projectRoot) {
7052
7101
  }
7053
7102
  return Object.keys(env).length > 0 ? env : undefined;
7054
7103
  }
7104
+ function applyGitHubCredentialHelperEnv(env) {
7105
+ env.GIT_TERMINAL_PROMPT = "0";
7106
+ env.GIT_CONFIG_COUNT = "2";
7107
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
7108
+ env.GIT_CONFIG_VALUE_0 = "";
7109
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
7110
+ env.GIT_CONFIG_VALUE_1 = '!f() { test "$1" = get || exit 0; token="${GITHUB_TOKEN:-${GH_TOKEN:-${RIG_GITHUB_TOKEN:-}}}"; test -n "$token" || exit 0; echo username=x-access-token; echo password="$token"; }; f';
7111
+ }
7055
7112
  function loadPersistedRuntimeSecrets(runtimeRoot) {
7056
7113
  if (!runtimeRoot) {
7057
7114
  return {};
@@ -7493,7 +7550,7 @@ async function recordVerifierFailure(projectRoot, taskId, paths) {
7493
7550
  let attempts = 1;
7494
7551
  if (existsSync21(failedApproachesPath)) {
7495
7552
  const content = readFileSync12(failedApproachesPath, "utf-8");
7496
- attempts = (content.match(new RegExp(`^## ${escapeRegExp(taskId)}\\b`, "gm")) || []).length + 1;
7553
+ attempts = (content.match(new RegExp(`^## ${escapeRegExp2(taskId)}\\b`, "gm")) || []).length + 1;
7497
7554
  } else {
7498
7555
  mkdirSync11(resolve24(failedApproachesPath, ".."), { recursive: true });
7499
7556
  writeFileSync11(failedApproachesPath, `# Failed Approaches
@@ -2484,8 +2484,8 @@ function githubStatusFor(issue) {
2484
2484
  return "open";
2485
2485
  }
2486
2486
  function selectedGitHubEnv() {
2487
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
2488
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
2487
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
2488
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
2489
2489
  }
2490
2490
  function ghSpawnOptions() {
2491
2491
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -1706,8 +1706,8 @@ function githubStatusFor(issue) {
1706
1706
  return "open";
1707
1707
  }
1708
1708
  function selectedGitHubEnv() {
1709
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
1710
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
1709
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
1710
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
1711
1711
  }
1712
1712
  function ghSpawnOptions() {
1713
1713
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -4509,6 +4509,10 @@ function runtimeGitEnv(projectRoot) {
4509
4509
  }
4510
4510
  env[key] = value;
4511
4511
  }
4512
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
4513
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
4514
+ env.GITHUB_TOKEN = rigGithubToken;
4515
+ }
4512
4516
  if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
4513
4517
  env.GITHUB_TOKEN = env.GH_TOKEN;
4514
4518
  }
@@ -4532,6 +4536,13 @@ function runtimeGitEnv(projectRoot) {
4532
4536
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
4533
4537
  env.GH_TOKEN = env.GITHUB_TOKEN;
4534
4538
  }
4539
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
4540
+ if (gitHubToken) {
4541
+ env.RIG_GITHUB_TOKEN = gitHubToken;
4542
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
4543
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
4544
+ applyGitHubCredentialHelperEnv(env);
4545
+ }
4535
4546
  if (runtimeKnownHosts && existsSync22(runtimeKnownHosts)) {
4536
4547
  const sshParts = [
4537
4548
  "ssh",
@@ -4548,6 +4559,14 @@ function runtimeGitEnv(projectRoot) {
4548
4559
  }
4549
4560
  return Object.keys(env).length > 0 ? env : undefined;
4550
4561
  }
4562
+ function applyGitHubCredentialHelperEnv(env) {
4563
+ env.GIT_TERMINAL_PROMPT = "0";
4564
+ env.GIT_CONFIG_COUNT = "2";
4565
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
4566
+ env.GIT_CONFIG_VALUE_0 = "";
4567
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
4568
+ env.GIT_CONFIG_VALUE_1 = '!f() { test "$1" = get || exit 0; token="${GITHUB_TOKEN:-${GH_TOKEN:-${RIG_GITHUB_TOKEN:-}}}"; test -n "$token" || exit 0; echo username=x-access-token; echo password="$token"; }; f';
4569
+ }
4551
4570
  function loadPersistedRuntimeSecrets(runtimeRoot) {
4552
4571
  if (!runtimeRoot) {
4553
4572
  return {};
@@ -7603,7 +7622,11 @@ async function ensureAgentRuntime(options) {
7603
7622
  mkdirSync21(runtime.binDir, { recursive: true });
7604
7623
  mkdirSync21(workspaceLayout.distDir, { recursive: true });
7605
7624
  prepareRuntimeWorkspace(options.projectRoot, workspaceDir);
7606
- await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
7625
+ if (options.preserveTaskArtifacts) {
7626
+ console.log(`[rig-agent] Preserving runtime task artifacts for resume of ${options.taskId}.`);
7627
+ } else {
7628
+ await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
7629
+ }
7607
7630
  const ctx = {
7608
7631
  runtimeId: options.id,
7609
7632
  taskId: options.taskId,
@@ -1706,8 +1706,8 @@ function githubStatusFor(issue) {
1706
1706
  return "open";
1707
1707
  }
1708
1708
  function selectedGitHubEnv() {
1709
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
1710
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
1709
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
1710
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
1711
1711
  }
1712
1712
  function ghSpawnOptions() {
1713
1713
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -4509,6 +4509,10 @@ function runtimeGitEnv(projectRoot) {
4509
4509
  }
4510
4510
  env[key] = value;
4511
4511
  }
4512
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
4513
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
4514
+ env.GITHUB_TOKEN = rigGithubToken;
4515
+ }
4512
4516
  if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
4513
4517
  env.GITHUB_TOKEN = env.GH_TOKEN;
4514
4518
  }
@@ -4532,6 +4536,13 @@ function runtimeGitEnv(projectRoot) {
4532
4536
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
4533
4537
  env.GH_TOKEN = env.GITHUB_TOKEN;
4534
4538
  }
4539
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
4540
+ if (gitHubToken) {
4541
+ env.RIG_GITHUB_TOKEN = gitHubToken;
4542
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
4543
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
4544
+ applyGitHubCredentialHelperEnv(env);
4545
+ }
4535
4546
  if (runtimeKnownHosts && existsSync22(runtimeKnownHosts)) {
4536
4547
  const sshParts = [
4537
4548
  "ssh",
@@ -4548,6 +4559,14 @@ function runtimeGitEnv(projectRoot) {
4548
4559
  }
4549
4560
  return Object.keys(env).length > 0 ? env : undefined;
4550
4561
  }
4562
+ function applyGitHubCredentialHelperEnv(env) {
4563
+ env.GIT_TERMINAL_PROMPT = "0";
4564
+ env.GIT_CONFIG_COUNT = "2";
4565
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
4566
+ env.GIT_CONFIG_VALUE_0 = "";
4567
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
4568
+ env.GIT_CONFIG_VALUE_1 = '!f() { test "$1" = get || exit 0; token="${GITHUB_TOKEN:-${GH_TOKEN:-${RIG_GITHUB_TOKEN:-}}}"; test -n "$token" || exit 0; echo username=x-access-token; echo password="$token"; }; f';
4569
+ }
4551
4570
  function loadPersistedRuntimeSecrets(runtimeRoot) {
4552
4571
  if (!runtimeRoot) {
4553
4572
  return {};
@@ -7603,7 +7622,11 @@ async function ensureAgentRuntime(options) {
7603
7622
  mkdirSync21(runtime.binDir, { recursive: true });
7604
7623
  mkdirSync21(workspaceLayout.distDir, { recursive: true });
7605
7624
  prepareRuntimeWorkspace(options.projectRoot, workspaceDir);
7606
- await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
7625
+ if (options.preserveTaskArtifacts) {
7626
+ console.log(`[rig-agent] Preserving runtime task artifacts for resume of ${options.taskId}.`);
7627
+ } else {
7628
+ await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
7629
+ }
7607
7630
  const ctx = {
7608
7631
  runtimeId: options.id,
7609
7632
  taskId: options.taskId,