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

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 (46) hide show
  1. package/dist/bin/rig-agent-dispatch.js +84 -313
  2. package/dist/bin/rig-agent.js +85 -27
  3. package/dist/src/control-plane/agent-wrapper.js +101 -27
  4. package/dist/src/control-plane/authority-files.js +12 -6
  5. package/dist/src/control-plane/harness-main.js +1357 -180
  6. package/dist/src/control-plane/hooks/completion-verification.js +1669 -329
  7. package/dist/src/control-plane/hooks/inject-context.js +2 -2
  8. package/dist/src/control-plane/hooks/submodule-branch.js +26 -3
  9. package/dist/src/control-plane/hooks/task-runtime-start.js +26 -3
  10. package/dist/src/control-plane/native/git-ops.js +134 -68
  11. package/dist/src/control-plane/native/harness-cli.js +1357 -180
  12. package/dist/src/control-plane/native/pr-automation.js +1532 -54
  13. package/dist/src/control-plane/native/pr-review-gate.js +1330 -0
  14. package/dist/src/control-plane/native/run-ops.js +35 -12
  15. package/dist/src/control-plane/native/task-ops.js +1274 -155
  16. package/dist/src/control-plane/native/validator.js +2 -2
  17. package/dist/src/control-plane/native/verifier.js +1274 -154
  18. package/dist/src/control-plane/native/workspace-ops.js +12 -6
  19. package/dist/src/control-plane/runtime/index.js +38 -9
  20. package/dist/src/control-plane/runtime/isolation/home.js +31 -6
  21. package/dist/src/control-plane/runtime/isolation/index.js +38 -9
  22. package/dist/src/control-plane/runtime/isolation/runner.js +31 -6
  23. package/dist/src/control-plane/runtime/isolation/shared.js +9 -6
  24. package/dist/src/control-plane/runtime/isolation.js +38 -9
  25. package/dist/src/control-plane/runtime/queue.js +38 -9
  26. package/dist/src/control-plane/tasks/source-aware-task-config-source.js +14 -2
  27. package/dist/src/control-plane/tasks/source-lifecycle.js +2 -2
  28. package/dist/src/index.js +27 -20
  29. package/dist/src/layout.js +12 -7
  30. package/dist/src/local-server.js +20 -14
  31. package/native/darwin-arm64/{bin/rig-git → rig-git} +0 -0
  32. package/native/darwin-arm64/rig-git.build-manifest.json +4 -0
  33. package/native/darwin-arm64/{bin/rig-shell → rig-shell} +0 -0
  34. package/native/darwin-arm64/rig-shell.build-manifest.json +4 -0
  35. package/native/darwin-arm64/{bin/rig-tools → rig-tools} +0 -0
  36. package/native/darwin-arm64/rig-tools.build-manifest.json +4 -0
  37. package/native/darwin-arm64/{lib/runtime-native.dylib → runtime-native.dylib} +0 -0
  38. package/package.json +6 -6
  39. package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
  40. package/native/darwin-arm64/manifest.json +0 -1
  41. package/native/linux-x64/bin/rig-git +0 -0
  42. package/native/linux-x64/bin/rig-shell +0 -0
  43. package/native/linux-x64/bin/rig-tools +0 -0
  44. package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
  45. package/native/linux-x64/lib/runtime-native.so +0 -0
  46. package/native/linux-x64/manifest.json +0 -1
