@h-rig/runtime 0.0.6-alpha.0 → 0.0.6-alpha.10

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 (42) hide show
  1. package/dist/bin/rig-agent-dispatch.js +133 -14
  2. package/dist/bin/rig-agent.js +83 -26
  3. package/dist/src/control-plane/agent-wrapper.js +133 -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 +88 -17
  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/dist/src/index.js +15 -13
  26. package/dist/src/local-server.js +20 -14
  27. package/native/darwin-arm64/{bin/rig-git → rig-git} +0 -0
  28. package/native/darwin-arm64/rig-git.build-manifest.json +4 -0
  29. package/native/darwin-arm64/{bin/rig-shell → rig-shell} +0 -0
  30. package/native/darwin-arm64/rig-shell.build-manifest.json +4 -0
  31. package/native/darwin-arm64/{bin/rig-tools → rig-tools} +0 -0
  32. package/native/darwin-arm64/rig-tools.build-manifest.json +4 -0
  33. package/native/darwin-arm64/{lib/runtime-native.dylib → runtime-native.dylib} +0 -0
  34. package/package.json +8 -7
  35. package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
  36. package/native/darwin-arm64/manifest.json +0 -1
  37. package/native/linux-x64/bin/rig-git +0 -0
  38. package/native/linux-x64/bin/rig-shell +0 -0
  39. package/native/linux-x64/bin/rig-tools +0 -0
  40. package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
  41. package/native/linux-x64/lib/runtime-native.so +0 -0
  42. package/native/linux-x64/manifest.json +0 -1
@@ -2951,6 +2951,17 @@ async function readSourceAwareTaskStatus(projectRoot, taskId, options = {}) {
2951
2951
  return null;
2952
2952
  }
2953
2953
  }
