@h-rig/runtime 0.0.6-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +27 -0
  2. package/dist/bin/rig-agent-dispatch.js +9615 -0
  3. package/dist/bin/rig-agent.js +9512 -0
  4. package/dist/bin/rig-browser-tool.js +269 -0
  5. package/dist/src/agent-mode.js +48 -0
  6. package/dist/src/baked-secrets.js +121 -0
  7. package/dist/src/binary-build-worker.js +312 -0
  8. package/dist/src/binary-run.js +540 -0
  9. package/dist/src/boundaries.js +1 -0
  10. package/dist/src/build-time-config.js +25 -0
  11. package/dist/src/control-plane/agent-roles.js +27 -0
  12. package/dist/src/control-plane/agent-wrapper.js +9621 -0
  13. package/dist/src/control-plane/authority-files.js +582 -0
  14. package/dist/src/control-plane/browser-contract.js +135 -0
  15. package/dist/src/control-plane/controlled-bash.js +1111 -0
  16. package/dist/src/control-plane/errors.js +13 -0
  17. package/dist/src/control-plane/harness-main.js +10828 -0
  18. package/dist/src/control-plane/hook-materializer.js +75 -0
  19. package/dist/src/control-plane/hooks/audit-trail.js +353 -0
  20. package/dist/src/control-plane/hooks/completion-verification.js +7552 -0
  21. package/dist/src/control-plane/hooks/import-guard.js +890 -0
  22. package/dist/src/control-plane/hooks/inject-context.js +4189 -0
  23. package/dist/src/control-plane/hooks/post-edit-lint.js +43 -0
  24. package/dist/src/control-plane/hooks/safety-guard.js +910 -0
  25. package/dist/src/control-plane/hooks/scope-guard.js +907 -0
  26. package/dist/src/control-plane/hooks/shared.js +44 -0
  27. package/dist/src/control-plane/hooks/submodule-branch.js +7797 -0
  28. package/dist/src/control-plane/hooks/task-runtime-start.js +7799 -0
  29. package/dist/src/control-plane/hooks/test-integrity-guard.js +891 -0
  30. package/dist/src/control-plane/materialize-task-config.js +453 -0
  31. package/dist/src/control-plane/memory-sync/cli.js +2019 -0
  32. package/dist/src/control-plane/memory-sync/db.js +753 -0
  33. package/dist/src/control-plane/memory-sync/embed.js +281 -0
  34. package/dist/src/control-plane/memory-sync/index.js +2049 -0
  35. package/dist/src/control-plane/memory-sync/query.js +294 -0
  36. package/dist/src/control-plane/memory-sync/read.js +784 -0
  37. package/dist/src/control-plane/memory-sync/types.js +6 -0
  38. package/dist/src/control-plane/memory-sync/write.js +1547 -0
  39. package/dist/src/control-plane/native/git-native.js +490 -0
  40. package/dist/src/control-plane/native/git-ops.js +2860 -0
  41. package/dist/src/control-plane/native/harness-cli.js +9721 -0
  42. package/dist/src/control-plane/native/pr-automation.js +373 -0
  43. package/dist/src/control-plane/native/profile-ops.js +481 -0
  44. package/dist/src/control-plane/native/repo-ops.js +2342 -0
  45. package/dist/src/control-plane/native/root-resolver.js +66 -0
  46. package/dist/src/control-plane/native/run-ops.js +3281 -0
  47. package/dist/src/control-plane/native/runtime-native-sidecar.js +299 -0
  48. package/dist/src/control-plane/native/runtime-native.js +392 -0
  49. package/dist/src/control-plane/native/scope-rules.js +17 -0
  50. package/dist/src/control-plane/native/task-ops.js +6320 -0
  51. package/dist/src/control-plane/native/task-state.js +1512 -0
  52. package/dist/src/control-plane/native/utils.js +535 -0
  53. package/dist/src/control-plane/native/validator-binaries.js +889 -0
  54. package/dist/src/control-plane/native/validator.js +2197 -0
  55. package/dist/src/control-plane/native/verifier.js +3249 -0
  56. package/dist/src/control-plane/native/workspace-ops.js +1635 -0
  57. package/dist/src/control-plane/plugin-host-context.js +334 -0
  58. package/dist/src/control-plane/project-main-pre-run-sync.js +630 -0
  59. package/dist/src/control-plane/provider/claude-stream-records.js +158 -0
  60. package/dist/src/control-plane/provider/codex-app-server.js +885 -0
  61. package/dist/src/control-plane/provider/codex-exec-records.js +203 -0
  62. package/dist/src/control-plane/provider/rig-task-run-skill.js +39 -0
  63. package/dist/src/control-plane/provider/runtime-instructions.js +96 -0
  64. package/dist/src/control-plane/remote.js +854 -0
  65. package/dist/src/control-plane/repos/index.js +473 -0
  66. package/dist/src/control-plane/repos/layout.js +124 -0
  67. package/dist/src/control-plane/repos/mirror/bootstrap.js +268 -0
  68. package/dist/src/control-plane/repos/mirror/refresh.js +398 -0
  69. package/dist/src/control-plane/repos/mirror/state.js +167 -0
  70. package/dist/src/control-plane/repos/registry.js +77 -0
  71. package/dist/src/control-plane/repos/types.js +1 -0
  72. package/dist/src/control-plane/runtime/agent-mode.js +48 -0
  73. package/dist/src/control-plane/runtime/baked-secrets.js +120 -0
  74. package/dist/src/control-plane/runtime/claude-tool-router-binary.js +343 -0
  75. package/dist/src/control-plane/runtime/claude-tool-router.js +520 -0
  76. package/dist/src/control-plane/runtime/context.js +216 -0
  77. package/dist/src/control-plane/runtime/events.js +218 -0
  78. package/dist/src/control-plane/runtime/guard-types.js +6 -0
  79. package/dist/src/control-plane/runtime/guard.js +880 -0
  80. package/dist/src/control-plane/runtime/image/fingerprint-sidecar.js +1194 -0
  81. package/dist/src/control-plane/runtime/image/index.js +2255 -0
  82. package/dist/src/control-plane/runtime/image-fingerprint-sidecar.js +1191 -0
  83. package/dist/src/control-plane/runtime/image.js +2255 -0
  84. package/dist/src/control-plane/runtime/index.js +8511 -0
  85. package/dist/src/control-plane/runtime/isolation/discovery.js +599 -0
  86. package/dist/src/control-plane/runtime/isolation/home.js +1217 -0
  87. package/dist/src/control-plane/runtime/isolation/index.js +8193 -0
  88. package/dist/src/control-plane/runtime/isolation/runner.js +2651 -0
  89. package/dist/src/control-plane/runtime/isolation/shared.js +501 -0
  90. package/dist/src/control-plane/runtime/isolation/toolchain.js +1892 -0
  91. package/dist/src/control-plane/runtime/isolation/types.js +1 -0
  92. package/dist/src/control-plane/runtime/isolation/worktree.js +509 -0
  93. package/dist/src/control-plane/runtime/isolation.js +8193 -0
  94. package/dist/src/control-plane/runtime/overlay.js +67 -0
  95. package/dist/src/control-plane/runtime/plugin-mode.js +41 -0
  96. package/dist/src/control-plane/runtime/plugins.js +1131 -0
  97. package/dist/src/control-plane/runtime/provisioning-env.js +220 -0
  98. package/dist/src/control-plane/runtime/queue.js +8358 -0
  99. package/dist/src/control-plane/runtime/rig-shell.js +205 -0
  100. package/dist/src/control-plane/runtime/rig-tools.js +182 -0
  101. package/dist/src/control-plane/runtime/runner-context.js +1 -0
  102. package/dist/src/control-plane/runtime/runtime-paths.js +184 -0
  103. package/dist/src/control-plane/runtime/sandbox/backend-bwrap.js +311 -0
  104. package/dist/src/control-plane/runtime/sandbox/backend-none.js +21 -0
  105. package/dist/src/control-plane/runtime/sandbox/backend-seatbelt.js +268 -0
  106. package/dist/src/control-plane/runtime/sandbox/backend.js +1718 -0
  107. package/dist/src/control-plane/runtime/sandbox/orchestrator.js +1745 -0
  108. package/dist/src/control-plane/runtime/sandbox/utils.js +137 -0
  109. package/dist/src/control-plane/runtime/sandbox-backend-bwrap.js +311 -0
  110. package/dist/src/control-plane/runtime/sandbox-backend-none.js +21 -0
  111. package/dist/src/control-plane/runtime/sandbox-backend-seatbelt.js +268 -0
  112. package/dist/src/control-plane/runtime/sandbox-backend.js +1718 -0
  113. package/dist/src/control-plane/runtime/sandbox-orchestrator.js +1745 -0
  114. package/dist/src/control-plane/runtime/sandbox-utils.js +137 -0
  115. package/dist/src/control-plane/runtime/snapshot/index.js +454 -0
  116. package/dist/src/control-plane/runtime/snapshot/sidecar.js +502 -0
  117. package/dist/src/control-plane/runtime/snapshot/task-run.js +1578 -0
  118. package/dist/src/control-plane/runtime/snapshot-sidecar.js +498 -0
  119. package/dist/src/control-plane/runtime/snapshot.js +454 -0
  120. package/dist/src/control-plane/runtime/task-run-snapshot.js +1578 -0
  121. package/dist/src/control-plane/runtime/tool-gateway.js +422 -0
  122. package/dist/src/control-plane/runtime/tooling/browser-tools.js +32 -0
  123. package/dist/src/control-plane/runtime/tooling/claude-router-binary.js +343 -0
  124. package/dist/src/control-plane/runtime/tooling/claude-router.js +524 -0
  125. package/dist/src/control-plane/runtime/tooling/file-tools.js +182 -0
  126. package/dist/src/control-plane/runtime/tooling/gateway.js +422 -0
  127. package/dist/src/control-plane/runtime/tooling/index.js +1290 -0
  128. package/dist/src/control-plane/runtime/tooling/shell.js +205 -0
  129. package/dist/src/control-plane/runtime/types.js +1 -0
  130. package/dist/src/control-plane/setup-version.js +14 -0
  131. package/dist/src/control-plane/state-sync/index.js +1509 -0
  132. package/dist/src/control-plane/state-sync/read.js +856 -0
  133. package/dist/src/control-plane/state-sync/reconcile.js +260 -0
  134. package/dist/src/control-plane/state-sync/repo.js +302 -0
  135. package/dist/src/control-plane/state-sync/types.js +111 -0
  136. package/dist/src/control-plane/state-sync/write.js +1469 -0
  137. package/dist/src/control-plane/task-fields.js +38 -0
  138. package/dist/src/control-plane/task-source-bootstrap.js +46 -0
  139. package/dist/src/control-plane/task-source.js +30 -0
  140. package/dist/src/control-plane/tasks/legacy-task-config-source.js +130 -0
  141. package/dist/src/control-plane/tasks/plugin-task-source.js +103 -0
  142. package/dist/src/control-plane/tasks/source-aware-task-config-source.js +611 -0
  143. package/dist/src/control-plane/tasks/source-lifecycle.js +1093 -0
  144. package/dist/src/control-plane/tasks/task-record-reader.js +9 -0
  145. package/dist/src/control-plane/validators/boundary/public-apis.js +107 -0
  146. package/dist/src/control-plane/validators/integration/_shared.js +51 -0
  147. package/dist/src/control-plane/validators/integration/adm-audit-http.js +85 -0
  148. package/dist/src/control-plane/validators/integration/adm-auth-http.js +78 -0
  149. package/dist/src/control-plane/validators/integration/adm-issuer-http.js +80 -0
  150. package/dist/src/control-plane/validators/integration/adm-migration.js +78 -0
  151. package/dist/src/control-plane/validators/integration/adm-scaffold.js +78 -0
  152. package/dist/src/control-plane/validators/runtime-registration.js +64 -0
  153. package/dist/src/control-plane/validators/shared.js +683 -0
  154. package/dist/src/events.js +218 -0
  155. package/dist/src/execution.js +35 -0
  156. package/dist/src/index.js +1633 -0
  157. package/dist/src/layout.js +145 -0
  158. package/dist/src/local-server.js +202 -0
  159. package/dist/src/plugins.js +329 -0
  160. package/dist/src/remote-http.js +83 -0
  161. package/dist/src/runtime-context.js +216 -0
  162. package/dist/src/types.js +1 -0
  163. package/native/darwin-arm64/bin/rig-git +0 -0
  164. package/native/darwin-arm64/bin/rig-shell +0 -0
  165. package/native/darwin-arm64/bin/rig-tools +0 -0
  166. package/native/darwin-arm64/lib/runtime-native-darwin-arm64.dylib +0 -0
  167. package/native/darwin-arm64/lib/runtime-native.dylib +0 -0
  168. package/native/darwin-arm64/manifest.json +1 -0
  169. package/native/linux-x64/bin/rig-git +0 -0
  170. package/native/linux-x64/bin/rig-shell +0 -0
  171. package/native/linux-x64/bin/rig-tools +0 -0
  172. package/native/linux-x64/lib/runtime-native-linux-x64.so +0 -0
  173. package/native/linux-x64/lib/runtime-native.so +0 -0
  174. package/native/linux-x64/manifest.json +1 -0
  175. package/package.json +74 -0
  176. package/skills/rig-task-run.md +71 -0