@@ -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() } };
@@ -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);
@@ -7112,17 +7115,15 @@ function gitOpenPr(options) {
7112
7115
  const target = options.target || (taskId ? "monorepo" : "project");
7113
7116
  let repoRoot = options.projectRoot;
7114
7117
  let repoLabel = "project-rig";
7115
- let defaultBase = process.env.RIG_PR_BASE_PROJECT || "main";
7118
+ const envBase = target === "monorepo" ? process.env.RIG_PR_BASE_MONOREPO?.trim() || "" : process.env.RIG_PR_BASE_PROJECT?.trim() || "";
7116
7119
  if (target === "monorepo") {
7117
7120
  repoRoot = resolveOptionalMonorepoRoot(options.projectRoot) || resolveMonorepoRoot2(options.projectRoot);
7118
7121
  repoLabel = "monorepo";
7119
- defaultBase = process.env.RIG_PR_BASE_MONOREPO || "main";
7120
7122
  if (taskId) {
7121
7123
  gitSyncBranch(options.projectRoot, taskId, "monorepo");
7122
7124
  }
7123
7125
  } else if (taskId) {
7124
7126
  gitSyncBranch(options.projectRoot, taskId, "project");
7125
- defaultBase = inferProjectBase(options.projectRoot, defaultBase);
7126
7127
  }
7127
7128
  if (!existsSync22(resolve26(repoRoot, ".git"))) {
7128
7129
  throw new Error(`Repository not available for open-pr target ${target}: ${repoRoot}`);
@@ -7131,9 +7132,9 @@ function gitOpenPr(options) {
7131
7132
  if (!branch || branch === "HEAD") {
7132
7133
  throw new Error(`Cannot open PR from detached HEAD in ${repoLabel}. Checkout a branch first.`);
7133
7134
  }
7134
- const base = options.base || defaultBase;
7135
7135
  const repoNameWithOwner = resolveRepoNameWithOwner(options.projectRoot, repoRoot);
7136
7136
  const networkRemote = resolveNetworkRemoteName(options.projectRoot, repoRoot, repoNameWithOwner);
7137
+ const base = options.base || envBase || inferRepositoryDefaultBase(options.projectRoot, repoRoot, repoNameWithOwner, networkRemote, target === "project" ? inferProjectBase(options.projectRoot, "main") : "main");
7137
7138
  refreshRemoteBaseRef(options.projectRoot, repoRoot, base);
7138
7139
  let reviewer = (options.reviewer || "").trim();
7139
7140
  let reviewerSource = reviewer ? "flag" : undefined;
@@ -7169,10 +7170,11 @@ function gitOpenPr(options) {
7169
7170
  "",
7170
7171
  "## Task",
7171
7172
  `- beads: ${taskId || "n/a"}`,
7173
+ ...defaultPrRunLines(taskId, repoNameWithOwner),
7172
7174
  "",
7173
7175
  "## Review",
7174
7176
  "- Completion verification will run validation, verifier review, and PR policy checks.",
7175
- "- When repository policy allows it, Rig enables GitHub auto-merge after approval."
7177
+ "- When repository policy allows it, Rig attempts an immediate strict-gated, head-locked merge after approval."
7176
7178
  ].join(`
7177
7179
  `);
7178
7180
  const preCheck = runCapture2(withGhRepo([gh, "pr", "list", "--state", "merged", "--head", branch, "--json", "url,mergedAt", "--jq", ".[0]"], repoNameWithOwner), repoRoot);
@@ -7260,6 +7262,30 @@ function gitOpenPr(options) {
7260
7262
  }
7261
7263
  return result;
7262
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?.[1] && match[2]) {
7282
+ const sourceRepo = match[1];
7283
+ const issueNumber = match[2];
7284
+ return sourceRepo.toLowerCase() === repoNameWithOwner.toLowerCase() ? `Closes #${issueNumber}` : `Closes ${sourceRepo}#${issueNumber}`;
7285
+ }
7286
+ }
7287
+ return /^\d+$/.test(taskId) ? `Closes #${taskId}` : "";
7288
+ }
7263
7289
  function readPrViewState(gh, repoRoot, repoNameWithOwner, prUrl) {
7264
7290
  const view = runCapture2(withGhRepo([
7265
7291
  gh,
@@ -7385,32 +7411,19 @@ function resolveGithubCliBinary(projectRoot) {
7385
7411
  if (explicit) {
7386
7412
  candidates.add(explicit);
7387
7413
  }
7414
+ for (const candidate of ["/usr/bin/gh", "/opt/homebrew/bin/gh", "/usr/local/bin/gh"]) {
7415
+ candidates.add(candidate);
7416
+ }
7388
7417
  const explicitPathEntries = (process.env.PATH || "").split(":").map((entry) => entry.trim()).filter(Boolean);
7389
7418
  for (const entry of explicitPathEntries) {
7390
7419
  candidates.add(resolve26(entry, "gh"));
7391
7420
  }
7392
- const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim();
7393
- if (hostProjectRoot) {
7394
- candidates.add(resolve26(resolveHostRigBinDir(hostProjectRoot), "gh"));
7395
- }
7396
- candidates.add(resolve26(resolveHostRigBinDir(projectRoot), "gh"));
7397
- const runtimeContext = loadRuntimeContextFromEnv();
7398
- if (runtimeContext?.binDir) {
7399
- candidates.add(resolve26(runtimeContext.binDir, "gh"));
7400
- }
7401
- const runtimeHome = process.env.RIG_RUNTIME_HOME?.trim();
7402
- if (runtimeHome) {
7403
- candidates.add(resolve26(runtimeHome, "bin", "gh"));
7404
- }
7405
- for (const candidate of ["/opt/homebrew/bin/gh", "/usr/local/bin/gh", "/usr/bin/gh"]) {
7406
- candidates.add(candidate);
7407
- }
7408
7421
  const bunResolved = Bun.which("gh");
7409
7422
  if (bunResolved) {
7410
7423
  candidates.add(bunResolved);
7411
7424
  }
7412
7425
  for (const candidate of candidates) {
7413
- if (candidate && existsSync22(candidate)) {
7426
+ if (candidate && existsSync22(candidate) && !isRuntimeGatewayGhPath(candidate)) {
7414
7427
  return candidate;
7415
7428
  }
7416
7429
  }
@@ -7559,6 +7572,32 @@ function withGhRepo(command, repoNameWithOwner) {
7559
7572
  }
7560
7573
  return [command[0], command[1], command[2], ...ghRepoArgs(repoNameWithOwner), ...command.slice(3)];
7561
7574
  }
7575
+ function inferRepositoryDefaultBase(projectRoot, repoRoot, repoNameWithOwner, remoteName, fallback) {
7576
+ const remote = remoteName || "origin";
7577
+ const symbolic = runCapture2(gitCmd(projectRoot, repoRoot, "symbolic-ref", "--short", `refs/remotes/${remote}/HEAD`), projectRoot);
7578
+ if (symbolic.exitCode === 0) {
7579
+ const ref = symbolic.stdout.trim().replace(new RegExp(`^${escapeRegExp2(remote)}/`), "");
7580
+ if (ref && ref !== "HEAD") {
7581
+ return ref;
7582
+ }
7583
+ }
7584
+ const lsRemote = runCapture2(gitCmd(projectRoot, repoRoot, "ls-remote", "--symref", remote, "HEAD"), projectRoot);
7585
+ if (lsRemote.exitCode === 0) {
7586
+ const match = lsRemote.stdout.match(/^ref:\s+refs\/heads\/([^\t\r\n]+)\s+HEAD/m);
7587
+ if (match?.[1]) {
7588
+ return match[1];
7589
+ }
7590
+ }
7591
+ const gh = resolveGithubCliBinary(projectRoot);
7592
+ if (gh && repoNameWithOwner) {
7593
+ const api = runCapture2(withGhRepo([gh, "repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"], repoNameWithOwner), repoRoot);
7594
+ const branch = api.exitCode === 0 ? api.stdout.trim() : "";
7595
+ if (branch) {
7596
+ return branch;
7597
+ }
7598
+ }
7599
+ return fallback;
7600
+ }
7562
7601
  function inferProjectBase(projectRoot, fallback) {
7563
7602
  const containing = runCapture2(gitCmd(projectRoot, projectRoot, "branch", "-r", "--contains", "HEAD"), projectRoot);
7564
7603
  if (containing.exitCode !== 0) {
@@ -7940,6 +7979,10 @@ function runtimeGitEnv(projectRoot) {
7940
7979
  }
7941
7980
  env[key] = value;
7942
7981
  }
7982
+ const rigGithubToken = process.env.RIG_GITHUB_TOKEN?.trim() || "";
7983
+ if (rigGithubToken && !env.GITHUB_TOKEN && !env.GH_TOKEN) {
7984
+ env.GITHUB_TOKEN = rigGithubToken;
7985
+ }
7943
7986
  if (!env.GITHUB_TOKEN && env.GH_TOKEN) {
7944
7987
  env.GITHUB_TOKEN = env.GH_TOKEN;
7945
7988
  }
@@ -7963,6 +8006,13 @@ function runtimeGitEnv(projectRoot) {
7963
8006
  if (!env.GH_TOKEN && env.GITHUB_TOKEN) {
7964
8007
  env.GH_TOKEN = env.GITHUB_TOKEN;
7965
8008
  }
8009
+ const gitHubToken = env.GITHUB_TOKEN || env.GH_TOKEN || env.RIG_GITHUB_TOKEN || rigGithubToken;
8010
+ if (gitHubToken) {
8011
+ env.RIG_GITHUB_TOKEN = gitHubToken;
8012
+ env.GITHUB_TOKEN = env.GITHUB_TOKEN || gitHubToken;
8013
+ env.GH_TOKEN = env.GH_TOKEN || gitHubToken;
8014
+ applyGitHubCredentialHelperEnv(env);
8015
+ }
7966
8016
  if (runtimeKnownHosts && existsSync22(runtimeKnownHosts)) {
7967
8017
  const sshParts = [
7968
8018
  "ssh",
@@ -7979,6 +8029,14 @@ function runtimeGitEnv(projectRoot) {
7979
8029
  }
7980
8030
  return Object.keys(env).length > 0 ? env : undefined;
7981
8031
  }
8032
+ function applyGitHubCredentialHelperEnv(env) {
8033
+ env.GIT_TERMINAL_PROMPT = "0";
8034
+ env.GIT_CONFIG_COUNT = "2";
8035
+ env.GIT_CONFIG_KEY_0 = "credential.helper";
8036
+ env.GIT_CONFIG_VALUE_0 = "";
8037
+ env.GIT_CONFIG_KEY_1 = "credential.helper";
8038
+ 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';
8039
+ }
7982
8040
  function loadPersistedRuntimeSecrets(runtimeRoot) {
7983
8041
  if (!runtimeRoot) {
7984
8042
  return {};
@@ -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,
@@ -9278,15 +9319,13 @@ async function runAgentWrapper(options = {}) {
9278
9319
  throw error;
9279
9320
  }
9280
9321
  const serverManagedRun = Boolean(process.env.RIG_SERVER_RUN_ID?.trim());
9281
- if (exitCode === 0 && serverManagedRun) {
9282
- await updateTaskSourceAfterRun(monorepoRoot, taskId, runtime);
9283
- }
9284
9322
  const taskClosed = await isTaskClosed(monorepoRoot, taskId);
9285
9323
  const finalExitCode = resolveFinalProviderExitCode({
9286
9324
  providerExitCode: exitCode,
9287
9325
  taskClosed,
9288
9326
  serverManagedRun
9289
9327
  });
9328
+ const handoffRequired = exitCode !== 0 || !taskClosed && !serverManagedRun;
9290
9329
  emitWrapperEvent("provider.completed", {
9291
9330
  provider,
9292
9331
  runtimeId: runtime.id,
@@ -9295,24 +9334,21 @@ async function runAgentWrapper(options = {}) {
9295
9334
  providerExitCode: exitCode,
9296
9335
  taskClosed,
9297
9336
  serverManagedRun,
9298
- handoffRequired: exitCode !== 0 || !taskClosed
9337
+ handoffRequired
9299
9338
  });
9300
- if (exitCode !== 0 || !taskClosed) {
9339
+ if (handoffRequired) {
9301
9340
  recordRuntimeHandoff(projectRoot, runtime, taskId, exitCode);
9302
9341
  }
9303
- if (exitCode === 0 && !taskClosed && serverManagedRun) {
9304
- console.error(`[rig-agent] Server-managed task run cannot finish successfully while ${taskId} is still open.`);
9305
- }
9306
9342
  return finalExitCode;
9307
9343
  }
9308
9344
  function resolveFinalProviderExitCode(input) {
9309
9345
  if (input.providerExitCode !== 0) {
9310
9346
  return input.providerExitCode;
9311
9347
  }
9312
- if (input.taskClosed) {
9348
+ if (input.taskClosed || input.serverManagedRun) {
9313
9349
  return 0;
9314
9350
  }
9315
- return input.serverManagedRun ? 2 : 0;
9351
+ return 0;
9316
9352
  }
9317
9353
  function shouldBypassProviderSandboxOnPlatform(provider, platform) {
9318
9354
  if (platform !== "darwin") {
@@ -9336,12 +9372,16 @@ function buildProviderArgs(provider, runtime, argv) {
9336
9372
  }
9337
9373
  if (provider === "pi") {
9338
9374
  const piArgs = [...argv];
9375
+ const piProvider = cliOptionValue(piArgs, "--provider") || process.env.RIG_PI_PROVIDER?.trim() || "openai-codex";
9339
9376
  if (!hasCliOption(piArgs, "--provider")) {
9340
- piArgs.unshift(process.env.RIG_PI_PROVIDER?.trim() || "openai-codex");
9377
+ piArgs.unshift(piProvider);
9341
9378
  piArgs.unshift("--provider");
9342
9379
  }
9343
- if (!hasCliOption(piArgs, "--model")) {
9344
- piArgs.unshift(process.env.RIG_PI_MODEL?.trim() || "gpt-5.5");
9380
+ const model = cliOptionValue(piArgs, "--model") || process.env.RIG_PI_MODEL?.trim() || "gpt-5.5";
9381
+ if (hasCliOption(piArgs, "--model")) {
9382
+ rewriteCliOptionValue(piArgs, "--model", normalizePiModelForProvider(model, piProvider));
9383
+ } else {
9384
+ piArgs.unshift(normalizePiModelForProvider(model, piProvider));
9345
9385
  piArgs.unshift("--model");
9346
9386
  }
9347
9387
  return piArgs;
@@ -9409,6 +9449,38 @@ function resolveProvider() {
9409
9449
  function hasCliOption(argv, option) {
9410
9450
  return argv.some((arg) => arg === option || arg.startsWith(`${option}=`));
9411
9451
  }
9452
+ function cliOptionValue(argv, option) {
9453
+ for (let index = 0;index < argv.length; index += 1) {
9454
+ const arg = argv[index];
9455
+ if (arg === option) {
9456
+ const next = argv[index + 1];
9457
+ return next && !next.startsWith("--") ? next : undefined;
9458
+ }
9459
+ if (arg?.startsWith(`${option}=`)) {
9460
+ return arg.slice(option.length + 1);
9461
+ }
9462
+ }
9463
+ return;
9464
+ }
9465
+ function rewriteCliOptionValue(argv, option, value) {
9466
+ for (let index = 0;index < argv.length; index += 1) {
9467
+ const arg = argv[index];
9468
+ if (arg === option && argv[index + 1] && !argv[index + 1].startsWith("--")) {
9469
+ argv[index + 1] = value;
9470
+ return;
9471
+ }
9472
+ if (arg?.startsWith(`${option}=`)) {
9473
+ argv[index] = `${option}=${value}`;
9474
+ return;
9475
+ }
9476
+ }
9477
+ }
9478
+ function normalizePiModelForProvider(model, provider) {
9479
+ if (provider === "openrouter" && model === "openai-codex/gpt-5.5") {
9480
+ return "openai/gpt-5.5";
9481
+ }
9482
+ return model;
9483
+ }
9412
9484
  function providerBinary(provider) {
9413
9485
  if (provider === "codex") {
9414
9486
  return Bun.which("codex") || "codex";
@@ -9510,8 +9582,8 @@ async function readPluginTaskStatus(projectRoot, taskId) {
9510
9582
  async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9511
9583
  const comment = buildTaskRunLifecycleComment({
9512
9584
  runId: process.env.RIG_SERVER_RUN_ID || "(unknown)",
9513
- status: "closed",
9514
- summary: "Rig task run completed and closed this task.",
9585
+ status: "completed",
9586
+ summary: "Rig task run completed; source closeout waits for approved PR merge.",
9515
9587
  runtimeWorkspace: runtime.workspaceDir,
9516
9588
  logsDir: runtime.logsDir,
9517
9589
  sessionDir: runtime.sessionDir
@@ -9519,7 +9591,7 @@ async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9519
9591
  try {
9520
9592
  const result = await updateConfiguredTaskSourceTask(projectRoot, {
9521
9593
  taskId,
9522
- update: { status: "closed", comment }
9594
+ update: { status: "completed", comment }
9523
9595
  });
9524
9596
  if (result.updated) {
9525
9597
  return;
@@ -9528,7 +9600,9 @@ async function updateTaskSourceAfterRun(projectRoot, taskId, runtime) {
9528
9600
  } catch (error) {
9529
9601
  let fallbackUpdated = false;
9530
9602
  try {
9531
- fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, { status: "closed", comment });
9603
+ const sourceIssueId = loadRuntimeContextFromEnv()?.sourceTask?.sourceIssueId;
9604
+ fallbackUpdated = updateGithubIssueTaskBySourceIssueId(sourceIssueId, taskId, { status: "completed", comment });
9605
+ fallbackUpdated = updateSourceAwareTaskConfigTask(projectRoot, taskId, { status: "completed", comment }) || fallbackUpdated;
9532
9606
  } catch (fallbackError) {
9533
9607
  console.error(`[rig-agent] Source-aware compatibility update also failed for ${taskId}: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`);
9534
9608
  }
@@ -464,10 +464,6 @@ function runBelongsToProject(projectRoot, run, runsDir) {
464
464
  if (recordedProjectRoot) {
465
465
  return resolve2(recordedProjectRoot) === normalizedRoot;
466
466
  }
467
- const projectLocalRunsDir = resolve2(normalizedRoot, ".rig", "runs");
468
- if (isPathWithin(projectLocalRunsDir, resolve2(runsDir))) {
469
- return true;
470
- }
471
467
  const pathFields = [
472
468
  run.worktreePath,
473
469
  run.artifactRoot,
@@ -475,10 +471,20 @@ function runBelongsToProject(projectRoot, run, runsDir) {
475
471
  run.sessionPath,
476
472
  run.sessionLogPath
477
473
  ].filter((value) => typeof value === "string" && value.trim().length > 0);
478
- if (pathFields.length === 0) {
474
+ if (pathFields.length > 0) {
475
+ if (pathFields.some((value) => isPathWithin(normalizedRoot, resolve2(value)))) {
476
+ return true;
477
+ }
478
+ const pointsAtManagedWorkspace = pathFields.some((value) => /(?:^|[/\\])\.worktrees(?:[/\\]|$)/.test(value));
479
+ if (pointsAtManagedWorkspace) {
480
+ return false;
481
+ }
482
+ }
483
+ const projectLocalRunsDir = resolve2(normalizedRoot, ".rig", "runs");
484
+ if (isPathWithin(projectLocalRunsDir, resolve2(runsDir))) {
479
485
  return true;
480
486
  }
481
- return pathFields.some((value) => isPathWithin(normalizedRoot, resolve2(value)));
487
+ return pathFields.length === 0;
482
488
  }
483
489
  function isPathWithin(root, candidate) {
484
490
  const relativePath = relative(root, candidate);