2954
+ function updateGithubIssueTaskBySourceIssueId(sourceIssueId, taskId, update, options = {}) {
2955
+ const parsed = sourceIssueId?.trim().match(/^([^/]+)\/([^#]+)#(\d+)$/);
2956
+ if (!parsed || parsed[3] !== taskId) {
2957
+ return false;
2958
+ }
2959
+ applyGithubIssueUpdate(options.ghBinary ?? "gh", options.spawn ?? spawnSync, parsed[3], {
2960
+ sourceIssueId: sourceIssueId.trim(),
2961
+ taskSource: { kind: "github-issues", owner: parsed[1], repo: parsed[2] }
2962
+ }, update);
2963
+ return true;
2964
+ }
2954
2965
  function updateSourceAwareTaskConfigTask(projectRoot, taskId, update, options = {}) {
2955
2966
  const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
2956
2967
  const rawEntry = readRawTaskEntry(configPath, taskId);
@@ -3306,8 +3317,8 @@ function ensureStatusLabel(bin, repo, spawnFn, label) {
3306
3317
  }
3307
3318
  }
3308
3319
  function selectedGitHubEnv() {
3309
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
3310
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
3320
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
3321
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
3311
3322
  }
3312
3323
  function ghSpawnOptions() {
3313
3324
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -5710,20 +5721,23 @@ function hashProjectPath(workspaceDir) {
5710
5721
  }
5711
5722
  function resolveGithubCliBinaryPath() {
5712
5723
  const explicit = process.env.RIG_GH_BIN?.trim();
5713
- if (explicit && existsSync23(explicit)) {
5724
+ if (explicit && existsSync23(explicit) && !isRuntimeGatewayGhPath(explicit)) {
5714
5725
  return explicit;
5715
5726
  }
5716
- const bunResolved = Bun.which("gh");
5717
- if (bunResolved && existsSync23(bunResolved)) {
5718
- return bunResolved;
5719
- }
5720
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
5727
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
5721
5728
  if (existsSync23(candidate)) {
5722
5729
  return candidate;
5723
5730
  }
5724
5731
  }
5732
+ const bunResolved = Bun.which("gh");
5733
+ if (bunResolved && existsSync23(bunResolved) && !isRuntimeGatewayGhPath(bunResolved)) {
5734
+ return bunResolved;
5735
+ }
5725
5736
  return "";
5726
5737
  }
5738
+ function isRuntimeGatewayGhPath(candidate) {
5739
+ return /\/\.rig\/bin\/gh$/.test(candidate.replace(/\\/g, "/"));
5740
+ }
5727
5741
  async function resolveGithubCliAuthToken(ghBinary = "") {
5728
5742
  const gh = ghBinary || resolveGithubCliBinaryPath();
5729
5743
  if (!gh) {
@@ -5824,6 +5838,8 @@ async function runtimeEnv(projectRoot, runtime) {
5824
5838
  XDG_CACHE_HOME: runtime.cacheDir,
5825
5839
  XDG_STATE_HOME: runtime.stateDir,
5826
5840
  RIG_AGENT_ID: runtime.id,
5841
+ ...process.env.RIG_RUN_ID?.trim() ? { RIG_RUN_ID: process.env.RIG_RUN_ID.trim() } : {},
5842
+ ...process.env.RIG_SERVER_RUN_ID?.trim() ? { RIG_SERVER_RUN_ID: process.env.RIG_SERVER_RUN_ID.trim() } : {},
5827
5843
  RIG_TASK_ID: runtime.taskId,
5828
5844
  RIG_TASK_RUNTIME_ID: runtime.id,
5829
5845
  RIG_TASK_WORKSPACE: runtime.workspaceDir,
@@ -5887,6 +5903,10 @@ async function runtimeEnv(projectRoot, runtime) {
5887
5903
  env[key] = value;
5888
5904
  }
5889
5905
  }
5906
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
5907
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
5908
+ env.GITHUB_TOKEN = rigGithubToken;
5909
+ }
5890
5910
  const fallbackGithubToken = !env.GITHUB_TOKEN && !env.GH_TOKEN ? await resolveGithubCliAuthToken(hostGhBinary) : "";
5891
5911
  if (fallbackGithubToken) {
5892
5912
  env.GITHUB_TOKEN = fallbackGithubToken;
@@ -5897,6 +5917,13 @@ async function runtimeEnv(projectRoot, runtime) {
5897
5917
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
5898
5918
  env.GH_TOKEN = env.GITHUB_TOKEN;
5899
5919
  }
5920
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || rigGithubToken;
5921
+ if (gitHubToken) {
5922
+ env.RIG_GITHUB_TOKEN = gitHubToken;
5923
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
5924
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
5925
+ applyGitHubCredentialHelperEnv(env);
5926
+ }
5900
5927
  if (!env.GREPTILE_GITHUB_TOKEN && env.GITHUB_TOKEN) {
5901
5928
  env.GREPTILE_GITHUB_TOKEN = env.GITHUB_TOKEN;
5902
5929
  }
@@ -5977,12 +6004,21 @@ async function materializeRuntimeCertBundle(runtime) {
5977
6004
  }
5978
6005
  return targetPath;
5979
6006
  }
6007
+ function applyGitHubCredentialHelperEnv(env) {
6008
+ env.GIT_TERMINAL_PROMPT = "0";
6009
+ env.GIT_CONFIG_COUNT = "2";
6010
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
6011
+ env.GIT_CONFIG_VALUE_0 = "";
6012
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
6013
+ 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';
6014
+ }
5980
6015
  function persistRuntimeSecrets(runtimeRoot, env) {
5981
6016
  const secretsPath = resolve25(runtimeRoot, "runtime-secrets.json");
5982
6017
  const persisted = {};
5983
6018
  for (const key of [
5984
6019
  "GITHUB_TOKEN",
5985
6020
  "GH_TOKEN",
6021
+ "RIG_GITHUB_TOKEN",
5986
6022
  "GREPTILE_GITHUB_TOKEN",
5987
6023
  "GREPTILE_API_KEY",
5988
6024
  "AI_REVIEW_MODE",
@@ -8461,7 +8497,11 @@ async function ensureAgentRuntime(options) {
8461
8497
  mkdirSync18(runtime.binDir, { recursive: true });
8462
8498
  mkdirSync18(workspaceLayout.distDir, { recursive: true });
8463
8499
  prepareRuntimeWorkspace(options.projectRoot, workspaceDir);
8464
- await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
8500
+ if (options.preserveTaskArtifacts) {
8501
+ console.log(`[rig-agent] Preserving runtime task artifacts for resume of ${options.taskId}.`);
8502
+ } else {
8503
+ await resetEphemeralTaskArtifacts(workspaceDir, options.taskId);
8504
+ }
8465
8505
  const ctx = {
8466
8506
  runtimeId: options.id,
8467
8507
  taskId: options.taskId,
@@ -9173,7 +9213,8 @@ async function runAgentWrapper(options = {}) {
9173
9213
  taskId,
9174
9214
  mode: "worktree",
9175
9215
  provider,
9176
- taskRecordReader: taskRecordReaderFromEnv(taskId)
9216
+ taskRecordReader: taskRecordReaderFromEnv(taskId),
9217
+ preserveTaskArtifacts: process.env.RIG_RUN_RESUME === "1" || process.env.RIG_RUNTIME_ARTIFACT_CLEANUP === "preserve"
9177
9218
  });
9178
9219
  emitWrapperEvent("runtime.provision.completed", {
9179
9220
  runtimeId: runtime.id,
@@ -9193,6 +9234,18 @@ async function runAgentWrapper(options = {}) {
9193
9234
  console.error(`[rig-agent] Failed to provision runtime: ${error}`);
9194
9235
  return 1;
9195
9236
  }
9237
+ try {
9238
+ await waitForDirtyBaselineReady(runtime, taskId);
9239
+ } catch (error) {
9240
+ emitWrapperEvent("runtime.baseline.failed", {
9241
+ runtimeId: runtime.id,
9242
+ taskId,
9243
+ workspaceDir: runtime.workspaceDir,
9244
+ error: error instanceof Error ? error.message : String(error)
9245
+ });
9246
+ console.error(`[rig-agent] Failed to apply selected baseline before provider launch: ${error}`);
9247
+ return 1;
9248
+ }
9196
9249
  const providerArgs = buildProviderArgs(provider, runtime, argv);
9197
9250
  const providerCommand = [providerBinary(provider), ...providerArgs];
9198
9251
  emitWrapperEvent("provider.launch", {
@@ -9324,12 +9377,16 @@ function buildProviderArgs(provider, runtime, argv) {
9324
9377
  }
9325
9378
  if (provider === "pi") {
9326
9379
  const piArgs = [...argv];
9380
+ const piProvider = cliOptionValue(piArgs, "--provider") || process.env.RIG_PI_PROVIDER?.trim() || "openai-codex";
9327
9381
  if (!hasCliOption(piArgs, "--provider")) {
9328
- piArgs.unshift(process.env.RIG_PI_PROVIDER?.trim() || "openai-codex");
9382
+ piArgs.unshift(piProvider);
9329
9383
  piArgs.unshift("--provider");
9330
9384
  }
9331
- if (!hasCliOption(piArgs, "--model")) {
9332
- piArgs.unshift(process.env.RIG_PI_MODEL?.trim() || "gpt-5.5");
9385
+ const model = cliOptionValue(piArgs, "--model") || process.env.RIG_PI_MODEL?.trim() || "gpt-5.5";
9386
+ if (hasCliOption(piArgs, "--model")) {
9387
+ rewriteCliOptionValue(piArgs, "--model", normalizePiModelForProvider(model, piProvider));
9388
+ } else {
9389
+ piArgs.unshift(normalizePiModelForProvider(model, piProvider));
9333
9390
  piArgs.unshift("--model");
9334
9391
  }
9335
9392
  return piArgs;
@@ -9397,6 +9454,38 @@ function resolveProvider() {
9397
9454
  function hasCliOption(argv, option) {
9398
9455
  return argv.some((arg) => arg === option || arg.startsWith(`${option}=`));
9399
9456
  }
9457
+ function cliOptionValue(argv, option) {
9458
+ for (let index = 0;index < argv.length; index += 1) {
9459
+ const arg = argv[index];
9460
+ if (arg === option) {
9461
+ const next = argv[index + 1];
9462
+ return next && !next.startsWith("--") ? next : undefined;
9463
+ }
9464
+ if (arg?.startsWith(`${option}=`)) {
9465
+ return arg.slice(option.length + 1);
9466
+ }
9467
+ }
9468
+ return;
9469
+ }
9470
+ function rewriteCliOptionValue(argv, option, value) {
9471
+ for (let index = 0;index < argv.length; index += 1) {
9472
+ const arg = argv[index];
9473
+ if (arg === option && argv[index + 1] && !argv[index + 1].startsWith("--")) {
9474
+ argv[index + 1] = value;
9475
+ return;
9476
+ }
9477
+ if (arg?.startsWith(`${option}=`)) {
9478
+ argv[index] = `${option}=${value}`;
9479
+ return;
9480
+ }
9481
+ }
9482
+ }
9483
+ function normalizePiModelForProvider(model, provider) {
9484
+ if (provider === "openrouter" && model === "openai-codex/gpt-5.5") {
9485
+ return "openai/gpt-5.5";
9486
+ }
9487
+ return model;
9488
+ }
9400
9489
  function providerBinary(provider) {
9401
9490
  if (provider === "codex") {
9402
9491
  return Bun.which("codex") || "codex";
@@ -9413,6 +9502,34 @@ function providerBinary(provider) {
9413
9502
  function emitWrapperEvent(type, payload) {
9414
9503
  console.log(`__RIG_WRAPPER_EVENT__${JSON.stringify({ type, payload, at: new Date().toISOString() })}`);
9415
9504
  }
9505
+ function sleep(ms) {
9506
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
9507
+ }
9508
+ async function waitForDirtyBaselineReady(runtime, taskId) {
9509
+ const readyFile = process.env.RIG_DIRTY_BASELINE_READY_FILE?.trim();
9510
+ if (process.env.RIG_BASELINE_MODE !== "dirty-snapshot" || !readyFile)
9511
+ return;
9512
+ const timeoutMs = Number.parseInt(process.env.RIG_DIRTY_BASELINE_TIMEOUT_MS ?? "30000", 10);
9513
+ const deadline = Date.now() + (Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 30000);
9514
+ emitWrapperEvent("runtime.baseline.waiting", {
9515
+ runtimeId: runtime.id,
9516
+ taskId,
9517
+ workspaceDir: runtime.workspaceDir,
9518
+ readyFile
9519
+ });
9520
+ while (!existsSync33(readyFile)) {
9521
+ if (Date.now() >= deadline) {
9522
+ throw new Error(`Timed out waiting for dirty baseline ready file: ${readyFile}`);
9523
+ }
9524
+ await sleep(50);
9525
+ }
9526
+ emitWrapperEvent("runtime.baseline.completed", {
9527
+ runtimeId: runtime.id,
9528
+ taskId,
9529
+ workspaceDir: runtime.workspaceDir,
9530
+ readyFile
9531
+ });
9532
+ }
9416
9533
  async function resolveTaskId(projectRoot, monorepoRoot) {
9417
9534
  if (process.env.RIG_TASK_ID) {
9418
9535
  return process.env.RIG_TASK_ID;
@@ -9488,7 +9605,9 @@ async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9488
9605
  } catch (error) {
9489
9606
  let fallbackUpdated = false;
9490
9607
  try {
9491
- fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, { status: "closed", comment });
9608
+ const sourceIssueId = loadRuntimeContextFromEnv()?.sourceTask?.sourceIssueId;
9609
+ fallbackUpdated = updateGithubIssueTaskBySourceIssueId(sourceIssueId, taskId, { status: "closed", comment });
9610
+ fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, { status: "closed", comment }) || fallbackUpdated;
9492
9611
  } catch (fallbackError) {
9493
9612
  console.error(`[rig-agent] Source-aware compatibility update also failed for ${taskId}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
9494
9613
  }
@@ -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 {};