@@ -0,0 +1,509 @@
1
+ // @bun
2
+ // packages/runtime/src/control-plane/runtime/isolation/worktree.ts
3
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, rmSync as rmSync3 } from "fs";
4
+ import { dirname as dirname2, resolve as resolve2 } from "path";
5
+
6
+ // packages/runtime/src/control-plane/runtime/isolation/shared.ts
7
+ import { existsSync as existsSync2, readFileSync, rmSync as rmSync2 } from "fs";
8
+
9
+ // packages/runtime/src/control-plane/native/runtime-native.ts
10
+ import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
11
+ import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from "fs";
12
+ import { tmpdir } from "os";
13
+ import { dirname, resolve } from "path";
14
+ var sharedNativeRuntimeOutputDir = resolve(tmpdir(), "rig-native");
15
+ var sharedNativeRuntimeOutputPath = resolve(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
16
+ var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
17
+ var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
18
+ async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
19
+ if (await buildNativeRuntimeLibrary(outputPath, options)) {
20
+ return outputPath;
21
+ }
22
+ return !options.force && existsSync(outputPath) ? outputPath : null;
23
+ }
24
+ async function loadNativeRuntimeLibrary() {
25
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
26
+ return null;
27
+ }
28
+ for (const candidate of nativeRuntimeLibraryCandidates()) {
29
+ if (!candidate || !existsSync(candidate)) {
30
+ continue;
31
+ }
32
+ const loaded = tryDlopenNativeRuntimeLibrary(candidate);
33
+ if (loaded) {
34
+ return loaded;
35
+ }
36
+ }
37
+ const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
38
+ if (!builtLibraryPath) {
39
+ return null;
40
+ }
41
+ return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
42
+ }
43
+ function nativePackageLibraryCandidates(fromDir, names) {
44
+ const candidates = [];
45
+ let cursor = resolve(fromDir);
46
+ for (let index = 0;index < 8; index += 1) {
47
+ for (const name of names) {
48
+ candidates.push(resolve(cursor, "native", `${process.platform}-${process.arch}`, name), resolve(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve(cursor, "native", name), resolve(cursor, "native", "lib", name));
49
+ }
50
+ const parent = dirname(cursor);
51
+ if (parent === cursor)
52
+ break;
53
+ cursor = parent;
54
+ }
55
+ return candidates;
56
+ }
57
+ function nativeRuntimeLibraryCandidates() {
58
+ const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
59
+ const execDir = process.execPath?.trim() ? dirname(process.execPath.trim()) : "";
60
+ const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
61
+ return [...new Set([
62
+ explicit,
63
+ ...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
64
+ execDir ? resolve(execDir, colocatedNativeRuntimeFileName) : "",
65
+ execDir ? resolve(execDir, platformSpecific) : "",
66
+ execDir ? resolve(execDir, "..", colocatedNativeRuntimeFileName) : "",
67
+ execDir ? resolve(execDir, "..", platformSpecific) : "",
68
+ execDir ? resolve(execDir, "lib", colocatedNativeRuntimeFileName) : "",
69
+ execDir ? resolve(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
70
+ sharedNativeRuntimeOutputPath
71
+ ].filter(Boolean))];
72
+ }
73
+ function resolveNativeRuntimeSourcePath() {
74
+ const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
75
+ if (explicit && existsSync(explicit)) {
76
+ return explicit;
77
+ }
78
+ const bundled = resolve(import.meta.dir, "../../../native/snapshot.zig");
79
+ return existsSync(bundled) ? bundled : null;
80
+ }
81
+ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
82
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
83
+ return false;
84
+ }
85
+ const zigBinary = Bun.which("zig");
86
+ const sourcePath = resolveNativeRuntimeSourcePath();
87
+ if (!zigBinary || !sourcePath) {
88
+ return false;
89
+ }
90
+ const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
91
+ try {
92
+ mkdirSync(dirname(outputPath), { recursive: true });
93
+ const needsBuild = options.force === true || !existsSync(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
94
+ if (!needsBuild) {
95
+ return true;
96
+ }
97
+ const build = Bun.spawn([
98
+ zigBinary,
99
+ "build-lib",
100
+ sourcePath,
101
+ "-dynamic",
102
+ "-O",
103
+ "ReleaseFast",
104
+ `-femit-bin=${tempOutputPath}`
105
+ ], {
106
+ cwd: import.meta.dir,
107
+ stdout: "pipe",
108
+ stderr: "pipe"
109
+ });
110
+ const exitCode = await build.exited;
111
+ if (exitCode !== 0 || !existsSync(tempOutputPath)) {
112
+ rmSync(tempOutputPath, { force: true });
113
+ return false;
114
+ }
115
+ renameSync(tempOutputPath, outputPath);
116
+ return true;
117
+ } catch {
118
+ rmSync(tempOutputPath, { force: true });
119
+ return false;
120
+ }
121
+ }
122
+ function tryDlopenNativeRuntimeLibrary(outputPath) {
123
+ try {
124
+ return dlopen(outputPath, {
125
+ rig_scope_match: {
126
+ args: ["ptr", "ptr"],
127
+ returns: "u8"
128
+ },
129
+ snapshot_capture: {
130
+ args: ["ptr", "u64", "ptr", "u64"],
131
+ returns: "ptr"
132
+ },
133
+ snapshot_delta: {
134
+ args: ["ptr", "ptr"],
135
+ returns: "ptr"
136
+ },
137
+ snapshot_store_delta: {
138
+ args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
139
+ returns: "ptr"
140
+ },
141
+ snapshot_inspect_delta: {
142
+ args: ["ptr", "u64"],
143
+ returns: "ptr"
144
+ },
145
+ snapshot_apply_delta: {
146
+ args: ["ptr", "u64", "ptr", "u64"],
147
+ returns: "ptr"
148
+ },
149
+ snapshot_release: {
150
+ args: ["ptr"],
151
+ returns: "void"
152
+ },
153
+ runtime_hash_file: {
154
+ args: ["ptr", "u64"],
155
+ returns: "ptr"
156
+ },
157
+ runtime_hash_tree: {
158
+ args: ["ptr", "u64"],
159
+ returns: "ptr"
160
+ },
161
+ runtime_prepare_paths: {
162
+ args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
163
+ returns: "ptr"
164
+ },
165
+ runtime_link_dependency_layer: {
166
+ args: ["ptr", "u64", "ptr", "u64"],
167
+ returns: "ptr"
168
+ },
169
+ runtime_scan_worktrees: {
170
+ args: ["ptr", "u64"],
171
+ returns: "ptr"
172
+ }
173
+ });
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ // packages/runtime/src/control-plane/native/utils.ts
180
+ var scopeRegexCache = new Map;
181
+
182
+ // packages/runtime/src/control-plane/runtime/isolation/shared.ts
183
+ var generatedCredentialFiles = new Set;
184
+ function isRuntimeGatewayGitPath(candidate) {
185
+ return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
186
+ }
187
+ function resolveHostGitBinary() {
188
+ const candidates = [
189
+ process.env.RIG_GIT_BIN?.trim() || "",
190
+ "/usr/bin/git",
191
+ "/opt/homebrew/bin/git",
192
+ "/usr/local/bin/git"
193
+ ];
194
+ const bunResolved = Bun.which("git");
195
+ if (bunResolved && !isRuntimeGatewayGitPath(bunResolved)) {
196
+ candidates.push(bunResolved);
197
+ }
198
+ for (const candidate of candidates) {
199
+ if (!candidate || isRuntimeGatewayGitPath(candidate)) {
200
+ continue;
201
+ }
202
+ if (existsSync2(candidate)) {
203
+ return candidate;
204
+ }
205
+ }
206
+ return "git";
207
+ }
208
+ async function runGitCommand(repoRoot, args) {
209
+ const gitBinary = resolveHostGitBinary();
210
+ return Bun.$`${gitBinary} -C ${repoRoot} ${args}`.quiet().nothrow();
211
+ }
212
+ async function readGitConfigValue(repoRoot, key, global = false) {
213
+ const args = ["config", ...global ? ["--global"] : [], "--get", key];
214
+ const result = await runGitCommand(repoRoot, args);
215
+ if (result.exitCode !== 0) {
216
+ return "";
217
+ }
218
+ return String(result.stdout).trim();
219
+ }
220
+ async function readGitStdout(repoRoot, args) {
221
+ const result = await runGitCommand(repoRoot, args);
222
+ if (result.exitCode !== 0) {
223
+ throw new Error(`git -C ${repoRoot} ${args.join(" ")} failed: ${result.stderr || result.stdout}`);
224
+ }
225
+ return String(result.stdout).trim();
226
+ }
227
+ async function hasGitRemote(repoRoot, remote) {
228
+ const result = await runGitCommand(repoRoot, ["remote", "get-url", remote]);
229
+ return result.exitCode === 0;
230
+ }
231
+ async function ensureFullGitHistory(repoRoot) {
232
+ const shallow = await runGitCommand(repoRoot, ["rev-parse", "--is-shallow-repository"]);
233
+ if (shallow.exitCode !== 0 || String(shallow.stdout).trim() !== "true") {
234
+ return;
235
+ }
236
+ const unshallow = await runGitCommand(repoRoot, ["fetch", "--unshallow", "--tags", "origin"]);
237
+ if (unshallow.exitCode === 0) {
238
+ return;
239
+ }
240
+ const output = `${unshallow.stderr}
241
+ ${unshallow.stdout}`.trim();
242
+ if (/--unshallow on a complete repository|does not make sense/i.test(output)) {
243
+ return;
244
+ }
245
+ throw new Error(`Failed to expand git history for ${repoRoot}: ${output}`);
246
+ }
247
+ async function refreshRemoteBranch(repoRoot, remote, branch) {
248
+ if (!await hasGitRemote(repoRoot, remote)) {
249
+ return;
250
+ }
251
+ try {
252
+ await ensureFullGitHistory(repoRoot);
253
+ const fetch = await runGitCommand(repoRoot, [
254
+ "fetch",
255
+ "--prune",
256
+ "--tags",
257
+ remote,
258
+ `+refs/heads/${branch}:refs/remotes/${remote}/${branch}`
259
+ ]);
260
+ if (fetch.exitCode !== 0) {
261
+ return;
262
+ }
263
+ } catch {
264
+ return;
265
+ }
266
+ }
267
+ function sanitizeRuntimeRefSegment(value) {
268
+ const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
269
+ return (sanitized || "runtime").slice(0, 64);
270
+ }
271
+ function runtimeBranchBackupName(branch) {
272
+ const branchId = branch.replace(/^rig\//, "");
273
+ return `rig-backup/${sanitizeRuntimeRefSegment(branchId)}-${Date.now()}`;
274
+ }
275
+ function taskRuntimeId(taskId) {
276
+ return `task-${taskId}`;
277
+ }
278
+
279
+ // packages/runtime/src/control-plane/runtime/isolation/worktree.ts
280
+ async function resolveMonorepoBaseRef(monorepoRoot) {
281
+ const explicit = process.env.RIG_RUNTIME_BASE_REF?.trim();
282
+ if (explicit) {
283
+ const exists = await runGitCommand(monorepoRoot, ["rev-parse", "--verify", "--quiet", explicit]);
284
+ if (exists.exitCode !== 0) {
285
+ throw new Error(`RIG_RUNTIME_BASE_REF=${explicit} does not resolve at ${monorepoRoot}.`);
286
+ }
287
+ return explicit;
288
+ }
289
+ for (const remote of await preferredBaseRemotes(monorepoRoot)) {
290
+ await refreshRemoteBranch(monorepoRoot, remote, "main");
291
+ const remoteMain = await runGitCommand(monorepoRoot, ["show-ref", "--verify", "--quiet", `refs/remotes/${remote}/main`]);
292
+ if (remoteMain.exitCode === 0) {
293
+ return `${remote}/main`;
294
+ }
295
+ }
296
+ const localMain = await runGitCommand(monorepoRoot, ["show-ref", "--verify", "--quiet", "refs/heads/main"]);
297
+ if (localMain.exitCode === 0) {
298
+ return "main";
299
+ }
300
+ throw new Error(`Failed to resolve a monorepo base ref at ${monorepoRoot}. Expected local main or a remote main ref.`);
301
+ }
302
+ function ensureProvisioningHostProjectRootEnv(projectRoot) {
303
+ if (!projectRoot.trim()) {
304
+ return;
305
+ }
306
+ if (!process.env.RIG_HOST_PROJECT_ROOT?.trim()) {
307
+ process.env.RIG_HOST_PROJECT_ROOT = projectRoot;
308
+ }
309
+ if (!process.env.PROJECT_RIG_ROOT?.trim()) {
310
+ process.env.PROJECT_RIG_ROOT = projectRoot;
311
+ }
312
+ }
313
+ async function provisionRuntimeWorktree(config) {
314
+ const branch = runtimeBranchName(config.taskId, config.runtimeId);
315
+ let hasValidWorktree = existsSync3(resolve2(config.workspaceDir, ".git")) && (await runGitCommand(config.workspaceDir, ["rev-parse", "--show-toplevel"])).exitCode === 0;
316
+ if (existsSync3(config.workspaceDir) && !hasValidWorktree) {
317
+ rmSync3(config.workspaceDir, { recursive: true, force: true });
318
+ }
319
+ if (!hasValidWorktree) {
320
+ mkdirSync2(dirname2(config.workspaceDir), { recursive: true });
321
+ const branchExists = await runGitCommand(config.monorepoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`]);
322
+ const add = branchExists.exitCode === 0 ? await runGitCommand(config.monorepoRoot, ["worktree", "add", "--force", config.workspaceDir, branch]) : await runGitCommand(config.monorepoRoot, ["worktree", "add", "--force", "-b", branch, config.workspaceDir, config.baseRef]);
323
+ if (add.exitCode !== 0) {
324
+ throw new Error(`Failed to create monorepo worktree for ${config.runtimeId}: ${add.stderr.toString() || add.stdout.toString()}`);
325
+ }
326
+ hasValidWorktree = true;
327
+ }
328
+ await ensureRuntimeBranch(config.workspaceDir, branch, config.baseRef);
329
+ }
330
+ async function cleanupRuntimeWorktree(monorepoRoot, workspaceDir) {
331
+ await runGitCommand(monorepoRoot, ["worktree", "remove", "--force", workspaceDir]);
332
+ }
333
+ async function configureRuntimeGitIdentity(projectRoot, repoRoot) {
334
+ const userName = process.env.RIG_GIT_USER_NAME?.trim() || await readGitConfigValue(projectRoot, "user.name") || await readGitConfigValue(projectRoot, "user.name", true) || process.env.GIT_AUTHOR_NAME?.trim() || process.env.GIT_COMMITTER_NAME?.trim() || "rig-agent";
335
+ const userEmail = process.env.RIG_GIT_USER_EMAIL?.trim() || await readGitConfigValue(projectRoot, "user.email") || await readGitConfigValue(projectRoot, "user.email", true) || process.env.GIT_AUTHOR_EMAIL?.trim() || process.env.GIT_COMMITTER_EMAIL?.trim() || "rig-agent@users.noreply.github.com";
336
+ for (const [key, value] of [
337
+ ["user.name", userName],
338
+ ["user.email", userEmail]
339
+ ]) {
340
+ await runGitCommand(repoRoot, ["config", "--local", key, value]);
341
+ }
342
+ }
343
+ async function resolveMonorepoBaseCommit(monorepoRoot, baseRef) {
344
+ return readGitStdout(monorepoRoot, ["rev-parse", baseRef]);
345
+ }
346
+ async function ensureRuntimeBranch(repoRoot, branch, baseRef) {
347
+ const pruneWorktrees = async () => {
348
+ await runGitCommand(repoRoot, ["worktree", "prune", "--expire", "now"]);
349
+ };
350
+ await pruneWorktrees();
351
+ await clearInterruptedRuntimeGitState(repoRoot, branch);
352
+ const current = await runGitCommand(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]);
353
+ if (current.exitCode === 0 && current.stdout.toString().trim() === branch) {
354
+ await repairDisconnectedRuntimeBranch(repoRoot, branch, baseRef);
355
+ await clearInterruptedRuntimeGitState(repoRoot, branch);
356
+ return;
357
+ }
358
+ let checkout = await runGitCommand(repoRoot, ["checkout", "-B", branch]);
359
+ const checkoutError = checkout.stderr.toString() || checkout.stdout.toString();
360
+ if (checkout.exitCode !== 0 && checkoutError.includes("already used by worktree")) {
361
+ await pruneWorktrees();
362
+ checkout = await runGitCommand(repoRoot, ["checkout", "-B", branch]);
363
+ }
364
+ if (checkout.exitCode !== 0 && /resolve your current index first|would be overwritten by checkout/i.test(checkoutError)) {
365
+ await discardTrackedRuntimeChanges(repoRoot, branch, checkoutError);
366
+ checkout = await runGitCommand(repoRoot, ["checkout", "-B", branch]);
367
+ }
368
+ if (checkout.exitCode !== 0) {
369
+ throw new Error(`Failed to prepare runtime branch ${branch} in ${repoRoot}: ${checkout.stderr.toString() || checkout.stdout.toString()}`);
370
+ }
371
+ await repairDisconnectedRuntimeBranch(repoRoot, branch, baseRef);
372
+ await clearInterruptedRuntimeGitState(repoRoot, branch);
373
+ }
374
+ async function repairDisconnectedRuntimeBranch(repoRoot, branch, baseRef) {
375
+ const mergeBase = await runGitCommand(repoRoot, ["merge-base", "HEAD", baseRef]);
376
+ if (mergeBase.exitCode === 0) {
377
+ const counts = await runGitCommand(repoRoot, ["rev-list", "--left-right", "--count", `HEAD...${baseRef}`]);
378
+ if (counts.exitCode === 0) {
379
+ const [aheadRaw = "0", behindRaw = "0"] = String(counts.stdout).trim().split(/\s+/);
380
+ const ahead = Number.parseInt(aheadRaw, 10);
381
+ const behind = Number.parseInt(behindRaw, 10);
382
+ if (Number.isFinite(ahead) && Number.isFinite(behind) && ahead === 0 && behind > 0) {
383
+ const reset = await runGitCommand(repoRoot, ["reset", "--hard", baseRef]);
384
+ if (reset.exitCode !== 0) {
385
+ throw new Error(`Failed to reset stale runtime branch ${branch} to ${baseRef} in ${repoRoot}: ${reset.stderr.toString() || reset.stdout.toString()}`);
386
+ }
387
+ console.log(`[rig-agent] Reset stale runtime branch ${branch} to ${baseRef} in ${repoRoot}`);
388
+ }
389
+ }
390
+ return;
391
+ }
392
+ const backupBranch = runtimeBranchBackupName(branch);
393
+ const rename = await runGitCommand(repoRoot, ["branch", "-m", backupBranch]);
394
+ if (rename.exitCode !== 0) {
395
+ throw new Error(`Failed to archive disconnected runtime branch ${branch} in ${repoRoot}: ${rename.stderr.toString() || rename.stdout.toString()}`);
396
+ }
397
+ const recreate = await runGitCommand(repoRoot, ["checkout", "-B", branch, baseRef]);
398
+ if (recreate.exitCode !== 0) {
399
+ throw new Error(`Failed to recreate runtime branch ${branch} from ${baseRef} in ${repoRoot}: ${recreate.stderr.toString() || recreate.stdout.toString()}`);
400
+ }
401
+ console.log(`[rig-agent] Recreated disconnected runtime branch ${branch} from ${baseRef}; archived as ${backupBranch}`);
402
+ }
403
+ async function clearInterruptedRuntimeGitState(repoRoot, branch) {
404
+ let cleared = false;
405
+ for (const [sentinel, abortArgs] of [
406
+ ["MERGE_HEAD", ["merge", "--abort"]],
407
+ ["REBASE_HEAD", ["rebase", "--abort"]],
408
+ ["CHERRY_PICK_HEAD", ["cherry-pick", "--abort"]]
409
+ ]) {
410
+ const state = await runGitCommand(repoRoot, ["rev-parse", "--verify", "--quiet", sentinel]);
411
+ if (state.exitCode !== 0) {
412
+ continue;
413
+ }
414
+ cleared = true;
415
+ const abort = await runGitCommand(repoRoot, [...abortArgs]);
416
+ if (abort.exitCode === 0) {
417
+ continue;
418
+ }
419
+ const reset = await runGitCommand(repoRoot, ["reset", "--hard", "HEAD"]);
420
+ if (reset.exitCode !== 0) {
421
+ throw new Error(`Failed to recover interrupted git state for runtime branch ${branch} in ${repoRoot}: ${reset.stderr.toString() || reset.stdout.toString()}`);
422
+ }
423
+ }
424
+ const unmerged = await runGitCommand(repoRoot, ["ls-files", "-u"]);
425
+ if (unmerged.exitCode === 0 && String(unmerged.stdout).trim()) {
426
+ cleared = true;
427
+ const reset = await runGitCommand(repoRoot, ["reset", "--hard", "HEAD"]);
428
+ if (reset.exitCode !== 0) {
429
+ throw new Error(`Failed to clear unmerged files for runtime branch ${branch} in ${repoRoot}: ${reset.stderr.toString() || reset.stdout.toString()}`);
430
+ }
431
+ }
432
+ if (cleared) {
433
+ console.log(`[rig-agent] Reset interrupted git state in runtime branch ${branch} at ${repoRoot}`);
434
+ }
435
+ }
436
+ async function discardTrackedRuntimeChanges(repoRoot, branch, reason) {
437
+ const status = await runGitCommand(repoRoot, ["status", "--porcelain"]);
438
+ if (status.exitCode !== 0) {
439
+ throw new Error(`Failed to inspect runtime worktree state for ${branch} in ${repoRoot}: ${status.stderr.toString() || status.stdout.toString()}`);
440
+ }
441
+ const trackedChanges = String(status.stdout).split(/\r?\n/).map((line) => line.trimEnd()).filter(Boolean).filter((line) => !line.startsWith("?? "));
442
+ if (trackedChanges.length === 0) {
443
+ return;
444
+ }
445
+ const reset = await runGitCommand(repoRoot, ["reset", "--hard", "HEAD"]);
446
+ if (reset.exitCode !== 0) {
447
+ throw new Error(`Failed to discard tracked runtime changes for ${branch} in ${repoRoot}: ${reset.stderr.toString() || reset.stdout.toString()}`);
448
+ }
449
+ console.log(`[rig-agent] Reset tracked runtime changes in ${branch} at ${repoRoot} after checkout failed: ${reason.trim()}`);
450
+ }
451
+ function runtimeBranchName(taskId, runtimeId) {
452
+ if (runtimeId === taskRuntimeId(taskId)) {
453
+ return `rig/${taskId}`;
454
+ }
455
+ return `rig/${taskId}-${sanitizeRuntimeRefSegment(runtimeId)}`;
456
+ }
457
+ function runtimeWorktreeName(taskId, runtimeId) {
458
+ if (runtimeId === taskRuntimeId(taskId)) {
459
+ return taskId;
460
+ }
461
+ return sanitizeRuntimeRefSegment(runtimeId);
462
+ }
463
+ function runtimeWorktreeNameFromRuntimeId(runtimeId) {
464
+ return sanitizeRuntimeRefSegment(runtimeId.replace(/^task-/, ""));
465
+ }
466
+ async function preferredBaseRemotes(repoRoot) {
467
+ const remotesResult = await runGitCommand(repoRoot, ["remote"]);
468
+ if (remotesResult.exitCode !== 0) {
469
+ return [];
470
+ }
471
+ const remotes = String(remotesResult.stdout).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
472
+ if (remotes.length === 0) {
473
+ return [];
474
+ }
475
+ const preferred = [];
476
+ const pushRemote = (remote) => {
477
+ if (!preferred.includes(remote)) {
478
+ preferred.push(remote);
479
+ }
480
+ };
481
+ if (await hasGitRemote(repoRoot, "github")) {
482
+ pushRemote("github");
483
+ }
484
+ for (const remote of remotes) {
485
+ if (remote === "github") {
486
+ continue;
487
+ }
488
+ const urlResult = await runGitCommand(repoRoot, ["remote", "get-url", remote]);
489
+ const url = urlResult.exitCode === 0 ? String(urlResult.stdout).trim() : "";
490
+ if (/github\.com[:/]/i.test(url)) {
491
+ pushRemote(remote);
492
+ }
493
+ }
494
+ for (const remote of remotes) {
495
+ pushRemote(remote);
496
+ }
497
+ return preferred;
498
+ }
499
+ export {
500
+ runtimeWorktreeNameFromRuntimeId,
501
+ runtimeWorktreeName,
502
+ runtimeBranchName,
503
+ resolveMonorepoBaseRef,
504
+ resolveMonorepoBaseCommit,
505
+ provisionRuntimeWorktree,
506
+ ensureProvisioningHostProjectRootEnv,
507
+ configureRuntimeGitIdentity,
508
+ cleanupRuntimeWorktree
509
+ };