@h-rig/runtime 0.0.6-alpha.3 → 0.0.6-alpha.5

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.
@@ -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,
@@ -8481,7 +8497,11 @@ async function ensureAgentRuntime(options) {
8481
8497
  mkdirSync18(runtime.binDir, { recursive: true });
8482
8498
  mkdirSync18(workspaceLayout.distDir, { recursive: true });
8483
8499
  prepareRuntimeWorkspace(options.projectRoot, workspaceDir);
8484
- 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
+ }
8485
8505
  const ctx = {
8486
8506
  runtimeId: options.id,
8487
8507
  taskId: options.taskId,
@@ -9193,7 +9213,8 @@ async function runAgentWrapper(options = {}) {
9193
9213
  taskId,
9194
9214
  mode: "worktree",
9195
9215
  provider,
9196
- taskRecordReader: taskRecordReaderFromEnv(taskId)
9216
+ taskRecordReader: taskRecordReaderFromEnv(taskId),
9217
+ preserveTaskArtifacts: process.env.RIG_RUN_RESUME === "1" || process.env.RIG_RUNTIME_ARTIFACT_CLEANUP === "preserve"
9197
9218
  });
9198
9219
  emitWrapperEvent("runtime.provision.completed", {
9199
9220
  runtimeId: runtime.id,
@@ -9356,12 +9377,16 @@ function buildProviderArgs(provider, runtime, argv) {
9356
9377
  }
9357
9378
  if (provider === "pi") {
9358
9379
  const piArgs = [...argv];
9380
+ const piProvider = cliOptionValue(piArgs, "--provider") || process.env.RIG_PI_PROVIDER?.trim() || "openai-codex";
9359
9381
  if (!hasCliOption(piArgs, "--provider")) {
9360
- piArgs.unshift(process.env.RIG_PI_PROVIDER?.trim() || "openai-codex");
9382
+ piArgs.unshift(piProvider);
9361
9383
  piArgs.unshift("--provider");
9362
9384
  }
9363
- if (!hasCliOption(piArgs, "--model")) {
9364
- 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));
9365
9390
  piArgs.unshift("--model");
9366
9391
  }
9367
9392
  return piArgs;
@@ -9429,6 +9454,38 @@ function resolveProvider() {
9429
9454
  function hasCliOption(argv, option) {
9430
9455
  return argv.some((arg) => arg === option || arg.startsWith(`${option}=`));
9431
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
+ }
9432
9489
  function providerBinary(provider) {
9433
9490
  if (provider === "codex") {
9434
9491
  return Bun.which("codex") || "codex";
@@ -9548,7 +9605,9 @@ async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9548
9605
  } catch (error) {
9549
9606
  let fallbackUpdated = false;
9550
9607
  try {
9551
- 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;
9552
9611
  } catch (fallbackError) {
9553
9612
  console.error(`[rig-agent] Source-aware compatibility update also failed for ${taskId}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
9554
9613
  }
@@ -4421,8 +4421,8 @@ function githubStatusFor(issue) {
4421
4421
  return "open";
4422
4422
  }
4423
4423
  function selectedGitHubEnv() {
4424
- const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
4425
- return { GH_TOKEN: token, GITHUB_TOKEN: token };
4424
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() || process.env.RIG_GITHUB_TOKEN?.trim() || "";
4425
+ return { GH_TOKEN: token, GITHUB_TOKEN: token, RIG_GITHUB_TOKEN: token };
4426
4426
  }
4427
4427
  function ghSpawnOptions() {
4428
4428
  return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
@@ -6389,7 +6389,7 @@ This file records approaches that did not work.
6389
6389
  `, "utf-8");
6390
6390
  }
6391
6391
  const content = readFileSync12(failedPath, "utf-8");
6392
- const attempts = (content.match(new RegExp(`^## ${escapeRegExp2(activeTask)}\\b`, "gm")) || []).length + 1;
6392
+ const attempts = (content.match(new RegExp(`^## ${escapeRegExp(activeTask)}\\b`, "gm")) || []).length + 1;
6393
6393
  appendFileSync(failedPath, `
6394
6394
  ## ${activeTask} - Attempt ${attempts} (${nowIso()})
6395
6395
 
@@ -6885,7 +6885,7 @@ function printArtifactSection(path, header) {
6885
6885
  process.stdout.write(readFileSync12(path, "utf-8"));
6886
6886
  console.log("");
6887
6887
  }
6888
- function escapeRegExp2(value) {
6888
+ function escapeRegExp(value) {
6889
6889
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6890
6890
  }
6891
6891
  function changedFilesForTask(projectRoot, taskId, scoped) {
@@ -6914,12 +6914,12 @@ var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
6914
6914
  "task-result.json",
6915
6915
  "validation-summary.json"
6916
6916
  ]);
6917
- function resolveHostRigBinDir(root) {
6918
- return resolve26(root, ".rig", "bin");
6919
- }
6920
6917
  function isRuntimeGatewayGitPath(candidate) {
6921
6918
  return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
6922
6919
  }
6920
+ function isRuntimeGatewayGhPath(candidate) {
6921
+ return /\/\.rig\/bin\/gh$/.test(candidate.replace(/\\/g, "/"));
6922
+ }
6923
6923
  function resolveOptionalMonorepoRoot(projectRoot) {
6924
6924
  const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
6925
6925
  if (runtimeWorkspace && existsSync22(resolve26(runtimeWorkspace, ".git"))) {
@@ -6954,6 +6954,9 @@ function resolveGitBinary(projectRoot) {
6954
6954
  }
6955
6955
  return "git";
6956
6956
  }
6957
+ function escapeRegExp2(value) {
6958
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6959
+ }
6957
6960
  function safeCurrentTaskId(projectRoot) {
6958
6961
  try {
6959
6962
  const taskId = currentTaskId(projectRoot);
@@ -7167,6 +7170,7 @@ function gitOpenPr(options) {
7167
7170
  "",
7168
7171
  "## Task",
7169
7172
  `- beads: ${taskId || "n/a"}`,
7173
+ ...defaultPrRunLines(taskId, repoNameWithOwner),
7170
7174
  "",
7171
7175
  "## Review",
7172
7176
  "- Completion verification will run validation, verifier review, and PR policy checks.",
@@ -7258,6 +7262,29 @@ function gitOpenPr(options) {
7258
7262
  }
7259
7263
  return result;
7260
7264
  }
7265
+ function defaultPrRunLines(taskId, repoNameWithOwner) {
7266
+ const lines = [];
7267
+ const runId = process.env.RIG_SERVER_RUN_ID?.trim();
7268
+ if (runId) {
7269
+ lines.push(`- Run: ${runId}`);
7270
+ }
7271
+ const closeout = defaultPrCloseoutLine(taskId, repoNameWithOwner);
7272
+ if (closeout) {
7273
+ lines.push(`- ${closeout}`);
7274
+ }
7275
+ return lines;
7276
+ }
7277
+ function defaultPrCloseoutLine(taskId, repoNameWithOwner) {
7278
+ const sourceIssueId = loadRuntimeContextFromEnv()?.sourceTask?.sourceIssueId;
7279
+ if (sourceIssueId) {
7280
+ const match = sourceIssueId.match(/^([^#]+)#(\d+)$/);
7281
+ if (match) {
7282
+ const [, sourceRepo, issueNumber] = match;
7283
+ return sourceRepo.toLowerCase() === repoNameWithOwner.toLowerCase() ? `Closes #${issueNumber}` : `Closes ${sourceRepo}#${issueNumber}`;
7284
+ }
7285
+ }
7286
+ return /^\d+$/.test(taskId) ? `Closes #${taskId}` : "";
7287
+ }
7261
7288
  function readPrViewState(gh, repoRoot, repoNameWithOwner, prUrl) {
7262
7289
  const view = runCapture2(withGhRepo([
7263
7290
  gh,
@@ -7383,32 +7410,19 @@ function resolveGithubCliBinary(projectRoot) {
7383
7410
  if (explicit) {
7384
7411
  candidates.add(explicit);
7385
7412
  }
7413
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
7414
+ candidates.add(candidate);
7415
+ }
7386
7416
  const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
7387
7417
  for (const entry of explicitPathEntries) {
7388
7418
  candidates.add(resolve26(entry, "gh"));
7389
7419
  }
7390
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim();
7391
- if (hostProjectRoot) {
7392
- candidates.add(resolve26(resolveHostRigBinDir(hostProjectRoot), "gh"));
7393
- }
7394
- candidates.add(resolve26(resolveHostRigBinDir(projectRoot), "gh"));
7395
- const runtimeContext = loadRuntimeContextFromEnv();
7396
- if (runtimeContext?.binDir) {
7397
- candidates.add(resolve26(runtimeContext.binDir, "gh"));
7398
- }
7399
- const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
7400
- if (runtimeHome) {
7401
- candidates.add(resolve26(runtimeHome, "bin", "gh"));
7402
- }
7403
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
7404
- candidates.add(candidate);
7405
- }
7406
7420
  const bunResolved = Bun.which("gh");
7407
7421
  if (bunResolved) {
7408
7422
  candidates.add(bunResolved);
7409
7423
  }
7410
7424
  for (const candidate of candidates) {
7411
- if (candidate && existsSync22(candidate)) {
7425
+ if (candidate && existsSync22(candidate) && !isRuntimeGatewayGhPath(candidate)) {
7412
7426
  return candidate;
7413
7427
  }
7414
7428
  }
@@ -7561,7 +7575,7 @@ function inferRepositoryDefaultBase(projectRoot, repoRoot, repoNameWithOwner, re
7561
7575
  const remote = remoteName || "origin";
7562
7576
  const symbolic = runCapture2(gitCmd(projectRoot, repoRoot, "symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`), projectRoot);
7563
7577
  if (symbolic.exitCode === 0) {
7564
- const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp(remote)}/`), "");
7578
+ const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp2(remote)}/`), "");
7565
7579
  if (ref && ref !== "HEAD") {
7566
7580
  return ref;
7567
7581
  }
@@ -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,
@@ -8481,7 +8497,11 @@ async function ensureAgentRuntime(options) {
8481
8497
  mkdirSync18(runtime.binDir, { recursive: true });
8482
8498
  mkdirSync18(workspaceLayout.distDir, { recursive: true });
8483
8499
  prepareRuntimeWorkspace(options.projectRoot, workspaceDir);
8484
- 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
+ }
8485
8505
  const ctx = {
8486
8506
  runtimeId: options.id,
8487
8507
  taskId: options.taskId,
@@ -9193,7 +9213,8 @@ async function runAgentWrapper(options = {}) {
9193
9213
  taskId,
9194
9214
  mode: "worktree",
9195
9215
  provider,
9196
- taskRecordReader: taskRecordReaderFromEnv(taskId)
9216
+ taskRecordReader: taskRecordReaderFromEnv(taskId),
9217
+ preserveTaskArtifacts: process.env.RIG_RUN_RESUME === "1" || process.env.RIG_RUNTIME_ARTIFACT_CLEANUP === "preserve"
9197
9218
  });
9198
9219
  emitWrapperEvent("runtime.provision.completed", {
9199
9220
  runtimeId: runtime.id,
@@ -9356,12 +9377,16 @@ function buildProviderArgs(provider, runtime, argv) {
9356
9377
  }
9357
9378
  if (provider === "pi") {
9358
9379
  const piArgs = [...argv];
9380
+ const piProvider = cliOptionValue(piArgs, "--provider") || process.env.RIG_PI_PROVIDER?.trim() || "openai-codex";
9359
9381
  if (!hasCliOption(piArgs, "--provider")) {
9360
- piArgs.unshift(process.env.RIG_PI_PROVIDER?.trim() || "openai-codex");
9382
+ piArgs.unshift(piProvider);
9361
9383
  piArgs.unshift("--provider");
9362
9384
  }
9363
- if (!hasCliOption(piArgs, "--model")) {
9364
- 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));
9365
9390
  piArgs.unshift("--model");
9366
9391
  }
9367
9392
  return piArgs;
@@ -9429,6 +9454,38 @@ function resolveProvider() {
9429
9454
  function hasCliOption(argv, option) {
9430
9455
  return argv.some((arg) => arg === option || arg.startsWith(`${option}=`));
9431
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
+ }
9432
9489
  function providerBinary(provider) {
9433
9490
  if (provider === "codex") {
9434
9491
  return Bun.which("codex") || "codex";
@@ -9548,7 +9605,9 @@ async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9548
9605
  } catch (error) {
9549
9606
  let fallbackUpdated = false;
9550
9607
  try {
9551
- 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;
9552
9611
  } catch (fallbackError) {
9553
9612
  console.error(`[rig-agent] Source-aware compatibility update also failed for ${taskId}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
9554
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() } };
@@ -5461,7 +5461,7 @@ This file records approaches that did not work.
5461
5461
  `, "utf-8");
5462
5462
  }
5463
5463
  const content = readFileSync10(failedPath, "utf-8");
5464
- const attempts = (content.match(new RegExp(`^## ${escapeRegExp2(activeTask)}\\b`, "gm")) || []).length + 1;
5464
+ const attempts = (content.match(new RegExp(`^## ${escapeRegExp(activeTask)}\\b`, "gm")) || []).length + 1;
5465
5465
  appendFileSync(failedPath, `
5466
5466
  ## ${activeTask} - Attempt ${attempts} (${nowIso()})
5467
5467
 
@@ -5960,7 +5960,7 @@ function printArtifactSection(path, header) {
5960
5960
  process.stdout.write(readFileSync10(path, "utf-8"));
5961
5961
  console.log("");
5962
5962
  }
5963
- function escapeRegExp2(value) {
5963
+ function escapeRegExp(value) {
5964
5964
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5965
5965
  }
5966
5966
  function changedFilesForTask(projectRoot, taskId, scoped) {
@@ -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);
@@ -6242,6 +6245,7 @@ function gitOpenPr(options) {
6242
6245
  "",
6243
6246
  "## Task",
6244
6247
  `- beads: ${taskId || "n/a"}`,
6248
+ ...defaultPrRunLines(taskId, repoNameWithOwner),
6245
6249
  "",
6246
6250
  "## Review",
6247
6251
  "- Completion verification will run validation, verifier review, and PR policy checks.",
@@ -6333,6 +6337,29 @@ function gitOpenPr(options) {
6333
6337
  }
6334
6338
  return result;
6335
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
+ }
6336
6363
  function readPrViewState(gh, repoRoot, repoNameWithOwner, prUrl) {
6337
6364
  const view = runCapture2(withGhRepo([
6338
6365
  gh,
@@ -6483,32 +6510,19 @@ function resolveGithubCliBinary(projectRoot) {
6483
6510
  if (explicit) {
6484
6511
  candidates.add(explicit);
6485
6512
  }
6513
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
6514
+ candidates.add(candidate);
6515
+ }
6486
6516
  const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
6487
6517
  for (const entry of explicitPathEntries) {
6488
6518
  candidates.add(resolve24(entry, "gh"));
6489
6519
  }
6490
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim();
6491
- if (hostProjectRoot) {
6492
- candidates.add(resolve24(resolveHostRigBinDir(hostProjectRoot), "gh"));
6493
- }
6494
- candidates.add(resolve24(resolveHostRigBinDir(projectRoot), "gh"));
6495
- const runtimeContext = loadRuntimeContextFromEnv();
6496
- if (runtimeContext?.binDir) {
6497
- candidates.add(resolve24(runtimeContext.binDir, "gh"));
6498
- }
6499
- const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
6500
- if (runtimeHome) {
6501
- candidates.add(resolve24(runtimeHome, "bin", "gh"));
6502
- }
6503
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
6504
- candidates.add(candidate);
6505
- }
6506
6520
  const bunResolved = Bun.which("gh");
6507
6521
  if (bunResolved) {
6508
6522
  candidates.add(bunResolved);
6509
6523
  }
6510
6524
  for (const candidate of candidates) {
6511
- if (candidate && existsSync21(candidate)) {
6525
+ if (candidate && existsSync21(candidate) && !isRuntimeGatewayGhPath(candidate)) {
6512
6526
  return candidate;
6513
6527
  }
6514
6528
  }
@@ -6661,7 +6675,7 @@ function inferRepositoryDefaultBase(projectRoot, repoRoot, repoNameWithOwner, re
6661
6675
  const remote = remoteName || "origin";
6662
6676
  const symbolic = runCapture2(gitCmd(projectRoot, repoRoot, "symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`), projectRoot);
6663
6677
  if (symbolic.exitCode === 0) {
6664
- const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp(remote)}/`), "");
6678
+ const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp2(remote)}/`), "");
6665
6679
  if (ref && ref !== "HEAD") {
6666
6680
  return ref;
6667
6681
  }