@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,3281 @@
1
+ // @bun
2
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
3
+
4
+ // packages/runtime/src/layout.ts
5
+ import { existsSync } from "fs";
6
+ import { basename, dirname, resolve } from "path";
7
+ function resolveMonorepoRoot(projectRoot) {
8
+ const normalizedProjectRoot = resolve(projectRoot);
9
+ const explicit = process.env.MONOREPO_ROOT?.trim();
10
+ if (explicit) {
11
+ const explicitRoot = resolve(explicit);
12
+ const explicitParent = dirname(explicitRoot);
13
+ if (basename(explicitParent) === ".worktrees") {
14
+ const owner = dirname(explicitParent);
15
+ const ownerHasGit = existsSync(resolve(owner, ".git"));
16
+ const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
17
+ const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
18
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
19
+ return owner;
20
+ }
21
+ throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
22
+ }
23
+ if (!existsSync(resolve(explicitRoot, ".git"))) {
24
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
25
+ }
26
+ const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
27
+ const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
28
+ if (!hasTaskConfig && !hasRigConfig) {
29
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
30
+ }
31
+ return explicitRoot;
32
+ }
33
+ const projectParent = dirname(normalizedProjectRoot);
34
+ if (basename(projectParent) === ".worktrees") {
35
+ const worktreeOwner = dirname(projectParent);
36
+ const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
37
+ const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
38
+ const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
39
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
40
+ return worktreeOwner;
41
+ }
42
+ }
43
+ return normalizedProjectRoot;
44
+ }
45
+ function resolveRuntimeWorkspaceLayout(workspaceDir) {
46
+ const root = resolve(workspaceDir);
47
+ const rigRoot = resolve(root, ".rig");
48
+ const logsDir = resolve(rigRoot, "logs");
49
+ const stateDir = resolve(rigRoot, "state");
50
+ const runtimeDir = resolve(rigRoot, "runtime");
51
+ const binDir = resolve(rigRoot, "bin");
52
+ return {
53
+ workspaceDir: root,
54
+ rigRoot,
55
+ stateDir,
56
+ logsDir,
57
+ artifactsRoot: resolve(root, RIG_ARTIFACTS_DIRNAME),
58
+ runtimeDir,
59
+ homeDir: resolve(rigRoot, "home"),
60
+ tmpDir: resolve(rigRoot, "tmp"),
61
+ cacheDir: resolve(rigRoot, "cache"),
62
+ sessionDir: resolve(rigRoot, "session"),
63
+ binDir,
64
+ distDir: resolve(rigRoot, "dist"),
65
+ pluginBinDir: resolve(binDir, "plugins"),
66
+ contextPath: resolve(rigRoot, "runtime-context.json"),
67
+ controlPlaneEventsFile: resolve(logsDir, "control-plane.events.jsonl")
68
+ };
69
+ }
70
+ var RIG_ARTIFACTS_DIRNAME = "artifacts";
71
+ var init_layout = () => {};
72
+
73
+ // packages/runtime/src/control-plane/runtime/sandbox/utils.ts
74
+ var init_utils = __esm(() => {
75
+ init_layout();
76
+ });
77
+
78
+ // packages/runtime/src/control-plane/native/run-ops.ts
79
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync2, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync5 } from "fs";
80
+ import { randomUUID } from "crypto";
81
+ import { basename as basename6, dirname as dirname11, resolve as resolve14 } from "path";
82
+
83
+ // packages/runtime/src/binary-run.ts
84
+ init_layout();
85
+ import { chmodSync, cpSync, existsSync as existsSync2, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
86
+ import { basename as basename2, dirname as dirname2, resolve as resolve2 } from "path";
87
+ import { fileURLToPath } from "url";
88
+ import { drainMicrotasks, gcAndSweep } from "bun:jsc";
89
+ var runtimeBinaryBuildQueue = Promise.resolve();
90
+ async function buildRuntimeBinary(options) {
91
+ return runSerializedRuntimeBinaryBuild(async () => {
92
+ const resolved = resolveRuntimeBinaryBuildOptions(options);
93
+ runBestEffortBuildGc();
94
+ const manifestPath = runtimeBinaryCacheManifestPath(resolved.outputPath);
95
+ const buildKey = createRuntimeBinaryBuildKey({
96
+ entrypoint: resolved.entrypoint,
97
+ define: resolved.define,
98
+ env: resolved.env
99
+ });
100
+ if (await isRuntimeBinaryBuildFresh({ outputPath: resolved.outputPath, manifestPath, buildKey })) {
101
+ return;
102
+ }
103
+ if (shouldUseRuntimeBinaryBuildWorker()) {
104
+ await buildRuntimeBinaryViaWorker(resolved);
105
+ } else {
106
+ await buildRuntimeBinaryInProcess(resolved, { manifestPath, buildKey });
107
+ }
108
+ runBestEffortBuildGc();
109
+ });
110
+ }
111
+ async function buildRuntimeBinaryInProcess(options, manifest) {
112
+ const tempBuildDir = resolve2(dirname2(options.outputPath), `.bun-build-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
113
+ const tempOutputPath = resolve2(tempBuildDir, basename2(options.outputPath));
114
+ mkdirSync(tempBuildDir, { recursive: true });
115
+ await withTemporaryEnv({
116
+ ...options.env,
117
+ ...options.define ? { RIG_BUILD_CONFIG_JSON: JSON.stringify(options.define) } : {}
118
+ }, async () => withTemporaryCwd(tempBuildDir, async () => {
119
+ const buildResult = await Bun.build({
120
+ entrypoints: [options.entrypoint],
121
+ compile: {
122
+ target: currentCompileTarget(),
123
+ outfile: tempOutputPath
124
+ },
125
+ target: "bun",
126
+ format: "esm",
127
+ minify: true,
128
+ bytecode: true,
129
+ metafile: true,
130
+ define: options.define ? {
131
+ __RIG_BUILD_CONFIG__: JSON.stringify(options.define)
132
+ } : undefined
133
+ });
134
+ if (!buildResult.success) {
135
+ const details = buildResult.logs.map((log) => [log.message, log.position?.file ? `${log.position.file}:${log.position.line}:${log.position.column}` : ""].filter(Boolean).join(" ")).filter(Boolean).join(`
136
+ `);
137
+ throw new Error(`Failed to build ${options.entrypoint}: ${details || "Bun.build() returned errors"}`);
138
+ }
139
+ if (!existsSync2(tempOutputPath)) {
140
+ const emitted = buildResult.outputs.map((output) => output.path).join(", ") || "(none)";
141
+ throw new Error(`Failed to build ${options.entrypoint}: Bun.build() did not emit ${tempOutputPath}. Emitted: ${emitted}`);
142
+ }
143
+ renameSync(tempOutputPath, options.outputPath);
144
+ chmodSync(options.outputPath, 493);
145
+ if (manifest) {
146
+ await writeRuntimeBinaryCacheManifest({
147
+ manifestPath: manifest.manifestPath,
148
+ buildKey: manifest.buildKey,
149
+ cwd: tempBuildDir,
150
+ metafile: buildResult.metafile
151
+ });
152
+ }
153
+ })).finally(() => {
154
+ rmSync(tempBuildDir, { recursive: true, force: true });
155
+ });
156
+ }
157
+ function runBestEffortBuildGc() {
158
+ try {
159
+ drainMicrotasks();
160
+ } catch {}
161
+ try {
162
+ gcAndSweep();
163
+ } catch {}
164
+ }
165
+ function runtimeBinaryCacheManifestPath(outputPath) {
166
+ return `${outputPath}.build-manifest.json`;
167
+ }
168
+ function resolveRuntimeBinaryBuildOptions(options) {
169
+ return {
170
+ ...options,
171
+ entrypoint: resolve2(options.cwd, options.sourcePath),
172
+ outputPath: resolve2(options.outputPath)
173
+ };
174
+ }
175
+ function shouldUseRuntimeBinaryBuildWorker() {
176
+ if (process.env.RIG_RUNTIME_BUILD_WORKER === "1") {
177
+ return false;
178
+ }
179
+ if (process.env.RIG_RUNTIME_BUILD_IN_PROCESS === "1") {
180
+ return false;
181
+ }
182
+ return true;
183
+ }
184
+ async function buildRuntimeBinaryViaWorker(options) {
185
+ const workerSourcePath = resolveRuntimeBinaryBuildWorkerSourcePath(options);
186
+ if (!workerSourcePath || !existsSync2(workerSourcePath)) {
187
+ await buildRuntimeBinaryInProcess(options, {
188
+ manifestPath: runtimeBinaryCacheManifestPath(options.outputPath),
189
+ buildKey: createRuntimeBinaryBuildKey({
190
+ entrypoint: options.entrypoint,
191
+ define: options.define,
192
+ env: options.env
193
+ })
194
+ });
195
+ return;
196
+ }
197
+ const payloadPath = createRuntimeBinaryBuildWorkerPayloadPath(options.outputPath);
198
+ const bunCli = resolveRuntimeBinaryBuildWorkerInvocation();
199
+ await Bun.write(payloadPath, `${JSON.stringify(options)}
200
+ `);
201
+ const build = Bun.spawn([bunCli.command, workerSourcePath, payloadPath], {
202
+ cwd: options.cwd,
203
+ stdout: "pipe",
204
+ stderr: "pipe",
205
+ env: {
206
+ ...process.env,
207
+ ...options.env,
208
+ ...bunCli.env,
209
+ RIG_RUNTIME_BUILD_WORKER: "1"
210
+ }
211
+ });
212
+ const [exitCode, stdout, stderr] = await Promise.all([
213
+ build.exited,
214
+ new Response(build.stdout).text(),
215
+ new Response(build.stderr).text()
216
+ ]);
217
+ rmSync(payloadPath, { force: true });
218
+ if (exitCode !== 0) {
219
+ throw new Error(`Failed to build ${options.entrypoint}: ${(stderr || stdout || `worker exited ${exitCode}`).trim()}`);
220
+ }
221
+ }
222
+ function createRuntimeBinaryBuildWorkerPayloadPath(outputPath) {
223
+ return resolve2(dirname2(outputPath), `.bun-build-worker-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);
224
+ }
225
+ function resolveRuntimeBinaryBuildWorkerSourcePath(options) {
226
+ const envRoots = [
227
+ options.cwd?.trim(),
228
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
229
+ process.env.PROJECT_RIG_ROOT?.trim()
230
+ ].filter(Boolean);
231
+ for (const root of envRoots) {
232
+ const candidate = resolve2(root, "packages/runtime/src/binary-build-worker.ts");
233
+ if (existsSync2(candidate)) {
234
+ return candidate;
235
+ }
236
+ }
237
+ const localCandidate = resolve2(import.meta.dir, "binary-build-worker.ts");
238
+ return existsSync2(localCandidate) ? localCandidate : null;
239
+ }
240
+ function resolveRuntimeBinaryBuildWorkerInvocation() {
241
+ const bunPath = Bun.which("bun");
242
+ if (bunPath) {
243
+ return { command: bunPath, env: {} };
244
+ }
245
+ if (process.execPath?.trim()) {
246
+ return {
247
+ command: process.execPath,
248
+ env: { BUN_BE_BUN: "1" }
249
+ };
250
+ }
251
+ throw new Error("bun is required to run the runtime binary build worker.");
252
+ }
253
+ function currentCompileTarget() {
254
+ if (process.platform === "darwin") {
255
+ return process.arch === "arm64" ? "bun-darwin-arm64" : "bun-darwin-x64";
256
+ }
257
+ if (process.platform === "linux") {
258
+ return process.arch === "arm64" ? "bun-linux-arm64" : "bun-linux-x64";
259
+ }
260
+ return "bun-windows-x64";
261
+ }
262
+ function createRuntimeBinaryBuildKey(input) {
263
+ return JSON.stringify({
264
+ version: 1,
265
+ bunVersion: Bun.version,
266
+ platform: process.platform,
267
+ arch: process.arch,
268
+ entrypoint: input.entrypoint,
269
+ define: sortRecord(input.define),
270
+ env: sortRecord(input.env)
271
+ });
272
+ }
273
+ async function isRuntimeBinaryBuildFresh(input) {
274
+ if (!existsSync2(input.outputPath) || !existsSync2(input.manifestPath)) {
275
+ return false;
276
+ }
277
+ let manifest = null;
278
+ try {
279
+ manifest = await Bun.file(input.manifestPath).json();
280
+ } catch {
281
+ return false;
282
+ }
283
+ if (!manifest || manifest.version !== 1 || manifest.buildKey !== input.buildKey) {
284
+ return false;
285
+ }
286
+ for (const [filePath, expectedDigest] of Object.entries(manifest.inputs || {})) {
287
+ if (!existsSync2(filePath)) {
288
+ return false;
289
+ }
290
+ if (await sha256File(filePath) !== expectedDigest) {
291
+ return false;
292
+ }
293
+ }
294
+ return true;
295
+ }
296
+ async function writeRuntimeBinaryCacheManifest(input) {
297
+ const inputs = {};
298
+ for (const inputPath of Object.keys(input.metafile?.inputs || {}).sort()) {
299
+ const normalized = normalizeBuildInputPath(input.cwd, inputPath);
300
+ if (!normalized || !existsSync2(normalized)) {
301
+ continue;
302
+ }
303
+ inputs[normalized] = await sha256File(normalized);
304
+ }
305
+ const manifest = {
306
+ version: 1,
307
+ buildKey: input.buildKey,
308
+ inputs
309
+ };
310
+ await Bun.write(input.manifestPath, `${JSON.stringify(manifest, null, 2)}
311
+ `);
312
+ }
313
+ function normalizeBuildInputPath(cwd, inputPath) {
314
+ if (!inputPath) {
315
+ return null;
316
+ }
317
+ if (inputPath.startsWith("file://")) {
318
+ return fileURLToPath(inputPath);
319
+ }
320
+ if (inputPath.startsWith("<")) {
321
+ return null;
322
+ }
323
+ return resolve2(cwd, inputPath);
324
+ }
325
+ async function sha256File(path) {
326
+ const hasher = new Bun.CryptoHasher("sha256");
327
+ hasher.update(await Bun.file(path).arrayBuffer());
328
+ return hasher.digest("hex");
329
+ }
330
+ function sortRecord(value) {
331
+ if (!value) {
332
+ return;
333
+ }
334
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)));
335
+ }
336
+ async function runSerializedRuntimeBinaryBuild(action) {
337
+ const previous = runtimeBinaryBuildQueue;
338
+ let release;
339
+ runtimeBinaryBuildQueue = new Promise((resolve3) => {
340
+ release = resolve3;
341
+ });
342
+ await previous;
343
+ try {
344
+ return await action();
345
+ } finally {
346
+ release();
347
+ }
348
+ }
349
+ async function withTemporaryEnv(env, action) {
350
+ if (!env) {
351
+ return action();
352
+ }
353
+ const previousValues = new Map;
354
+ for (const [key, value] of Object.entries(env)) {
355
+ previousValues.set(key, process.env[key]);
356
+ if (value === undefined) {
357
+ delete process.env[key];
358
+ } else {
359
+ process.env[key] = value;
360
+ }
361
+ }
362
+ try {
363
+ return await action();
364
+ } finally {
365
+ for (const [key, value] of previousValues.entries()) {
366
+ if (value === undefined) {
367
+ delete process.env[key];
368
+ } else {
369
+ process.env[key] = value;
370
+ }
371
+ }
372
+ }
373
+ }
374
+ async function withTemporaryCwd(cwd, action) {
375
+ const previousCwd = process.cwd();
376
+ process.chdir(cwd);
377
+ try {
378
+ return await action();
379
+ } finally {
380
+ process.chdir(previousCwd);
381
+ }
382
+ }
383
+
384
+ // packages/runtime/src/control-plane/remote.ts
385
+ import { homedir } from "os";
386
+ import { dirname as dirname4, join as join2 } from "path";
387
+ import { parse as parseToml2, stringify as stringifyToml2 } from "smol-toml";
388
+
389
+ // packages/runtime/src/control-plane/authority-files.ts
390
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2, appendFileSync, copyFileSync, statSync, readdirSync, chmodSync as chmodSync2 } from "fs";
391
+ import { dirname as dirname3, join, relative, resolve as resolve3 } from "path";
392
+ init_layout();
393
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
394
+ function readTomlFile(path, fallback) {
395
+ if (!existsSync3(path))
396
+ return fallback;
397
+ const raw = readFileSync(path, "utf8");
398
+ if (!raw.trim())
399
+ return fallback;
400
+ return parseToml(raw);
401
+ }
402
+ function writeTomlFile(path, value) {
403
+ mkdirSync2(dirname3(path), { recursive: true });
404
+ writeFileSync2(path, `${stringifyToml(value).trimEnd()}
405
+ `, "utf8");
406
+ }
407
+ function normalizeString(value) {
408
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
409
+ }
410
+ function normalizeStringArray(value) {
411
+ if (!Array.isArray(value))
412
+ return [];
413
+ return value.map((entry) => normalizeString(entry)).filter((entry) => entry !== null);
414
+ }
415
+ function normalizePort(value) {
416
+ const port = typeof value === "number" ? value : Number(value);
417
+ return Number.isInteger(port) && port > 0 && port <= 65535 ? port : 7890;
418
+ }
419
+ function loadRemoteEndpointsToml(projectRoot) {
420
+ const paths = resolveAuthorityPaths(projectRoot);
421
+ const parsed = readTomlFile(paths.remoteEndpointsPath, { version: 1, endpoints: {} });
422
+ return {
423
+ version: parsed.version ?? 1,
424
+ endpoints: parsed.endpoints ?? {}
425
+ };
426
+ }
427
+ function loadRemoteSecretsToml(projectRoot) {
428
+ const paths = resolveAuthorityPaths(projectRoot);
429
+ const parsed = readTomlFile(paths.remoteSecretsPath, { version: 1, secrets: {} });
430
+ return {
431
+ version: parsed.version ?? 1,
432
+ secrets: parsed.secrets ?? {}
433
+ };
434
+ }
435
+ function saveRemoteEndpointsToml(projectRoot, payload) {
436
+ const paths = resolveAuthorityPaths(projectRoot);
437
+ writeTomlFile(paths.remoteEndpointsPath, payload);
438
+ }
439
+ function resolveAuthorityPaths(projectRoot) {
440
+ const normalizedRoot = resolve3(projectRoot);
441
+ const stateRoot = resolveAuthorityStateRoot(normalizedRoot);
442
+ const stateDir = resolveAuthorityStateDir(normalizedRoot);
443
+ return {
444
+ projectRoot: normalizedRoot,
445
+ harnessRoot: resolve3(normalizedRoot, "rig"),
446
+ runsDir: resolve3(stateRoot, "runs"),
447
+ remoteDir: resolve3(stateRoot, "remote"),
448
+ stateDir,
449
+ remoteEndpointsPath: resolve3(stateRoot, "remote", "endpoints.toml"),
450
+ remoteSecretsPath: resolve3(stateDir, "remote-secrets.toml")
451
+ };
452
+ }
453
+ function resolveAuthorityStateRoot(projectRoot) {
454
+ const normalizedRoot = resolve3(projectRoot);
455
+ const taskWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
456
+ if (taskWorkspace) {
457
+ return resolve3(taskWorkspace, ".rig");
458
+ }
459
+ const stateDir = process.env.RIG_STATE_DIR?.trim();
460
+ if (stateDir) {
461
+ return dirname3(resolve3(stateDir));
462
+ }
463
+ const logsDir = process.env.RIG_LOGS_DIR?.trim();
464
+ if (logsDir) {
465
+ return dirname3(resolve3(logsDir));
466
+ }
467
+ const sessionFile = process.env.RIG_SESSION_FILE?.trim();
468
+ if (sessionFile) {
469
+ return dirname3(dirname3(resolve3(sessionFile)));
470
+ }
471
+ const projectStateRoot = resolve3(normalizedRoot, ".rig");
472
+ if (existsSync3(projectStateRoot)) {
473
+ return projectStateRoot;
474
+ }
475
+ return resolve3(normalizedRoot, ".rig");
476
+ }
477
+ function resolveAuthorityStateDir(projectRoot) {
478
+ const explicit = process.env.RIG_STATE_DIR?.trim();
479
+ if (explicit) {
480
+ return resolve3(explicit);
481
+ }
482
+ return resolve3(resolveAuthorityStateRoot(projectRoot), "state");
483
+ }
484
+ function resolveAuthorityArtifactsRoot(projectRoot) {
485
+ const explicit = process.env.ARTIFACTS_DIR?.trim();
486
+ if (explicit) {
487
+ return resolve3(explicit);
488
+ }
489
+ const projectArtifactsRoot = resolve3(projectRoot, "artifacts");
490
+ if (existsSync3(projectArtifactsRoot)) {
491
+ return projectArtifactsRoot;
492
+ }
493
+ const taskWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
494
+ if (taskWorkspace) {
495
+ return resolve3(taskWorkspace, "artifacts");
496
+ }
497
+ if (process.env.MONOREPO_ROOT?.trim()) {
498
+ return resolve3(resolveMonorepoRoot(projectRoot), "artifacts");
499
+ }
500
+ const normalizedRoot = resolve3(projectRoot);
501
+ const projectLooksLikeMonorepo = existsSync3(resolve3(normalizedRoot, ".git")) && existsSync3(resolve3(normalizedRoot, ".rig", "task-config.json"));
502
+ if (!projectLooksLikeMonorepo) {
503
+ return projectArtifactsRoot;
504
+ }
505
+ return resolve3(resolveMonorepoRoot(projectRoot), "artifacts");
506
+ }
507
+ function resolveAuthorityRuntimeDir(projectRoot) {
508
+ return resolve3(resolveAuthorityStateRoot(projectRoot), "runtime");
509
+ }
510
+ function uniquePaths(paths) {
511
+ const seen = new Set;
512
+ const result = [];
513
+ for (const value of paths) {
514
+ const normalized = normalizeString(value);
515
+ if (!normalized || seen.has(normalized))
516
+ continue;
517
+ seen.add(normalized);
518
+ result.push(normalized);
519
+ }
520
+ return result;
521
+ }
522
+ function listAuthorityRunRoots(projectRoot) {
523
+ const normalizedRoot = resolve3(projectRoot);
524
+ const monorepoRoot = resolveMonorepoRoot(normalizedRoot);
525
+ return uniquePaths([
526
+ resolve3(resolveAuthorityStateRoot(normalizedRoot), "runs"),
527
+ resolve3(monorepoRoot, ".rig", "runs")
528
+ ]);
529
+ }
530
+ function listAuthorityArtifactRoots(projectRoot) {
531
+ return uniquePaths([
532
+ resolveAuthorityArtifactsRoot(projectRoot)
533
+ ]);
534
+ }
535
+ function listAuthorityRuntimeAgentsRoots(projectRoot) {
536
+ return uniquePaths([
537
+ resolve3(resolveAuthorityRuntimeDir(projectRoot), "agents")
538
+ ]);
539
+ }
540
+ function listAuthorityRemoteEndpoints(projectRoot) {
541
+ const endpoints = loadRemoteEndpointsToml(projectRoot).endpoints ?? {};
542
+ const secrets = loadRemoteSecretsToml(projectRoot).secrets ?? {};
543
+ return Object.values(endpoints).map((entry) => {
544
+ const token = secrets[entry.secret_ref] ?? "";
545
+ return {
546
+ id: entry.id,
547
+ alias: entry.alias,
548
+ host: entry.host,
549
+ port: normalizePort(entry.port),
550
+ token,
551
+ autoConnect: Boolean(entry.auto_connect),
552
+ addedAt: entry.created_at,
553
+ updatedAt: entry.updated_at,
554
+ lastConnectedAt: normalizeString(entry.last_connected_at) ?? null,
555
+ labels: normalizeStringArray(entry.labels),
556
+ capabilities: normalizeStringArray(entry.capabilities),
557
+ transport: normalizeString(entry.transport) ?? "websocket",
558
+ secretRef: entry.secret_ref
559
+ };
560
+ }).sort((left, right) => left.alias.localeCompare(right.alias));
561
+ }
562
+ function markAuthorityRemoteEndpointConnected(projectRoot, endpointId, connectedAt = new Date().toISOString()) {
563
+ const endpointsToml = loadRemoteEndpointsToml(projectRoot);
564
+ const entry = endpointsToml.endpoints?.[endpointId];
565
+ if (!entry)
566
+ return;
567
+ endpointsToml.endpoints = {
568
+ ...endpointsToml.endpoints ?? {},
569
+ [endpointId]: {
570
+ ...entry,
571
+ last_connected_at: connectedAt,
572
+ updated_at: connectedAt
573
+ }
574
+ };
575
+ saveRemoteEndpointsToml(projectRoot, endpointsToml);
576
+ }
577
+ function resolveAuthorityRunDir(projectRoot, runId) {
578
+ const roots = listAuthorityRunRoots(projectRoot);
579
+ for (const runsDir of roots) {
580
+ const candidate = resolve3(runsDir, runId);
581
+ if (existsSync3(candidate)) {
582
+ return candidate;
583
+ }
584
+ }
585
+ return resolve3(roots[0] ?? resolveAuthorityPaths(projectRoot).runsDir, runId);
586
+ }
587
+ function writeJsonFile(path, value) {
588
+ mkdirSync2(dirname3(path), { recursive: true });
589
+ writeFileSync2(path, `${JSON.stringify(value, null, 2)}
590
+ `, "utf8");
591
+ }
592
+ function readJsonFile(path, fallback) {
593
+ if (!existsSync3(path))
594
+ return fallback;
595
+ try {
596
+ return JSON.parse(readFileSync(path, "utf8"));
597
+ } catch {
598
+ return fallback;
599
+ }
600
+ }
601
+ function listAuthorityRuns(projectRoot) {
602
+ const runs = new Map;
603
+ for (const runsDir of listAuthorityRunRoots(projectRoot)) {
604
+ if (!existsSync3(runsDir))
605
+ continue;
606
+ for (const runId of readDirRuns(runsDir)) {
607
+ if (runs.has(runId))
608
+ continue;
609
+ const entry = readJsonFile(resolve3(runsDir, runId, "run.json"), null);
610
+ if (entry && runBelongsToProject(projectRoot, entry, runsDir)) {
611
+ runs.set(runId, entry);
612
+ }
613
+ }
614
+ }
615
+ return [...runs.values()].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
616
+ }
617
+ function runBelongsToProject(projectRoot, run, runsDir) {
618
+ const normalizedRoot = resolve3(projectRoot);
619
+ const record = run;
620
+ const recordedProjectRoot = normalizeString(record.projectRoot);
621
+ if (recordedProjectRoot) {
622
+ return resolve3(recordedProjectRoot) === normalizedRoot;
623
+ }
624
+ const projectLocalRunsDir = resolve3(normalizedRoot, ".rig", "runs");
625
+ if (isPathWithin(projectLocalRunsDir, resolve3(runsDir))) {
626
+ return true;
627
+ }
628
+ const pathFields = [
629
+ run.worktreePath,
630
+ run.artifactRoot,
631
+ run.logRoot,
632
+ run.sessionPath,
633
+ run.sessionLogPath
634
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
635
+ if (pathFields.length === 0) {
636
+ return true;
637
+ }
638
+ return pathFields.some((value) => isPathWithin(normalizedRoot, resolve3(value)));
639
+ }
640
+ function isPathWithin(root, candidate) {
641
+ const relativePath = relative(root, candidate);
642
+ return relativePath === "" || !relativePath.startsWith("..") && !relativePath.startsWith(`/`) && !relativePath.startsWith(`\\`);
643
+ }
644
+ function readDirRuns(runsDir) {
645
+ try {
646
+ return readdirSync(runsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
647
+ } catch {
648
+ return [];
649
+ }
650
+ }
651
+ function readAuthorityRun(projectRoot, runId) {
652
+ for (const runsDir of listAuthorityRunRoots(projectRoot)) {
653
+ const entry = readJsonFile(resolve3(runsDir, runId, "run.json"), null);
654
+ if (entry) {
655
+ return entry;
656
+ }
657
+ }
658
+ return null;
659
+ }
660
+ function resolveTaskArtifactDirs(projectRoot, taskId) {
661
+ const candidates = [];
662
+ const seen = new Set;
663
+ const add = (value) => {
664
+ const normalized = normalizeString(value);
665
+ if (!normalized || seen.has(normalized))
666
+ return;
667
+ seen.add(normalized);
668
+ candidates.push(normalized);
669
+ };
670
+ for (const run of listAuthorityRuns(projectRoot)) {
671
+ if (run.taskId !== taskId)
672
+ continue;
673
+ add(run.artifactRoot);
674
+ if (run.worktreePath) {
675
+ add(resolve3(run.worktreePath, "artifacts", taskId));
676
+ }
677
+ }
678
+ for (const artifactsRoot of listAuthorityArtifactRoots(projectRoot)) {
679
+ add(resolve3(artifactsRoot, taskId));
680
+ }
681
+ return candidates;
682
+ }
683
+
684
+ // packages/runtime/src/control-plane/remote.ts
685
+ var DEFAULT_TIMEOUTS = {
686
+ connectMs: 5000,
687
+ authMs: 5000,
688
+ requestMs: 30000,
689
+ subscriptionMs: 1e4
690
+ };
691
+ var REMOTES_CONFIG_PATH = join2(homedir(), ".config", "rig", "remotes.toml");
692
+
693
+ class RemoteCliError extends Error {
694
+ code;
695
+ exitCode;
696
+ details;
697
+ constructor(code, message, exitCode = 1, details) {
698
+ super(message);
699
+ this.name = "RemoteCliError";
700
+ this.code = code;
701
+ this.exitCode = exitCode;
702
+ this.details = details;
703
+ }
704
+ }
705
+ class RemoteWsClient {
706
+ endpoint;
707
+ ws = null;
708
+ connected = false;
709
+ pending = new Map;
710
+ onEvent;
711
+ requestTimeoutMs;
712
+ connectTimeoutMs;
713
+ authTimeoutMs;
714
+ subscriptionTimeoutMs;
715
+ connectionToken = null;
716
+ connectionTokenExpiresAt = null;
717
+ tokenRefreshTimer = null;
718
+ constructor(endpoint, options = {}) {
719
+ this.endpoint = endpoint;
720
+ this.onEvent = options.onEvent;
721
+ this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_TIMEOUTS.connectMs;
722
+ this.authTimeoutMs = options.authTimeoutMs ?? DEFAULT_TIMEOUTS.authMs;
723
+ this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_TIMEOUTS.requestMs;
724
+ this.subscriptionTimeoutMs = options.subscriptionTimeoutMs ?? DEFAULT_TIMEOUTS.subscriptionMs;
725
+ }
726
+ setEventHandler(handler) {
727
+ this.onEvent = handler;
728
+ }
729
+ async connect() {
730
+ if (this.connected) {
731
+ return;
732
+ }
733
+ const url = `ws://${this.endpoint.host}:${this.endpoint.port}`;
734
+ await new Promise((resolve4, reject) => {
735
+ let authResolved = false;
736
+ let connectTimer = setTimeout(() => {
737
+ connectTimer = null;
738
+ reject(new RemoteCliError("RIG_REMOTE_CONNECT_TIMEOUT", `Timed out connecting to ${url} after ${this.connectTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.connectTimeoutMs }));
739
+ }, this.connectTimeoutMs);
740
+ const ws = new WebSocket(url);
741
+ this.ws = ws;
742
+ ws.onopen = () => {
743
+ const authMessage = {
744
+ type: "auth",
745
+ id: crypto.randomUUID(),
746
+ timestamp: new Date().toISOString(),
747
+ token: this.connectionToken || this.endpoint.token,
748
+ tokenType: this.connectionToken ? "connection" : "server"
749
+ };
750
+ let authTimer = setTimeout(() => {
751
+ authTimer = null;
752
+ reject(new RemoteCliError("RIG_REMOTE_AUTH_TIMEOUT", `Timed out waiting for auth response from ${url} after ${this.authTimeoutMs}ms.`, 3, { host: this.endpoint.host, port: this.endpoint.port, timeoutMs: this.authTimeoutMs }));
753
+ }, this.authTimeoutMs);
754
+ ws.send(JSON.stringify(authMessage));
755
+ const originalOnMessage = ws.onmessage;
756
+ ws.onmessage = (event) => {
757
+ const message = parseIncomingMessage(event.data);
758
+ if (!message) {
759
+ return;
760
+ }
761
+ if (message.type === "auth_response" && !authResolved) {
762
+ authResolved = true;
763
+ if (connectTimer) {
764
+ clearTimeout(connectTimer);
765
+ connectTimer = null;
766
+ }
767
+ if (authTimer) {
768
+ clearTimeout(authTimer);
769
+ authTimer = null;
770
+ }
771
+ const auth = message;
772
+ if (!auth.success) {
773
+ reject(new RemoteCliError("RIG_REMOTE_AUTH_FAILED", auth.error || "Remote authentication failed.", 3, { host: this.endpoint.host, port: this.endpoint.port }));
774
+ return;
775
+ }
776
+ if (auth.connectionToken && auth.connectionTokenExpiresAt) {
777
+ this.connectionToken = auth.connectionToken;
778
+ this.connectionTokenExpiresAt = auth.connectionTokenExpiresAt;
779
+ this.scheduleTokenRefresh();
780
+ }
781
+ if (this.endpoint.configPath.endsWith("endpoints.toml")) {
782
+ const projectRoot = dirname4(dirname4(dirname4(this.endpoint.configPath)));
783
+ try {
784
+ markAuthorityRemoteEndpointConnected(projectRoot, this.endpoint.alias ? listAuthorityRemoteEndpoints(projectRoot).find((entry) => entry.alias === this.endpoint.alias)?.id ?? "" : "");
785
+ } catch {}
786
+ }
787
+ this.connected = true;
788
+ ws.onmessage = (event2) => {
789
+ const next = parseIncomingMessage(event2.data);
790
+ if (!next) {
791
+ return;
792
+ }
793
+ this.routeMessage(next);
794
+ };
795
+ resolve4();
796
+ return;
797
+ }
798
+ if (typeof originalOnMessage === "function") {
799
+ originalOnMessage.call(ws, event);
800
+ }
801
+ };
802
+ };
803
+ ws.onerror = () => {
804
+ if (connectTimer) {
805
+ clearTimeout(connectTimer);
806
+ connectTimer = null;
807
+ }
808
+ if (!authResolved) {
809
+ reject(new RemoteCliError("RIG_REMOTE_CONNECT_FAILED", `Failed to connect to ${url}.`, 3, { host: this.endpoint.host, port: this.endpoint.port }));
810
+ }
811
+ };
812
+ ws.onclose = () => {
813
+ this.connected = false;
814
+ this.clearPending("Connection closed");
815
+ };
816
+ });
817
+ }
818
+ disconnect() {
819
+ this.connected = false;
820
+ this.clearTokenRefreshTimer();
821
+ this.clearPending("Connection closed");
822
+ if (this.ws) {
823
+ try {
824
+ this.ws.close();
825
+ } catch {}
826
+ this.ws = null;
827
+ }
828
+ }
829
+ async subscribe(eventTypes = []) {
830
+ const message = { type: "subscribe" };
831
+ if (eventTypes.length > 0) {
832
+ message.eventTypes = eventTypes;
833
+ }
834
+ const response = await this.request(message, this.subscriptionTimeoutMs);
835
+ return expectType(response, "operation_result");
836
+ }
837
+ async unsubscribe() {
838
+ return expectType(await this.request({ type: "unsubscribe" }), "operation_result");
839
+ }
840
+ async getState() {
841
+ return expectType(await this.request({ type: "get_state" }), "state_response");
842
+ }
843
+ async getTasks() {
844
+ return expectType(await this.request({ type: "get_tasks" }), "tasks_response");
845
+ }
846
+ async pause() {
847
+ return expectType(await this.request({ type: "pause" }), "operation_result");
848
+ }
849
+ async resume() {
850
+ return expectType(await this.request({ type: "resume" }), "operation_result");
851
+ }
852
+ async interrupt() {
853
+ return expectType(await this.request({ type: "interrupt" }), "operation_result");
854
+ }
855
+ async continueExecution() {
856
+ return expectType(await this.request({ type: "continue" }), "operation_result");
857
+ }
858
+ async refreshTasks() {
859
+ return expectType(await this.request({ type: "refresh_tasks" }), "operation_result");
860
+ }
861
+ async addIterations(count) {
862
+ return expectType(await this.request({ type: "add_iterations", count }), "operation_result");
863
+ }
864
+ async removeIterations(count) {
865
+ return expectType(await this.request({ type: "remove_iterations", count }), "operation_result");
866
+ }
867
+ async getPromptPreview(taskId) {
868
+ return expectType(await this.request({ type: "get_prompt_preview", taskId }), "prompt_preview_response");
869
+ }
870
+ async getIterationOutput(taskId) {
871
+ return expectType(await this.request({ type: "get_iteration_output", taskId }), "iteration_output_response");
872
+ }
873
+ async startOrchestration(options) {
874
+ return expectType(await this.request({
875
+ type: "orchestrate:start",
876
+ maxWorkers: options.maxWorkers,
877
+ maxIterations: options.maxIterations,
878
+ directMerge: options.directMerge
879
+ }), "orchestrate:start_response");
880
+ }
881
+ async pauseOrchestration(orchestrationId) {
882
+ return expectType(await this.request({ type: "orchestrate:pause", orchestrationId }), "operation_result");
883
+ }
884
+ async resumeOrchestration(orchestrationId) {
885
+ return expectType(await this.request({ type: "orchestrate:resume", orchestrationId }), "operation_result");
886
+ }
887
+ async stopOrchestration(orchestrationId) {
888
+ return expectType(await this.request({ type: "orchestrate:stop", orchestrationId }), "operation_result");
889
+ }
890
+ async getOrchestrationState(orchestrationId) {
891
+ return expectType(await this.request({ type: "orchestrate:get_state", orchestrationId }), "orchestrate:state_response");
892
+ }
893
+ async ping() {
894
+ return this.request({ type: "ping" });
895
+ }
896
+ async request(payload, timeoutMs = this.requestTimeoutMs) {
897
+ if (!this.connected || !this.ws) {
898
+ throw new RemoteCliError("RIG_REMOTE_NOT_CONNECTED", "Remote client is not connected.", 3);
899
+ }
900
+ const id = crypto.randomUUID();
901
+ const message = {
902
+ ...payload,
903
+ id,
904
+ timestamp: new Date().toISOString()
905
+ };
906
+ return await new Promise((resolve4, reject) => {
907
+ const timeout = setTimeout(() => {
908
+ this.pending.delete(id);
909
+ reject(new RemoteCliError("RIG_REMOTE_REQUEST_TIMEOUT", `Timed out waiting for response to '${String(payload.type)}' after ${timeoutMs}ms.`, 3, { requestType: payload.type, requestId: id, timeoutMs }));
910
+ }, timeoutMs);
911
+ this.pending.set(id, { resolve: resolve4, reject, timeout });
912
+ this.ws?.send(JSON.stringify(message));
913
+ });
914
+ }
915
+ routeMessage(message) {
916
+ if (message.id && this.pending.has(message.id)) {
917
+ const pending = this.pending.get(message.id);
918
+ if (pending) {
919
+ clearTimeout(pending.timeout);
920
+ this.pending.delete(message.id);
921
+ pending.resolve(message);
922
+ }
923
+ return;
924
+ }
925
+ if (message.type === "token_refresh_response") {
926
+ const token = normalizeString2(message.connectionToken);
927
+ const expiresAt = normalizeString2(message.connectionTokenExpiresAt);
928
+ if (token && expiresAt) {
929
+ this.connectionToken = token;
930
+ this.connectionTokenExpiresAt = expiresAt;
931
+ this.scheduleTokenRefresh();
932
+ }
933
+ }
934
+ this.onEvent?.({ receivedAt: new Date().toISOString(), message });
935
+ }
936
+ scheduleTokenRefresh() {
937
+ this.clearTokenRefreshTimer();
938
+ if (!this.connectionToken || !this.connectionTokenExpiresAt || !this.ws || !this.connected) {
939
+ return;
940
+ }
941
+ const expiresAt = new Date(this.connectionTokenExpiresAt).getTime();
942
+ if (!Number.isFinite(expiresAt)) {
943
+ return;
944
+ }
945
+ const refreshAt = expiresAt - 60 * 60 * 1000;
946
+ const delay = Math.max(0, refreshAt - Date.now());
947
+ this.tokenRefreshTimer = setTimeout(() => {
948
+ if (!this.connected || !this.ws || !this.connectionToken) {
949
+ return;
950
+ }
951
+ const refreshMessage = {
952
+ type: "token_refresh",
953
+ id: crypto.randomUUID(),
954
+ timestamp: new Date().toISOString(),
955
+ connectionToken: this.connectionToken
956
+ };
957
+ this.ws.send(JSON.stringify(refreshMessage));
958
+ }, delay);
959
+ }
960
+ clearTokenRefreshTimer() {
961
+ if (this.tokenRefreshTimer) {
962
+ clearTimeout(this.tokenRefreshTimer);
963
+ this.tokenRefreshTimer = null;
964
+ }
965
+ }
966
+ clearPending(reason) {
967
+ for (const [requestId, pending] of this.pending.entries()) {
968
+ clearTimeout(pending.timeout);
969
+ pending.reject(new RemoteCliError("RIG_REMOTE_REQUEST_CANCELLED", `${reason} (request ${requestId}).`, 3, { requestId }));
970
+ this.pending.delete(requestId);
971
+ }
972
+ }
973
+ }
974
+ function normalizeString2(value) {
975
+ if (!value) {
976
+ return "";
977
+ }
978
+ return value.trim();
979
+ }
980
+ function parseIncomingMessage(raw) {
981
+ if (typeof raw !== "string") {
982
+ return null;
983
+ }
984
+ try {
985
+ const parsed = JSON.parse(raw);
986
+ if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
987
+ return null;
988
+ }
989
+ return parsed;
990
+ } catch {
991
+ return null;
992
+ }
993
+ }
994
+ function expectType(message, expectedType) {
995
+ if (message.type !== expectedType) {
996
+ throw new RemoteCliError("RIG_REMOTE_UNEXPECTED_RESPONSE", `Unexpected response type: ${message.type}. Expected ${expectedType}.`, 3, { expectedType, actualType: message.type, message });
997
+ }
998
+ return message;
999
+ }
1000
+
1001
+ // packages/runtime/src/control-plane/runtime/baked-secrets.ts
1002
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1003
+ import { resolve as resolve4 } from "path";
1004
+ var BAKED_RUNTIME_SECRETS = {
1005
+ ANTHROPIC_API_KEY: typeof RIG_BAKED_ANTHROPIC_API_KEY !== "undefined" ? RIG_BAKED_ANTHROPIC_API_KEY : "",
1006
+ OPENAI_API_KEY: typeof RIG_BAKED_OPENAI_API_KEY !== "undefined" ? RIG_BAKED_OPENAI_API_KEY : "",
1007
+ OPENROUTER_API_KEY: typeof RIG_BAKED_OPENROUTER_API_KEY !== "undefined" ? RIG_BAKED_OPENROUTER_API_KEY : "",
1008
+ AI_REVIEW_MODE: typeof RIG_BAKED_AI_REVIEW_MODE !== "undefined" ? RIG_BAKED_AI_REVIEW_MODE : "",
1009
+ AI_REVIEW_PROVIDER: typeof RIG_BAKED_AI_REVIEW_PROVIDER !== "undefined" ? RIG_BAKED_AI_REVIEW_PROVIDER : "",
1010
+ GREPTILE_API_BASE: typeof RIG_BAKED_GREPTILE_API_BASE !== "undefined" ? RIG_BAKED_GREPTILE_API_BASE : "",
1011
+ GREPTILE_REMOTE: typeof RIG_BAKED_GREPTILE_REMOTE !== "undefined" ? RIG_BAKED_GREPTILE_REMOTE : "",
1012
+ GREPTILE_REPOSITORY: typeof RIG_BAKED_GREPTILE_REPOSITORY !== "undefined" ? RIG_BAKED_GREPTILE_REPOSITORY : "",
1013
+ GREPTILE_CONTEXT_BRANCH: typeof RIG_BAKED_GREPTILE_CONTEXT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_CONTEXT_BRANCH : "",
1014
+ GREPTILE_DEFAULT_BRANCH: typeof RIG_BAKED_GREPTILE_DEFAULT_BRANCH !== "undefined" ? RIG_BAKED_GREPTILE_DEFAULT_BRANCH : "",
1015
+ GREPTILE_API_KEY: typeof RIG_BAKED_GREPTILE_API_KEY !== "undefined" ? RIG_BAKED_GREPTILE_API_KEY : "",
1016
+ GREPTILE_GITHUB_TOKEN: typeof RIG_BAKED_GREPTILE_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GREPTILE_GITHUB_TOKEN : "",
1017
+ GREPTILE_POLL_ATTEMPTS: typeof RIG_BAKED_GREPTILE_POLL_ATTEMPTS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_ATTEMPTS : "",
1018
+ GREPTILE_POLL_INTERVAL_MS: typeof RIG_BAKED_GREPTILE_POLL_INTERVAL_MS !== "undefined" ? RIG_BAKED_GREPTILE_POLL_INTERVAL_MS : "",
1019
+ GH_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
1020
+ GITHUB_TOKEN: typeof RIG_BAKED_GITHUB_TOKEN !== "undefined" ? RIG_BAKED_GITHUB_TOKEN : "",
1021
+ GITHUB_SSH_KEY: typeof RIG_BAKED_GITHUB_SSH_KEY !== "undefined" ? RIG_BAKED_GITHUB_SSH_KEY : "",
1022
+ AWS_ACCESS_KEY_ID: typeof RIG_BAKED_AWS_ACCESS_KEY_ID !== "undefined" ? RIG_BAKED_AWS_ACCESS_KEY_ID : "",
1023
+ AWS_SECRET_ACCESS_KEY: typeof RIG_BAKED_AWS_SECRET_ACCESS_KEY !== "undefined" ? RIG_BAKED_AWS_SECRET_ACCESS_KEY : "",
1024
+ AWS_REGION: typeof RIG_BAKED_AWS_REGION !== "undefined" ? RIG_BAKED_AWS_REGION : "",
1025
+ LINEAR_API_KEY: typeof RIG_BAKED_LINEAR_API_KEY !== "undefined" ? RIG_BAKED_LINEAR_API_KEY : "",
1026
+ LINEAR_WEBHOOK_SECRET: typeof RIG_BAKED_LINEAR_WEBHOOK_SECRET !== "undefined" ? RIG_BAKED_LINEAR_WEBHOOK_SECRET : ""
1027
+ };
1028
+ function resolveRuntimeSecrets(env, baked = BAKED_RUNTIME_SECRETS) {
1029
+ const resolved = {};
1030
+ const keys = new Set([
1031
+ ...Object.keys(BAKED_RUNTIME_SECRETS),
1032
+ ...Object.keys(baked)
1033
+ ]);
1034
+ for (const key of keys) {
1035
+ const envValue = env[key]?.trim();
1036
+ const bakedValue = baked[key]?.trim();
1037
+ if (envValue) {
1038
+ resolved[key] = envValue;
1039
+ } else if (bakedValue) {
1040
+ resolved[key] = bakedValue;
1041
+ }
1042
+ }
1043
+ return resolved;
1044
+ }
1045
+ function loadDotEnvSecrets(projectRoot, env = process.env) {
1046
+ const dotenvPath = resolve4(projectRoot, ".env");
1047
+ if (!existsSync4(dotenvPath)) {
1048
+ return {};
1049
+ }
1050
+ const parsed = {};
1051
+ const lines = readFileSync2(dotenvPath, "utf-8").split(/\r?\n/);
1052
+ for (const rawLine of lines) {
1053
+ const line = rawLine.trim();
1054
+ if (!line || line.startsWith("#")) {
1055
+ continue;
1056
+ }
1057
+ const exportMatch = line.match(/^(?:export\s+)?([A-Z0-9_]+)\s*=\s*(.*)$/);
1058
+ if (!exportMatch) {
1059
+ continue;
1060
+ }
1061
+ const key = exportMatch[1];
1062
+ if (!(key in BAKED_RUNTIME_SECRETS)) {
1063
+ continue;
1064
+ }
1065
+ const value = expandShellValue(exportMatch[2] ?? "", { ...env, ...parsed });
1066
+ if (value) {
1067
+ parsed[key] = value;
1068
+ }
1069
+ }
1070
+ return parsed;
1071
+ }
1072
+ function secretDefinesFromEnv(env = process.env, projectRoot) {
1073
+ const dotenvSecrets = projectRoot ? loadDotEnvSecrets(projectRoot, env) : {};
1074
+ const resolved = resolveRuntimeSecrets(env, {
1075
+ ...BAKED_RUNTIME_SECRETS,
1076
+ ...dotenvSecrets
1077
+ });
1078
+ return {
1079
+ RIG_BAKED_ANTHROPIC_API_KEY: resolved.ANTHROPIC_API_KEY || "",
1080
+ RIG_BAKED_OPENAI_API_KEY: resolved.OPENAI_API_KEY || "",
1081
+ RIG_BAKED_OPENROUTER_API_KEY: resolved.OPENROUTER_API_KEY || "",
1082
+ RIG_BAKED_AI_REVIEW_MODE: resolved.AI_REVIEW_MODE || "",
1083
+ RIG_BAKED_AI_REVIEW_PROVIDER: resolved.AI_REVIEW_PROVIDER || "",
1084
+ RIG_BAKED_GREPTILE_API_BASE: resolved.GREPTILE_API_BASE || "",
1085
+ RIG_BAKED_GREPTILE_REMOTE: resolved.GREPTILE_REMOTE || "",
1086
+ RIG_BAKED_GREPTILE_REPOSITORY: resolved.GREPTILE_REPOSITORY || "",
1087
+ RIG_BAKED_GREPTILE_CONTEXT_BRANCH: resolved.GREPTILE_CONTEXT_BRANCH || "",
1088
+ RIG_BAKED_GREPTILE_API_KEY: resolved.GREPTILE_API_KEY || "",
1089
+ RIG_BAKED_GREPTILE_GITHUB_TOKEN: resolved.GREPTILE_GITHUB_TOKEN || "",
1090
+ RIG_BAKED_GREPTILE_POLL_ATTEMPTS: resolved.GREPTILE_POLL_ATTEMPTS || "",
1091
+ RIG_BAKED_GREPTILE_POLL_INTERVAL_MS: resolved.GREPTILE_POLL_INTERVAL_MS || "",
1092
+ RIG_BAKED_GITHUB_TOKEN: resolved.GITHUB_TOKEN || resolved.GH_TOKEN || "",
1093
+ RIG_BAKED_GITHUB_SSH_KEY: resolved.GITHUB_SSH_KEY || "",
1094
+ RIG_BAKED_AWS_ACCESS_KEY_ID: resolved.AWS_ACCESS_KEY_ID || "",
1095
+ RIG_BAKED_AWS_SECRET_ACCESS_KEY: resolved.AWS_SECRET_ACCESS_KEY || "",
1096
+ RIG_BAKED_AWS_REGION: resolved.AWS_REGION || "",
1097
+ RIG_BAKED_LINEAR_API_KEY: resolved.LINEAR_API_KEY || "",
1098
+ RIG_BAKED_LINEAR_WEBHOOK_SECRET: resolved.LINEAR_WEBHOOK_SECRET || ""
1099
+ };
1100
+ }
1101
+ function expandShellValue(rawValue, env) {
1102
+ let value = rawValue.trim();
1103
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1104
+ value = value.slice(1, -1);
1105
+ }
1106
+ return value.replace(/\$\{([A-Z0-9_]+)(:-([^}]*))?\}/g, (_match, name, _defaultGroup, fallback) => {
1107
+ const envValue = env[name]?.trim();
1108
+ if (envValue) {
1109
+ return envValue;
1110
+ }
1111
+ return fallback ?? "";
1112
+ });
1113
+ }
1114
+
1115
+ // packages/runtime/src/control-plane/native/git-native.ts
1116
+ import { tmpdir } from "os";
1117
+ import { dirname as dirname5, isAbsolute, resolve as resolve5 } from "path";
1118
+ var sharedGitNativeOutputDir = resolve5(tmpdir(), "rig-native");
1119
+ var sharedGitNativeOutputPath = resolve5(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1120
+ // packages/runtime/src/control-plane/native/utils.ts
1121
+ import { ptr as ptr2 } from "bun:ffi";
1122
+ init_layout();
1123
+
1124
+ // packages/runtime/src/control-plane/native/runtime-native.ts
1125
+ import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
1126
+ import { copyFileSync as copyFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, renameSync as renameSync2, rmSync as rmSync2, statSync as statSync2 } from "fs";
1127
+ import { tmpdir as tmpdir2 } from "os";
1128
+ import { dirname as dirname6, resolve as resolve6 } from "path";
1129
+ var sharedNativeRuntimeOutputDir = resolve6(tmpdir2(), "rig-native");
1130
+ var sharedNativeRuntimeOutputPath = resolve6(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
1131
+ var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
1132
+ var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
1133
+ function requireNativeRuntimeLibrary(feature) {
1134
+ if (!nativeRuntimeLibrary) {
1135
+ throw new Error(`Native Zig runtime is required for ${feature}`);
1136
+ }
1137
+ return nativeRuntimeLibrary;
1138
+ }
1139
+ function runtimeScanWorktreesNative(worktreesRoot) {
1140
+ const response = runNativeRuntimeSidecar({
1141
+ op: "scan-worktrees",
1142
+ input: { worktreesRoot }
1143
+ });
1144
+ if (!response.ok) {
1145
+ throw new Error(response.error);
1146
+ }
1147
+ return response.entries ?? [];
1148
+ }
1149
+ async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
1150
+ if (await buildNativeRuntimeLibrary(outputPath, options)) {
1151
+ return outputPath;
1152
+ }
1153
+ return !options.force && existsSync5(outputPath) ? outputPath : null;
1154
+ }
1155
+ async function loadNativeRuntimeLibrary() {
1156
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
1157
+ return null;
1158
+ }
1159
+ for (const candidate of nativeRuntimeLibraryCandidates()) {
1160
+ if (!candidate || !existsSync5(candidate)) {
1161
+ continue;
1162
+ }
1163
+ const loaded = tryDlopenNativeRuntimeLibrary(candidate);
1164
+ if (loaded) {
1165
+ return loaded;
1166
+ }
1167
+ }
1168
+ const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
1169
+ if (!builtLibraryPath) {
1170
+ return null;
1171
+ }
1172
+ return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
1173
+ }
1174
+ function nativePackageLibraryCandidates(fromDir, names) {
1175
+ const candidates = [];
1176
+ let cursor = resolve6(fromDir);
1177
+ for (let index = 0;index < 8; index += 1) {
1178
+ for (const name of names) {
1179
+ candidates.push(resolve6(cursor, "native", `${process.platform}-${process.arch}`, name), resolve6(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve6(cursor, "native", name), resolve6(cursor, "native", "lib", name));
1180
+ }
1181
+ const parent = dirname6(cursor);
1182
+ if (parent === cursor)
1183
+ break;
1184
+ cursor = parent;
1185
+ }
1186
+ return candidates;
1187
+ }
1188
+ function nativeRuntimeLibraryCandidates() {
1189
+ const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
1190
+ const execDir = process.execPath?.trim() ? dirname6(process.execPath.trim()) : "";
1191
+ const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
1192
+ return [...new Set([
1193
+ explicit,
1194
+ ...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
1195
+ execDir ? resolve6(execDir, colocatedNativeRuntimeFileName) : "",
1196
+ execDir ? resolve6(execDir, platformSpecific) : "",
1197
+ execDir ? resolve6(execDir, "..", colocatedNativeRuntimeFileName) : "",
1198
+ execDir ? resolve6(execDir, "..", platformSpecific) : "",
1199
+ execDir ? resolve6(execDir, "lib", colocatedNativeRuntimeFileName) : "",
1200
+ execDir ? resolve6(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
1201
+ sharedNativeRuntimeOutputPath
1202
+ ].filter(Boolean))];
1203
+ }
1204
+ function resolveNativeRuntimeSourcePath() {
1205
+ const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
1206
+ if (explicit && existsSync5(explicit)) {
1207
+ return explicit;
1208
+ }
1209
+ const bundled = resolve6(import.meta.dir, "../../../native/snapshot.zig");
1210
+ return existsSync5(bundled) ? bundled : null;
1211
+ }
1212
+ function resolveNativeRuntimeSidecarSourcePath() {
1213
+ const envRoots = [
1214
+ process.cwd()?.trim(),
1215
+ process.env.RIG_HOST_PROJECT_ROOT?.trim(),
1216
+ process.env.PROJECT_RIG_ROOT?.trim()
1217
+ ].filter(Boolean);
1218
+ for (const root of envRoots) {
1219
+ for (const relative2 of [
1220
+ "packages/runtime/src/control-plane/native/runtime-native-sidecar.ts",
1221
+ "packages/runtime/dist/src/control-plane/native/runtime-native-sidecar.js"
1222
+ ]) {
1223
+ const candidate = resolve6(root, relative2);
1224
+ if (existsSync5(candidate)) {
1225
+ return candidate;
1226
+ }
1227
+ }
1228
+ }
1229
+ for (const localCandidate of [
1230
+ resolve6(import.meta.dir, "runtime-native-sidecar.js"),
1231
+ resolve6(import.meta.dir, "runtime-native-sidecar.ts")
1232
+ ]) {
1233
+ if (existsSync5(localCandidate))
1234
+ return localCandidate;
1235
+ }
1236
+ return null;
1237
+ }
1238
+ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
1239
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
1240
+ return false;
1241
+ }
1242
+ const zigBinary = Bun.which("zig");
1243
+ const sourcePath = resolveNativeRuntimeSourcePath();
1244
+ if (!zigBinary || !sourcePath) {
1245
+ return false;
1246
+ }
1247
+ const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
1248
+ try {
1249
+ mkdirSync3(dirname6(outputPath), { recursive: true });
1250
+ const needsBuild = options.force === true || !existsSync5(outputPath) || statSync2(sourcePath).mtimeMs > statSync2(outputPath).mtimeMs;
1251
+ if (!needsBuild) {
1252
+ return true;
1253
+ }
1254
+ const build = Bun.spawn([
1255
+ zigBinary,
1256
+ "build-lib",
1257
+ sourcePath,
1258
+ "-dynamic",
1259
+ "-O",
1260
+ "ReleaseFast",
1261
+ `-femit-bin=${tempOutputPath}`
1262
+ ], {
1263
+ cwd: import.meta.dir,
1264
+ stdout: "pipe",
1265
+ stderr: "pipe"
1266
+ });
1267
+ const exitCode = await build.exited;
1268
+ if (exitCode !== 0 || !existsSync5(tempOutputPath)) {
1269
+ rmSync2(tempOutputPath, { force: true });
1270
+ return false;
1271
+ }
1272
+ renameSync2(tempOutputPath, outputPath);
1273
+ return true;
1274
+ } catch {
1275
+ rmSync2(tempOutputPath, { force: true });
1276
+ return false;
1277
+ }
1278
+ }
1279
+ function tryDlopenNativeRuntimeLibrary(outputPath) {
1280
+ try {
1281
+ return dlopen(outputPath, {
1282
+ rig_scope_match: {
1283
+ args: ["ptr", "ptr"],
1284
+ returns: "u8"
1285
+ },
1286
+ snapshot_capture: {
1287
+ args: ["ptr", "u64", "ptr", "u64"],
1288
+ returns: "ptr"
1289
+ },
1290
+ snapshot_delta: {
1291
+ args: ["ptr", "ptr"],
1292
+ returns: "ptr"
1293
+ },
1294
+ snapshot_store_delta: {
1295
+ args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
1296
+ returns: "ptr"
1297
+ },
1298
+ snapshot_inspect_delta: {
1299
+ args: ["ptr", "u64"],
1300
+ returns: "ptr"
1301
+ },
1302
+ snapshot_apply_delta: {
1303
+ args: ["ptr", "u64", "ptr", "u64"],
1304
+ returns: "ptr"
1305
+ },
1306
+ snapshot_release: {
1307
+ args: ["ptr"],
1308
+ returns: "void"
1309
+ },
1310
+ runtime_hash_file: {
1311
+ args: ["ptr", "u64"],
1312
+ returns: "ptr"
1313
+ },
1314
+ runtime_hash_tree: {
1315
+ args: ["ptr", "u64"],
1316
+ returns: "ptr"
1317
+ },
1318
+ runtime_prepare_paths: {
1319
+ args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
1320
+ returns: "ptr"
1321
+ },
1322
+ runtime_link_dependency_layer: {
1323
+ args: ["ptr", "u64", "ptr", "u64"],
1324
+ returns: "ptr"
1325
+ },
1326
+ runtime_scan_worktrees: {
1327
+ args: ["ptr", "u64"],
1328
+ returns: "ptr"
1329
+ }
1330
+ });
1331
+ } catch {
1332
+ return null;
1333
+ }
1334
+ }
1335
+ function runNativeRuntimeSidecar(request) {
1336
+ const sidecarSourcePath = resolveNativeRuntimeSidecarSourcePath();
1337
+ if (!sidecarSourcePath) {
1338
+ throw new Error("runtime-native-sidecar.ts source file not found.");
1339
+ }
1340
+ const bunCli = resolveNativeRuntimeSidecarInvocation();
1341
+ const proc = Bun.spawnSync([bunCli.command, sidecarSourcePath, JSON.stringify(request)], {
1342
+ cwd: process.env.RIG_HOST_PROJECT_ROOT?.trim() || process.cwd(),
1343
+ stdout: "pipe",
1344
+ stderr: "pipe",
1345
+ env: {
1346
+ ...process.env,
1347
+ ...bunCli.env,
1348
+ RIG_NATIVE_RUNTIME_SIDECAR: "1"
1349
+ }
1350
+ });
1351
+ if (proc.exitCode !== 0) {
1352
+ throw new Error(proc.stderr.toString() || proc.stdout.toString() || `runtime native sidecar exited ${proc.exitCode}`);
1353
+ }
1354
+ const stdout = proc.stdout.toString().trim();
1355
+ if (!stdout) {
1356
+ throw new Error("runtime native sidecar returned empty output.");
1357
+ }
1358
+ return JSON.parse(stdout);
1359
+ }
1360
+ function resolveNativeRuntimeSidecarInvocation() {
1361
+ const bunPath = Bun.which("bun");
1362
+ if (bunPath) {
1363
+ return { command: bunPath, env: {} };
1364
+ }
1365
+ if (process.execPath?.trim()) {
1366
+ return {
1367
+ command: process.execPath,
1368
+ env: { BUN_BE_BUN: "1" }
1369
+ };
1370
+ }
1371
+ throw new Error("bun is required to run the runtime native sidecar.");
1372
+ }
1373
+
1374
+ // packages/runtime/src/control-plane/native/scope-rules.ts
1375
+ var activeRules = null;
1376
+ function getScopeRules() {
1377
+ return activeRules;
1378
+ }
1379
+
1380
+ // packages/runtime/src/control-plane/native/utils.ts
1381
+ function resolveMonorepoRoot2(projectRoot) {
1382
+ return resolveMonorepoRoot(projectRoot);
1383
+ }
1384
+ var nativeScopeMatcher = null;
1385
+ var scopeRegexCache = new Map;
1386
+ function runCapture(command, cwd, env) {
1387
+ const result = Bun.spawnSync(command, {
1388
+ cwd,
1389
+ env: env ? { ...process.env, ...env } : process.env,
1390
+ stdout: "pipe",
1391
+ stderr: "pipe"
1392
+ });
1393
+ return {
1394
+ exitCode: result.exitCode,
1395
+ stdout: result.stdout.toString(),
1396
+ stderr: result.stderr.toString()
1397
+ };
1398
+ }
1399
+ function unique(values) {
1400
+ return [...new Set(values)];
1401
+ }
1402
+ function normalizeRelativeScopePath(inputPath) {
1403
+ let normalized = inputPath.replace(/^\.\//, "");
1404
+ const rules = getScopeRules();
1405
+ if (rules?.stripPrefixes) {
1406
+ for (const prefix of rules.stripPrefixes) {
1407
+ if (normalized.startsWith(prefix)) {
1408
+ normalized = normalized.slice(prefix.length);
1409
+ }
1410
+ }
1411
+ }
1412
+ return normalized;
1413
+ }
1414
+ function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
1415
+ let normalized = inputPath.replace(/^\.\//, "");
1416
+ if (normalized.startsWith(projectRoot + "/")) {
1417
+ normalized = normalized.slice(projectRoot.length + 1);
1418
+ }
1419
+ if (normalized.startsWith(monorepoRoot + "/")) {
1420
+ normalized = normalized.slice(monorepoRoot.length + 1);
1421
+ }
1422
+ return normalizeRelativeScopePath(normalized);
1423
+ }
1424
+ function scopeMatches(path, scopes) {
1425
+ const matcher = getNativeScopeMatcher();
1426
+ const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
1427
+ for (const scope of scopes) {
1428
+ const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
1429
+ for (const candidatePath of pathVariants) {
1430
+ for (const candidateScope of scopeVariants) {
1431
+ if (candidatePath === candidateScope) {
1432
+ return true;
1433
+ }
1434
+ if (matcher.match(candidateScope, candidatePath)) {
1435
+ return true;
1436
+ }
1437
+ }
1438
+ }
1439
+ }
1440
+ return false;
1441
+ }
1442
+ function getNativeScopeMatcher() {
1443
+ if (nativeScopeMatcher) {
1444
+ return nativeScopeMatcher;
1445
+ }
1446
+ nativeScopeMatcher = createNativeScopeMatcher();
1447
+ return nativeScopeMatcher;
1448
+ }
1449
+ function createNativeScopeMatcher() {
1450
+ const library = requireNativeRuntimeLibrary("scope matching");
1451
+ return {
1452
+ match: (pattern, path) => {
1453
+ const patternBuffer = Buffer.from(`${pattern}\x00`);
1454
+ const pathBuffer = Buffer.from(`${path}\x00`);
1455
+ return library.symbols.rig_scope_match(Number(ptr2(patternBuffer)), Number(ptr2(pathBuffer))) !== 0;
1456
+ }
1457
+ };
1458
+ }
1459
+ // packages/runtime/src/control-plane/runtime/context.ts
1460
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1461
+ import { dirname as dirname7, resolve as resolve7 } from "path";
1462
+ var DEFAULT_RUNTIME_MEMORY_RETRIEVAL = {
1463
+ topK: 5,
1464
+ lexicalWeight: 0.35,
1465
+ vectorWeight: 0.45,
1466
+ recencyWeight: 0.1,
1467
+ confidenceWeight: 0.1
1468
+ };
1469
+ var runtimeContextStringFields = [
1470
+ "runtimeId",
1471
+ "taskId",
1472
+ "role",
1473
+ "workspaceDir",
1474
+ "stateDir",
1475
+ "logsDir",
1476
+ "sessionDir",
1477
+ "sessionFile",
1478
+ "policyFile",
1479
+ "binDir",
1480
+ "createdAt"
1481
+ ];
1482
+ var runtimeContextArrayFields = ["scopes", "validation"];
1483
+ var runtimeContextOptionalStringFields = [
1484
+ "artifactRoot",
1485
+ "hostProjectRoot",
1486
+ "monorepoMainRoot",
1487
+ "monorepoBaseRef",
1488
+ "monorepoBaseCommit"
1489
+ ];
1490
+ function loadRuntimeContext(path) {
1491
+ const absPath = resolve7(path);
1492
+ if (!existsSync6(absPath)) {
1493
+ throw new Error(`RuntimeTaskContext file not found: ${absPath}`);
1494
+ }
1495
+ let raw;
1496
+ try {
1497
+ raw = JSON.parse(readFileSync3(absPath, "utf8"));
1498
+ } catch (err) {
1499
+ throw new Error(`Failed to parse RuntimeTaskContext at ${absPath}: ${String(err)}`);
1500
+ }
1501
+ if (typeof raw !== "object" || raw === null) {
1502
+ throw new Error(`RuntimeTaskContext at ${absPath} is not an object`);
1503
+ }
1504
+ const obj = raw;
1505
+ for (const field of runtimeContextStringFields) {
1506
+ if (typeof obj[field] !== "string" || obj[field].length === 0) {
1507
+ throw new Error(`RuntimeTaskContext field "${field}" must be a non-empty string (at ${absPath})`);
1508
+ }
1509
+ }
1510
+ for (const field of runtimeContextArrayFields) {
1511
+ if (!Array.isArray(obj[field])) {
1512
+ throw new Error(`RuntimeTaskContext field "${field}" must be an array (at ${absPath})`);
1513
+ }
1514
+ if (!obj[field].every((entry) => typeof entry === "string")) {
1515
+ throw new Error(`RuntimeTaskContext field "${field}" must be a string[] (at ${absPath})`);
1516
+ }
1517
+ }
1518
+ for (const field of runtimeContextOptionalStringFields) {
1519
+ if (field in obj && obj[field] !== undefined && typeof obj[field] !== "string") {
1520
+ throw new Error(`RuntimeTaskContext field "${field}" must be a string when present (at ${absPath})`);
1521
+ }
1522
+ }
1523
+ if (obj.browser !== undefined) {
1524
+ if (typeof obj.browser !== "object" || obj.browser === null || Array.isArray(obj.browser)) {
1525
+ throw new Error(`RuntimeTaskContext field "browser" must be an object when present (at ${absPath})`);
1526
+ }
1527
+ const browser = obj.browser;
1528
+ for (const field of [
1529
+ "preset",
1530
+ "mode",
1531
+ "stateDir",
1532
+ "defaultProfile",
1533
+ "effectiveProfile",
1534
+ "defaultAttachUrl",
1535
+ "effectiveAttachUrl",
1536
+ "launchHelper",
1537
+ "checkHelper",
1538
+ "attachInfoHelper",
1539
+ "e2eHelper",
1540
+ "resetProfileHelper"
1541
+ ]) {
1542
+ if (typeof browser[field] !== "string" || browser[field].length === 0) {
1543
+ throw new Error(`RuntimeTaskContext field "browser.${field}" must be a non-empty string (at ${absPath})`);
1544
+ }
1545
+ }
1546
+ for (const field of ["devCommand", "launchCommand", "checkCommand", "e2eCommand"]) {
1547
+ if (browser[field] !== undefined && typeof browser[field] !== "string") {
1548
+ throw new Error(`RuntimeTaskContext field "browser.${field}" must be a string when present (at ${absPath})`);
1549
+ }
1550
+ }
1551
+ if (typeof browser.required !== "boolean") {
1552
+ throw new Error(`RuntimeTaskContext field "browser.required" must be a boolean (at ${absPath})`);
1553
+ }
1554
+ }
1555
+ if (obj.memory !== undefined) {
1556
+ if (typeof obj.memory !== "object" || obj.memory === null || Array.isArray(obj.memory)) {
1557
+ throw new Error(`RuntimeTaskContext field "memory" must be an object when present (at ${absPath})`);
1558
+ }
1559
+ const memory = obj.memory;
1560
+ for (const field of ["canonicalPath", "canonicalRef", "canonicalBaseOid", "hydratedPath"]) {
1561
+ if (typeof memory[field] !== "string" || memory[field].length === 0) {
1562
+ throw new Error(`RuntimeTaskContext field "memory.${field}" must be a non-empty string (at ${absPath})`);
1563
+ }
1564
+ }
1565
+ if (typeof memory.createdFresh !== "boolean") {
1566
+ throw new Error(`RuntimeTaskContext field "memory.createdFresh" must be a boolean (at ${absPath})`);
1567
+ }
1568
+ if (typeof memory.retrieval !== "object" || memory.retrieval === null || Array.isArray(memory.retrieval)) {
1569
+ throw new Error(`RuntimeTaskContext field "memory.retrieval" must be an object (at ${absPath})`);
1570
+ }
1571
+ const retrieval = memory.retrieval;
1572
+ for (const field of ["topK", "lexicalWeight", "vectorWeight", "recencyWeight", "confidenceWeight"]) {
1573
+ if (typeof retrieval[field] !== "number" || Number.isNaN(retrieval[field])) {
1574
+ throw new Error(`RuntimeTaskContext field "memory.retrieval.${field}" must be a number (at ${absPath})`);
1575
+ }
1576
+ }
1577
+ }
1578
+ if (obj.initialDirtyFiles !== undefined) {
1579
+ if (typeof obj.initialDirtyFiles !== "object" || obj.initialDirtyFiles === null || Array.isArray(obj.initialDirtyFiles)) {
1580
+ throw new Error(`RuntimeTaskContext field "initialDirtyFiles" must be an object when present (at ${absPath})`);
1581
+ }
1582
+ const dirtyFiles = obj.initialDirtyFiles;
1583
+ for (const key of ["project", "monorepo"]) {
1584
+ if (dirtyFiles[key] === undefined) {
1585
+ continue;
1586
+ }
1587
+ if (!Array.isArray(dirtyFiles[key]) || !dirtyFiles[key].every((entry) => typeof entry === "string")) {
1588
+ throw new Error(`RuntimeTaskContext field "initialDirtyFiles.${key}" must be a string[] when present (at ${absPath})`);
1589
+ }
1590
+ }
1591
+ }
1592
+ if (obj.initialHeadCommits !== undefined) {
1593
+ if (typeof obj.initialHeadCommits !== "object" || obj.initialHeadCommits === null || Array.isArray(obj.initialHeadCommits)) {
1594
+ throw new Error(`RuntimeTaskContext field "initialHeadCommits" must be an object when present (at ${absPath})`);
1595
+ }
1596
+ const headCommits = obj.initialHeadCommits;
1597
+ for (const key of ["project", "monorepo"]) {
1598
+ if (headCommits[key] === undefined) {
1599
+ continue;
1600
+ }
1601
+ if (typeof headCommits[key] !== "string") {
1602
+ throw new Error(`RuntimeTaskContext field "initialHeadCommits.${key}" must be a string when present (at ${absPath})`);
1603
+ }
1604
+ }
1605
+ }
1606
+ return obj;
1607
+ }
1608
+
1609
+ // packages/runtime/src/control-plane/memory-sync/query.ts
1610
+ var DEFAULT_RESULT_LIMIT = DEFAULT_RUNTIME_MEMORY_RETRIEVAL.topK;
1611
+ var DAY_MS = 24 * 60 * 60 * 1000;
1612
+ // packages/runtime/src/build-time-config.ts
1613
+ function normalizeBuildConfig(value) {
1614
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1615
+ return {};
1616
+ }
1617
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
1618
+ }
1619
+ function readBuildConfig() {
1620
+ if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
1621
+ return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
1622
+ }
1623
+ const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
1624
+ if (!raw) {
1625
+ return {};
1626
+ }
1627
+ try {
1628
+ return normalizeBuildConfig(JSON.parse(raw));
1629
+ } catch {
1630
+ return {};
1631
+ }
1632
+ }
1633
+
1634
+ // packages/runtime/src/control-plane/runtime/tooling/shell.ts
1635
+ import { tmpdir as tmpdir3 } from "os";
1636
+ import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
1637
+ var sharedNativeShellOutputDir = resolve8(tmpdir3(), "rig-native");
1638
+ var sharedNativeShellOutputPath = resolve8(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1639
+ // packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
1640
+ import { tmpdir as tmpdir4 } from "os";
1641
+ import { basename as basename4, dirname as dirname9, resolve as resolve9 } from "path";
1642
+ var sharedNativeToolsOutputDir = resolve9(tmpdir4(), "rig-native");
1643
+ var sharedNativeToolsOutputPath = resolve9(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1644
+ // packages/runtime/src/control-plane/plugin-host-context.ts
1645
+ import { createPluginHost } from "@rig/core";
1646
+ import { loadConfig } from "@rig/core/load-config";
1647
+
1648
+ // packages/runtime/src/control-plane/repos/registry.ts
1649
+ var MANAGED_REPOS = new Map;
1650
+
1651
+ // packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
1652
+ var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
1653
+
1654
+ // packages/runtime/src/control-plane/state-sync/types.ts
1655
+ var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
1656
+ "draft",
1657
+ "open",
1658
+ "ready",
1659
+ "queued",
1660
+ "in_progress",
1661
+ "under_review",
1662
+ "blocked",
1663
+ "completed",
1664
+ "cancelled"
1665
+ ]);
1666
+ // packages/runtime/src/control-plane/repos/layout.ts
1667
+ init_layout();
1668
+ // packages/runtime/src/control-plane/state-sync/reconcile.ts
1669
+ var STALE_CLAIM_MS = 24 * 60 * 60 * 1000;
1670
+ // packages/runtime/src/control-plane/native/validator.ts
1671
+ init_layout();
1672
+
1673
+ // packages/runtime/src/control-plane/runtime/sandbox-utils.ts
1674
+ init_utils();
1675
+
1676
+ // packages/runtime/src/control-plane/native/git-ops.ts
1677
+ var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
1678
+ "changed-files.txt",
1679
+ "contract-changes.md",
1680
+ "decision-log.md",
1681
+ "git-state.txt",
1682
+ "next-actions.md",
1683
+ "pr-state.json",
1684
+ "task-result.json",
1685
+ "validation-summary.json"
1686
+ ]);
1687
+
1688
+ // packages/runtime/src/control-plane/provider/runtime-instructions.ts
1689
+ var CLAUDE_ROUTER_TOOL_NAMES = [
1690
+ "`mcp__rig_runtime_tools__read`",
1691
+ "`mcp__rig_runtime_tools__write`",
1692
+ "`mcp__rig_runtime_tools__edit`",
1693
+ "`mcp__rig_runtime_tools__glob`",
1694
+ "`mcp__rig_runtime_tools__grep`"
1695
+ ].join(", ");
1696
+ var CODEX_DYNAMIC_TOOL_NAMES = [
1697
+ "`shell`",
1698
+ "`read`",
1699
+ "`write`",
1700
+ "`edit`",
1701
+ "`glob`",
1702
+ "`grep`"
1703
+ ].join(", ");
1704
+
1705
+ // packages/runtime/src/control-plane/native/task-ops.ts
1706
+ var BUILD_CONFIG = readBuildConfig();
1707
+ var BAKED_INFO_OUTPUT = BUILD_CONFIG.AGENT_INFO_OUTPUT ?? "";
1708
+ var BAKED_DEPS_OUTPUT = BUILD_CONFIG.AGENT_DEPS_OUTPUT ?? "";
1709
+ var BAKED_STATUS_OUTPUT = BUILD_CONFIG.AGENT_STATUS_OUTPUT ?? "";
1710
+ var REOPENABLE_TASK_STATUSES = new Set(["completed", "cancelled", "blocked"]);
1711
+ var GENERATED_TASK_ARTIFACT_FILES = new Set([
1712
+ "changed-files.txt",
1713
+ "decision-log.md",
1714
+ "next-actions.md",
1715
+ "task-result.json",
1716
+ "validation-summary.json",
1717
+ "review-feedback.md",
1718
+ "review-state.json",
1719
+ "review-status.txt",
1720
+ "review-greptile-raw.json",
1721
+ "pr-state.json",
1722
+ "git-state.txt"
1723
+ ]);
1724
+
1725
+ // packages/runtime/src/control-plane/runtime/isolation/index.ts
1726
+ init_layout();
1727
+
1728
+ // packages/runtime/src/control-plane/runtime/overlay.ts
1729
+ init_layout();
1730
+
1731
+ // packages/runtime/src/control-plane/runtime/isolation/shared.ts
1732
+ import { existsSync as existsSync7, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
1733
+ var generatedCredentialFiles = new Set;
1734
+ function resolveMonorepoRoot3(projectRoot) {
1735
+ return resolveMonorepoRoot2(projectRoot);
1736
+ }
1737
+ function isRuntimeGatewayGitPath(candidate) {
1738
+ return /\/\.rig\/bin\/git$/.test(candidate.replace(/\\/g, "/"));
1739
+ }
1740
+ function resolveHostGitBinary() {
1741
+ const candidates = [
1742
+ process.env.RIG_GIT_BIN?.trim() || "",
1743
+ "/usr/bin/git",
1744
+ "/opt/homebrew/bin/git",
1745
+ "/usr/local/bin/git"
1746
+ ];
1747
+ const bunResolved = Bun.which("git");
1748
+ if (bunResolved && !isRuntimeGatewayGitPath(bunResolved)) {
1749
+ candidates.push(bunResolved);
1750
+ }
1751
+ for (const candidate of candidates) {
1752
+ if (!candidate || isRuntimeGatewayGitPath(candidate)) {
1753
+ continue;
1754
+ }
1755
+ if (existsSync7(candidate)) {
1756
+ return candidate;
1757
+ }
1758
+ }
1759
+ return "git";
1760
+ }
1761
+ async function runGitCommand(repoRoot, args) {
1762
+ const gitBinary = resolveHostGitBinary();
1763
+ return Bun.$`${gitBinary} -C ${repoRoot} ${args}`.quiet().nothrow();
1764
+ }
1765
+ function sanitizeRuntimeRefSegment(value) {
1766
+ const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
1767
+ return (sanitized || "runtime").slice(0, 64);
1768
+ }
1769
+ function taskRuntimeId(taskId) {
1770
+ return `task-${taskId}`;
1771
+ }
1772
+
1773
+ // packages/runtime/src/control-plane/runtime/isolation/home.ts
1774
+ var GITHUB_KNOWN_HOSTS = [
1775
+ "github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
1776
+ "github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
1777
+ "github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
1778
+ ].join(`
1779
+ `);
1780
+
1781
+ // packages/runtime/src/control-plane/runtime/tooling/claude-router.ts
1782
+ if (false) {}
1783
+ // packages/runtime/src/control-plane/runtime/isolation/discovery.ts
1784
+ init_layout();
1785
+ import { existsSync as existsSync8 } from "fs";
1786
+ import { resolve as resolve11 } from "path";
1787
+
1788
+ // packages/runtime/src/control-plane/runtime/isolation/worktree.ts
1789
+ async function cleanupRuntimeWorktree(monorepoRoot, workspaceDir) {
1790
+ await runGitCommand(monorepoRoot, ["worktree", "remove", "--force", workspaceDir]);
1791
+ }
1792
+ function runtimeWorktreeNameFromRuntimeId(runtimeId) {
1793
+ return sanitizeRuntimeRefSegment(runtimeId.replace(/^task-/, ""));
1794
+ }
1795
+
1796
+ // packages/runtime/src/control-plane/runtime/tooling/claude-router-binary.ts
1797
+ import { tmpdir as tmpdir5 } from "os";
1798
+ import { dirname as dirname10, resolve as resolve10 } from "path";
1799
+ var sharedRouterOutputDir = resolve10(tmpdir5(), "rig-native");
1800
+ var sharedRouterOutputPath = resolve10(sharedRouterOutputDir, `rig-tool-router-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1801
+ // packages/runtime/src/control-plane/runtime/isolation/toolchain.ts
1802
+ function runtimeWorktreeId(workspaceDir) {
1803
+ return workspaceDir.split("/").at(-1) || "runtime";
1804
+ }
1805
+
1806
+ // packages/runtime/src/control-plane/runtime/isolation/discovery.ts
1807
+ async function readRuntimeMetadata(metadataPath) {
1808
+ if (!existsSync8(metadataPath)) {
1809
+ return null;
1810
+ }
1811
+ try {
1812
+ return await Bun.file(metadataPath).json();
1813
+ } catch {
1814
+ return null;
1815
+ }
1816
+ }
1817
+ async function listAgentRuntimes(projectRoot) {
1818
+ const runtimes = [];
1819
+ const monorepoRoot = resolveMonorepoRoot3(projectRoot);
1820
+ const worktreesRoot = resolve11(monorepoRoot, ".worktrees");
1821
+ if (!existsSync8(worktreesRoot)) {
1822
+ return [];
1823
+ }
1824
+ const workspaces = runtimeScanWorktreesNative(worktreesRoot);
1825
+ for (let scan of workspaces) {
1826
+ let workspacePath = scan.workspaceDir;
1827
+ const workspaceLayout = resolveRuntimeWorkspaceLayout(workspacePath);
1828
+ const rootDir = scan.runtimeDir || workspaceLayout.runtimeDir;
1829
+ const metadataPath = scan.metadataPath || resolve11(rootDir, "runtime.json");
1830
+ const contextFile = scan.contextPath || workspaceLayout.contextPath;
1831
+ let mode = "worktree";
1832
+ let createdAt = new Date().toISOString();
1833
+ let taskId = "";
1834
+ let runtimeId = "";
1835
+ let binDir = workspaceLayout.binDir;
1836
+ let logsDir = workspaceLayout.logsDir;
1837
+ let stateDir = workspaceLayout.stateDir;
1838
+ let sessionDir = workspaceLayout.sessionDir;
1839
+ const metadata = await readRuntimeMetadata(metadataPath);
1840
+ if (metadata) {
1841
+ runtimeId = metadata.id;
1842
+ mode = metadata.mode;
1843
+ createdAt = metadata.createdAt;
1844
+ }
1845
+ if (existsSync8(contextFile)) {
1846
+ try {
1847
+ const ctx = loadRuntimeContext(contextFile);
1848
+ taskId = ctx.taskId;
1849
+ workspacePath = ctx.workspaceDir;
1850
+ binDir = ctx.binDir;
1851
+ logsDir = ctx.logsDir;
1852
+ stateDir = ctx.stateDir;
1853
+ sessionDir = ctx.sessionDir;
1854
+ } catch {}
1855
+ }
1856
+ if (!runtimeId) {
1857
+ runtimeId = taskRuntimeId(taskId || runtimeWorktreeId(workspacePath));
1858
+ }
1859
+ runtimes.push({
1860
+ id: runtimeId,
1861
+ taskId,
1862
+ mode,
1863
+ rootDir,
1864
+ workspaceDir: workspacePath,
1865
+ homeDir: resolve11(rootDir, "home"),
1866
+ tmpDir: resolve11(rootDir, "tmp"),
1867
+ cacheDir: resolve11(rootDir, "cache"),
1868
+ logsDir,
1869
+ stateDir,
1870
+ sessionDir,
1871
+ claudeHomeDir: resolve11(rootDir, "home", ".claude"),
1872
+ contextFile,
1873
+ binDir,
1874
+ createdAt
1875
+ });
1876
+ }
1877
+ return runtimes;
1878
+ }
1879
+ function runtimeRootForCleanup(projectRoot, runtimeId) {
1880
+ let monorepoRoot = null;
1881
+ try {
1882
+ monorepoRoot = resolveMonorepoRoot3(projectRoot);
1883
+ } catch {}
1884
+ const workspaceRootCandidate = monorepoRoot ?? projectRoot;
1885
+ const initialWorkspaceDir = resolve11(workspaceRootCandidate, ".worktrees", runtimeWorktreeNameFromRuntimeId(runtimeId));
1886
+ const runtimeRoot = resolveRuntimeWorkspaceLayout(initialWorkspaceDir).runtimeDir;
1887
+ return { monorepoRoot, initialWorkspaceDir, runtimeRoot };
1888
+ }
1889
+
1890
+ // packages/runtime/src/control-plane/runtime/isolation/runner.ts
1891
+ import { existsSync as existsSync10, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
1892
+ import { basename as basename5, resolve as resolve13 } from "path";
1893
+
1894
+ // packages/runtime/src/control-plane/runtime/guard.ts
1895
+ import { optimizeNextInvocation } from "bun:jsc";
1896
+ import { existsSync as existsSync9, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
1897
+ import { resolve as resolve12 } from "path";
1898
+
1899
+ // packages/runtime/src/control-plane/runtime/guard-types.ts
1900
+ var POLICY_VERSION = 1;
1901
+
1902
+ // packages/runtime/src/control-plane/runtime/guard.ts
1903
+ var DEFAULT_SCOPE = {
1904
+ fail_closed: true,
1905
+ harness_paths_exempt: true,
1906
+ runtime_paths_exempt: true
1907
+ };
1908
+ var DEFAULT_SANDBOX = {
1909
+ mode: "enforce",
1910
+ network: true,
1911
+ read_deny: [],
1912
+ write_allow_from_runtime: true
1913
+ };
1914
+ var DEFAULT_ISOLATION = {
1915
+ default_mode: "worktree",
1916
+ repo_symlink_fallback: false,
1917
+ strict_provisioning: true,
1918
+ fail_closed_on_provision_error: true
1919
+ };
1920
+ var DEFAULT_COMPLETION = {
1921
+ derive_checks_from_scope: true,
1922
+ checks: [],
1923
+ typescript_config_probe: ["tsconfig.json"],
1924
+ eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
1925
+ };
1926
+ var DEFAULT_RUNTIME_IMAGE = {
1927
+ deps: {
1928
+ monorepo_install: false,
1929
+ hp_next_install: false
1930
+ },
1931
+ plugins_require_binaries: true
1932
+ };
1933
+ var DEFAULT_RUNTIME_SNAPSHOT = {
1934
+ enabled: true
1935
+ };
1936
+ function defaultPolicy() {
1937
+ return {
1938
+ version: POLICY_VERSION,
1939
+ mode: "enforce",
1940
+ scope: { ...DEFAULT_SCOPE },
1941
+ rules: [],
1942
+ sandbox: { ...DEFAULT_SANDBOX },
1943
+ isolation: { ...DEFAULT_ISOLATION },
1944
+ completion: { ...DEFAULT_COMPLETION },
1945
+ runtime_image: {
1946
+ deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
1947
+ plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
1948
+ },
1949
+ runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
1950
+ };
1951
+ }
1952
+ var policyCache = null;
1953
+ var policyCachePath = null;
1954
+ var seededPolicyConfig = null;
1955
+ var compiledRegexCache = new Map;
1956
+ function loadPolicy(projectRoot) {
1957
+ if (seededPolicyConfig) {
1958
+ return seededPolicyConfig;
1959
+ }
1960
+ const configPath = resolve12(projectRoot, "rig/policy/policy.json");
1961
+ if (!existsSync9(configPath)) {
1962
+ return defaultPolicy();
1963
+ }
1964
+ let mtimeMs;
1965
+ try {
1966
+ mtimeMs = statSync3(configPath).mtimeMs;
1967
+ } catch {
1968
+ return defaultPolicy();
1969
+ }
1970
+ if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
1971
+ return policyCache.config;
1972
+ }
1973
+ let parsed;
1974
+ try {
1975
+ parsed = JSON.parse(readFileSync5(configPath, "utf-8"));
1976
+ } catch {
1977
+ return defaultPolicy();
1978
+ }
1979
+ const config = mergeWithDefaults(parsed);
1980
+ policyCache = { mtimeMs, config };
1981
+ policyCachePath = configPath;
1982
+ return config;
1983
+ }
1984
+ function mergeWithDefaults(parsed) {
1985
+ const base = defaultPolicy();
1986
+ if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
1987
+ base.mode = parsed.mode;
1988
+ }
1989
+ if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
1990
+ const s = parsed.scope;
1991
+ if (typeof s.fail_closed === "boolean")
1992
+ base.scope.fail_closed = s.fail_closed;
1993
+ if (typeof s.harness_paths_exempt === "boolean")
1994
+ base.scope.harness_paths_exempt = s.harness_paths_exempt;
1995
+ if (typeof s.runtime_paths_exempt === "boolean")
1996
+ base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
1997
+ }
1998
+ if (Array.isArray(parsed.rules)) {
1999
+ base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
2000
+ }
2001
+ if (Array.isArray(parsed.deny) && base.rules.length === 0) {
2002
+ base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
2003
+ }
2004
+ if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
2005
+ const sb = parsed.sandbox;
2006
+ if (typeof sb.mode === "string" && isValidMode(sb.mode))
2007
+ base.sandbox.mode = sb.mode;
2008
+ if (typeof sb.network === "boolean")
2009
+ base.sandbox.network = sb.network;
2010
+ if (Array.isArray(sb.read_deny))
2011
+ base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
2012
+ if (typeof sb.write_allow_from_runtime === "boolean")
2013
+ base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
2014
+ }
2015
+ if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
2016
+ const iso = parsed.isolation;
2017
+ if (iso.default_mode === "worktree")
2018
+ base.isolation.default_mode = iso.default_mode;
2019
+ if (typeof iso.repo_symlink_fallback === "boolean")
2020
+ base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
2021
+ if (typeof iso.strict_provisioning === "boolean")
2022
+ base.isolation.strict_provisioning = iso.strict_provisioning;
2023
+ if (typeof iso.fail_closed_on_provision_error === "boolean")
2024
+ base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
2025
+ }
2026
+ if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
2027
+ const comp = parsed.completion;
2028
+ if (typeof comp.derive_checks_from_scope === "boolean")
2029
+ base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
2030
+ if (Array.isArray(comp.checks))
2031
+ base.completion.checks = comp.checks.filter((v) => typeof v === "string");
2032
+ if (Array.isArray(comp.typescript_config_probe))
2033
+ base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
2034
+ if (Array.isArray(comp.eslint_config_probe))
2035
+ base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
2036
+ }
2037
+ if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
2038
+ const runtimeImage = parsed.runtime_image;
2039
+ if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
2040
+ const deps = runtimeImage.deps;
2041
+ if (typeof deps.monorepo_install === "boolean") {
2042
+ base.runtime_image.deps.monorepo_install = deps.monorepo_install;
2043
+ }
2044
+ if (typeof deps.hp_next_install === "boolean") {
2045
+ base.runtime_image.deps.hp_next_install = deps.hp_next_install;
2046
+ }
2047
+ }
2048
+ if (typeof runtimeImage.plugins_require_binaries === "boolean") {
2049
+ base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
2050
+ }
2051
+ }
2052
+ if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
2053
+ const runtimeSnapshot = parsed.runtime_snapshot;
2054
+ if (typeof runtimeSnapshot.enabled === "boolean") {
2055
+ base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
2056
+ }
2057
+ }
2058
+ return base;
2059
+ }
2060
+ function isValidMode(value) {
2061
+ return value === "off" || value === "observe" || value === "enforce";
2062
+ }
2063
+ function isValidRule(value) {
2064
+ if (!value || typeof value !== "object" || Array.isArray(value))
2065
+ return false;
2066
+ const r = value;
2067
+ return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
2068
+ }
2069
+ function migrateLegacyDeny(deny) {
2070
+ const rules = [];
2071
+ for (const entry of deny) {
2072
+ if (typeof entry.id !== "string")
2073
+ continue;
2074
+ const match = {};
2075
+ if (typeof entry.pattern === "string")
2076
+ match.pattern = entry.pattern;
2077
+ if (typeof entry.regex === "string")
2078
+ match.regex = entry.regex;
2079
+ if (!match.pattern && !match.regex)
2080
+ continue;
2081
+ rules.push({
2082
+ id: entry.id,
2083
+ category: "command",
2084
+ match,
2085
+ action: "block",
2086
+ ...typeof entry.description === "string" ? { description: entry.description } : {}
2087
+ });
2088
+ }
2089
+ return rules;
2090
+ }
2091
+ function precompilePolicyRuleRegexes(rules) {
2092
+ return rules.map((rule) => {
2093
+ const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
2094
+ const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
2095
+ return {
2096
+ ...rule,
2097
+ ...compiledRegex ? { compiledRegex } : {},
2098
+ ...compiledUnlessRegex ? { compiledUnlessRegex } : {}
2099
+ };
2100
+ });
2101
+ }
2102
+ function getRegexUnsafeReason(pattern) {
2103
+ if (pattern.length > 512) {
2104
+ return "pattern exceeds max safe length (512 chars)";
2105
+ }
2106
+ if (/\\[1-9]/.test(pattern)) {
2107
+ return "pattern uses backreferences";
2108
+ }
2109
+ if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
2110
+ return "pattern contains nested quantifiers";
2111
+ }
2112
+ if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
2113
+ return "pattern contains nested broad quantifiers";
2114
+ }
2115
+ return null;
2116
+ }
2117
+ function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
2118
+ const cached = compiledRegexCache.get(pattern);
2119
+ if (cached !== undefined) {
2120
+ return cached ?? undefined;
2121
+ }
2122
+ const unsafeReason = getRegexUnsafeReason(pattern);
2123
+ if (unsafeReason) {
2124
+ if (logOnFailure) {
2125
+ console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
2126
+ }
2127
+ compiledRegexCache.set(pattern, null);
2128
+ return;
2129
+ }
2130
+ try {
2131
+ const compiled = new RegExp(pattern);
2132
+ compiledRegexCache.set(pattern, compiled);
2133
+ return compiled;
2134
+ } catch (error) {
2135
+ if (logOnFailure) {
2136
+ const message = error instanceof Error ? error.message : String(error);
2137
+ console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
2138
+ }
2139
+ compiledRegexCache.set(pattern, null);
2140
+ return;
2141
+ }
2142
+ }
2143
+ function matchRule(rule, input) {
2144
+ const { match } = rule;
2145
+ if (match.pattern && input.includes(match.pattern)) {
2146
+ return true;
2147
+ }
2148
+ if (match.regex) {
2149
+ const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
2150
+ if (!compiled) {
2151
+ return false;
2152
+ }
2153
+ try {
2154
+ return compiled.test(input);
2155
+ } catch {
2156
+ return false;
2157
+ }
2158
+ }
2159
+ return false;
2160
+ }
2161
+ function matchRuleUnless(rule, command, taskId) {
2162
+ if (!rule.unless)
2163
+ return false;
2164
+ if (rule.unless.regex) {
2165
+ const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
2166
+ if (!compiled) {
2167
+ return false;
2168
+ }
2169
+ try {
2170
+ if (compiled.test(command))
2171
+ return true;
2172
+ } catch {}
2173
+ }
2174
+ if (rule.unless.task_in && taskId) {
2175
+ if (rule.unless.task_in.includes(taskId))
2176
+ return true;
2177
+ }
2178
+ return false;
2179
+ }
2180
+ function resolveAction(mode, matched) {
2181
+ if (matched.length === 0)
2182
+ return "allow";
2183
+ if (mode === "off")
2184
+ return "allow";
2185
+ if (mode === "observe")
2186
+ return "warn";
2187
+ return "block";
2188
+ }
2189
+ function resolveAbsolutePath(projectRoot, rawPath) {
2190
+ if (rawPath.startsWith("/"))
2191
+ return resolve12(rawPath);
2192
+ return resolve12(projectRoot, rawPath);
2193
+ }
2194
+ function isHarnessPath(projectRoot, rawPath) {
2195
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
2196
+ const managedRoots = [
2197
+ resolve12(projectRoot, "rig"),
2198
+ resolve12(projectRoot, ".rig"),
2199
+ resolve12(projectRoot, "artifacts")
2200
+ ];
2201
+ return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
2202
+ }
2203
+ function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
2204
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
2205
+ if (taskWorkspace) {
2206
+ const workspaceRigRoot = resolve12(taskWorkspace, ".rig");
2207
+ const workspaceArtifactsRoot = resolve12(taskWorkspace, "artifacts");
2208
+ if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
2209
+ return true;
2210
+ }
2211
+ }
2212
+ const runtimeRoot = resolve12(projectRoot, ".rig/runtime/agents");
2213
+ return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
2214
+ }
2215
+ function isTestFile(path) {
2216
+ return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
2217
+ }
2218
+ function evaluate(context) {
2219
+ const policy = loadPolicy(context.projectRoot);
2220
+ switch (context.evaluation.type) {
2221
+ case "tool-call":
2222
+ return evaluateToolCall(policy, context);
2223
+ case "command":
2224
+ return evaluateCommand(policy, context);
2225
+ case "content-write":
2226
+ return evaluateContent(policy, context);
2227
+ case "file-access":
2228
+ return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
2229
+ }
2230
+ }
2231
+ function evaluateScope(policy, context, filePath, access) {
2232
+ const allowed = () => ({
2233
+ allowed: true,
2234
+ matchedRules: [],
2235
+ action: "allow",
2236
+ failClosed: false
2237
+ });
2238
+ if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
2239
+ return allowed();
2240
+ }
2241
+ if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
2242
+ return allowed();
2243
+ }
2244
+ if (!context.taskId) {
2245
+ if (access === "write" && policy.scope.fail_closed) {
2246
+ return {
2247
+ allowed: false,
2248
+ matchedRules: [],
2249
+ action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
2250
+ failClosed: true
2251
+ };
2252
+ }
2253
+ return allowed();
2254
+ }
2255
+ const scopes = context.taskScopes || [];
2256
+ if (scopes.length === 0) {
2257
+ return allowed();
2258
+ }
2259
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
2260
+ const absPath = resolve12(filePath);
2261
+ if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
2262
+ const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
2263
+ const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
2264
+ return {
2265
+ allowed: policy.mode !== "enforce",
2266
+ matchedRules: matched2,
2267
+ action: resolveAction(policy.mode, matched2),
2268
+ failClosed: false
2269
+ };
2270
+ }
2271
+ }
2272
+ const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
2273
+ let normalizedPath = filePath;
2274
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
2275
+ normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
2276
+ }
2277
+ normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
2278
+ if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
2279
+ return allowed();
2280
+ }
2281
+ const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
2282
+ const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
2283
+ return {
2284
+ allowed: policy.mode !== "enforce",
2285
+ matchedRules: matched,
2286
+ action: resolveAction(policy.mode, matched),
2287
+ failClosed: false
2288
+ };
2289
+ }
2290
+ function evaluateCommand(policy, context) {
2291
+ const evaluation = context.evaluation;
2292
+ if (evaluation.type !== "command") {
2293
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
2294
+ }
2295
+ const command = evaluation.command;
2296
+ const matchedRules = [];
2297
+ for (const rule of policy.rules) {
2298
+ if (rule.category !== "command")
2299
+ continue;
2300
+ if (!matchRule(rule, command))
2301
+ continue;
2302
+ if (matchRuleUnless(rule, command, context.taskId))
2303
+ continue;
2304
+ matchedRules.push({
2305
+ id: rule.id,
2306
+ category: rule.category,
2307
+ description: rule.description,
2308
+ reason: rule.description || `Matched rule ${rule.id}`
2309
+ });
2310
+ }
2311
+ const writeTarget = extractWriteTarget(command);
2312
+ if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
2313
+ const scopeResult = evaluateScope(policy, context, writeTarget, "write");
2314
+ if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
2315
+ matchedRules.push(...scopeResult.matchedRules);
2316
+ }
2317
+ }
2318
+ const action = resolveAction(policy.mode, matchedRules);
2319
+ return {
2320
+ allowed: action !== "block",
2321
+ matchedRules,
2322
+ action,
2323
+ failClosed: false
2324
+ };
2325
+ }
2326
+ function extractWriteTarget(command) {
2327
+ const redirect = command.match(/>>?\s+([^\s;|&]+)/);
2328
+ if (redirect?.[1])
2329
+ return redirect[1];
2330
+ const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
2331
+ if (tee?.[2])
2332
+ return tee[2];
2333
+ return "";
2334
+ }
2335
+ function evaluateContent(policy, context) {
2336
+ const evaluation = context.evaluation;
2337
+ if (evaluation.type !== "content-write") {
2338
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
2339
+ }
2340
+ const { content, file_path } = evaluation;
2341
+ const matchedRules = [];
2342
+ const scopeResult = evaluateScope(policy, context, file_path, "write");
2343
+ if (scopeResult.matchedRules.length > 0) {
2344
+ matchedRules.push(...scopeResult.matchedRules);
2345
+ }
2346
+ for (const rule of policy.rules) {
2347
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
2348
+ continue;
2349
+ if (rule.applies_to === "test-files" && !isTestFile(file_path))
2350
+ continue;
2351
+ if (!matchRule(rule, content))
2352
+ continue;
2353
+ if (matchRuleUnless(rule, content, context.taskId))
2354
+ continue;
2355
+ matchedRules.push({
2356
+ id: rule.id,
2357
+ category: rule.category,
2358
+ description: rule.description,
2359
+ reason: rule.description || `Matched rule ${rule.id}`
2360
+ });
2361
+ }
2362
+ const action = resolveAction(policy.mode, matchedRules);
2363
+ return {
2364
+ allowed: action !== "block",
2365
+ matchedRules,
2366
+ action,
2367
+ failClosed: false
2368
+ };
2369
+ }
2370
+ function evaluateToolCall(policy, context) {
2371
+ const evaluation = context.evaluation;
2372
+ if (evaluation.type !== "tool-call") {
2373
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
2374
+ }
2375
+ const { tool_name, tool_input } = evaluation;
2376
+ const allMatched = [];
2377
+ const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
2378
+ for (const fp of filePaths) {
2379
+ const access = isWriteTool(tool_name) ? "write" : "read";
2380
+ const scopeResult = evaluateScope(policy, context, fp, access);
2381
+ if (scopeResult.matchedRules.length > 0) {
2382
+ allMatched.push(...scopeResult.matchedRules);
2383
+ }
2384
+ }
2385
+ const content = extractContentFromToolInput(tool_input);
2386
+ if (content) {
2387
+ const filePath = filePaths[0] || "";
2388
+ const contentContext = {
2389
+ ...context,
2390
+ evaluation: { type: "content-write", file_path: filePath, content }
2391
+ };
2392
+ const contentPolicy = loadPolicy(context.projectRoot);
2393
+ for (const rule of contentPolicy.rules) {
2394
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
2395
+ continue;
2396
+ if (rule.applies_to === "test-files" && !isTestFile(filePath))
2397
+ continue;
2398
+ if (!matchRule(rule, content))
2399
+ continue;
2400
+ if (matchRuleUnless(rule, content, context.taskId))
2401
+ continue;
2402
+ allMatched.push({
2403
+ id: rule.id,
2404
+ category: rule.category,
2405
+ description: rule.description,
2406
+ reason: rule.description || `Matched rule ${rule.id}`
2407
+ });
2408
+ }
2409
+ }
2410
+ if (tool_name === "Bash") {
2411
+ const command = String(tool_input.command || tool_input.cmd || "");
2412
+ if (command) {
2413
+ const cmdContext = {
2414
+ ...context,
2415
+ evaluation: { type: "command", command }
2416
+ };
2417
+ const cmdResult = evaluateCommand(policy, cmdContext);
2418
+ if (cmdResult.matchedRules.length > 0) {
2419
+ allMatched.push(...cmdResult.matchedRules);
2420
+ }
2421
+ }
2422
+ }
2423
+ const seen = new Set;
2424
+ const deduplicated = [];
2425
+ for (const rule of allMatched) {
2426
+ if (!seen.has(rule.id)) {
2427
+ seen.add(rule.id);
2428
+ deduplicated.push(rule);
2429
+ }
2430
+ }
2431
+ const action = resolveAction(policy.mode, deduplicated);
2432
+ return {
2433
+ allowed: action !== "block",
2434
+ matchedRules: deduplicated,
2435
+ action,
2436
+ failClosed: false
2437
+ };
2438
+ }
2439
+ function isWriteTool(toolName) {
2440
+ return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
2441
+ }
2442
+ function extractFilePathsFromToolInput(toolName, input) {
2443
+ const paths = [];
2444
+ const add = (value) => {
2445
+ if (typeof value === "string" && value.trim()) {
2446
+ paths.push(value.trim());
2447
+ }
2448
+ };
2449
+ if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
2450
+ add(input.file_path);
2451
+ add(input.path);
2452
+ } else if (toolName === "Glob") {
2453
+ add(input.path);
2454
+ } else if (toolName === "Grep") {
2455
+ add(input.path);
2456
+ } else {
2457
+ add(input.file_path);
2458
+ add(input.path);
2459
+ }
2460
+ return paths;
2461
+ }
2462
+ function extractContentFromToolInput(input) {
2463
+ if (typeof input.content === "string")
2464
+ return input.content;
2465
+ if (typeof input.new_string === "string")
2466
+ return input.new_string;
2467
+ return "";
2468
+ }
2469
+ var guardHotPathPrimed = false;
2470
+ function primeGuardHotPaths() {
2471
+ if (guardHotPathPrimed) {
2472
+ return;
2473
+ }
2474
+ guardHotPathPrimed = true;
2475
+ try {
2476
+ optimizeNextInvocation(matchRule);
2477
+ optimizeNextInvocation(evaluate);
2478
+ } catch {}
2479
+ }
2480
+ primeGuardHotPaths();
2481
+
2482
+ // packages/runtime/src/control-plane/runtime/sandbox/backend.ts
2483
+ init_utils();
2484
+ // packages/runtime/src/control-plane/runtime/isolation/runner.ts
2485
+ init_layout();
2486
+ async function cleanupAgentRuntime(options) {
2487
+ const { monorepoRoot, initialWorkspaceDir, runtimeRoot } = runtimeRootForCleanup(options.projectRoot, options.id);
2488
+ const metadataPath = resolve13(runtimeRoot, "runtime.json");
2489
+ if (!existsSync10(runtimeRoot)) {
2490
+ return;
2491
+ }
2492
+ let mode = "worktree";
2493
+ let workspaceDir = "";
2494
+ const metadata = await readRuntimeMetadata2(metadataPath);
2495
+ const metadataMatchesRequestedRuntime = metadata !== null && metadata.id === options.id && resolve13(metadata.workspaceDir) === resolve13(initialWorkspaceDir);
2496
+ if (metadata && metadataMatchesRequestedRuntime) {
2497
+ mode = metadata.mode;
2498
+ workspaceDir = metadata.workspaceDir;
2499
+ } else if (existsSync10(initialWorkspaceDir)) {
2500
+ workspaceDir = initialWorkspaceDir;
2501
+ }
2502
+ const preservesTaskWorktree = metadataMatchesRequestedRuntime && metadata ? options.id === taskRuntimeId(metadata.taskId) : false;
2503
+ if (mode === "worktree" && workspaceDir && monorepoRoot && !preservesTaskWorktree) {
2504
+ await cleanupRuntimeWorktree(monorepoRoot, workspaceDir);
2505
+ } else if (mode === "worktree" && workspaceDir && preservesTaskWorktree) {
2506
+ cleanupTaskRuntimeOverlay(workspaceDir);
2507
+ }
2508
+ rmSync4(runtimeRoot, { recursive: true, force: true });
2509
+ }
2510
+ function cleanupTaskRuntimeOverlay(workspaceDir) {
2511
+ const layout = resolveRuntimeWorkspaceLayout(workspaceDir);
2512
+ for (const path of [
2513
+ layout.homeDir,
2514
+ layout.tmpDir,
2515
+ layout.cacheDir,
2516
+ layout.logsDir,
2517
+ layout.stateDir,
2518
+ layout.sessionDir,
2519
+ layout.binDir,
2520
+ layout.distDir,
2521
+ layout.runtimeDir,
2522
+ layout.contextPath
2523
+ ]) {
2524
+ rmSync4(path, { recursive: true, force: true });
2525
+ }
2526
+ }
2527
+ async function readRuntimeMetadata2(metadataPath) {
2528
+ if (!existsSync10(metadataPath)) {
2529
+ return null;
2530
+ }
2531
+ try {
2532
+ return await Bun.file(metadataPath).json();
2533
+ } catch {
2534
+ return null;
2535
+ }
2536
+ }
2537
+ // packages/runtime/src/control-plane/native/run-ops.ts
2538
+ var ACTIVE_RUN_STATUSES = new Set(["preparing", "running", "validating", "reviewing"]);
2539
+ function isPidAlive(pid) {
2540
+ if (!Number.isInteger(pid) || pid <= 0) {
2541
+ return false;
2542
+ }
2543
+ try {
2544
+ process.kill(pid, 0);
2545
+ return true;
2546
+ } catch {
2547
+ return false;
2548
+ }
2549
+ }
2550
+ function readProcessParents(projectRoot) {
2551
+ const captured = runCapture(["ps", "-axo", "pid=,ppid="], projectRoot);
2552
+ if (captured.exitCode !== 0) {
2553
+ return new Map;
2554
+ }
2555
+ const parents = new Map;
2556
+ for (const rawLine of captured.stdout.split(/\r?\n/)) {
2557
+ const line = rawLine.trim();
2558
+ if (!line)
2559
+ continue;
2560
+ const match = line.match(/^(\d+)\s+(\d+)$/);
2561
+ if (!match)
2562
+ continue;
2563
+ const pid = Number.parseInt(match[1], 10);
2564
+ const ppid = Number.parseInt(match[2], 10);
2565
+ if (Number.isInteger(pid) && pid > 0 && Number.isInteger(ppid) && ppid >= 0) {
2566
+ parents.set(pid, ppid);
2567
+ }
2568
+ }
2569
+ return parents;
2570
+ }
2571
+ function collectProcessTreePids(rootPid, parents) {
2572
+ if (!Number.isInteger(rootPid) || rootPid <= 0) {
2573
+ return [];
2574
+ }
2575
+ const childrenByParent = new Map;
2576
+ for (const [pid, ppid] of parents.entries()) {
2577
+ const existing = childrenByParent.get(ppid);
2578
+ if (existing) {
2579
+ existing.push(pid);
2580
+ } else {
2581
+ childrenByParent.set(ppid, [pid]);
2582
+ }
2583
+ }
2584
+ const ordered = [];
2585
+ const visit = (pid) => {
2586
+ for (const childPid of childrenByParent.get(pid) ?? []) {
2587
+ visit(childPid);
2588
+ }
2589
+ ordered.push(pid);
2590
+ };
2591
+ visit(rootPid);
2592
+ return [...new Set(ordered)];
2593
+ }
2594
+ function signalProcessSet(pids, signal) {
2595
+ for (const pid of pids) {
2596
+ if (!isPidAlive(pid))
2597
+ continue;
2598
+ try {
2599
+ process.kill(pid, signal);
2600
+ } catch {}
2601
+ }
2602
+ }
2603
+ async function stopAuthorityRunLocally(projectRoot, run) {
2604
+ const pid = typeof run.pid === "number" ? run.pid : null;
2605
+ if (!pid || pid === process.pid) {
2606
+ return false;
2607
+ }
2608
+ const markStopped = () => {
2609
+ writeJsonFile(resolve14(resolveAuthorityRunDir(projectRoot, run.runId), "run.json"), {
2610
+ ...run,
2611
+ status: "stopped",
2612
+ completedAt: new Date().toISOString(),
2613
+ updatedAt: new Date().toISOString()
2614
+ });
2615
+ };
2616
+ if (!isPidAlive(pid)) {
2617
+ markStopped();
2618
+ return true;
2619
+ }
2620
+ const processTree = collectProcessTreePids(pid, readProcessParents(projectRoot));
2621
+ const pidsToStop = processTree.length > 0 ? processTree : [pid];
2622
+ signalProcessSet(pidsToStop, "SIGTERM");
2623
+ const deadline = Date.now() + 2000;
2624
+ while (Date.now() < deadline) {
2625
+ if (pidsToStop.every((candidatePid) => !isPidAlive(candidatePid))) {
2626
+ markStopped();
2627
+ return true;
2628
+ }
2629
+ await Bun.sleep(100);
2630
+ }
2631
+ signalProcessSet(pidsToStop, "SIGKILL");
2632
+ if (pidsToStop.every((candidatePid) => !isPidAlive(candidatePid))) {
2633
+ markStopped();
2634
+ return true;
2635
+ }
2636
+ return false;
2637
+ }
2638
+ function defaultStartRunOptions(mode) {
2639
+ return {
2640
+ mode,
2641
+ workers: parsePositiveInt(process.env.RIG_PARALLEL_WORKERS, 3),
2642
+ wsPort: parsePositiveInt(process.env.RIG_WS_PORT, 7890),
2643
+ startEventServer: process.env.RIG_START_EVENT_SERVER !== "0",
2644
+ serverHost: process.env.RIG_SERVER_HOST || "0.0.0.0",
2645
+ serverPort: parsePositiveInt(process.env.RIG_SERVER_PORT, 8787),
2646
+ serverPollMs: parsePositiveInt(process.env.RIG_SERVER_POLL_MS, 1000),
2647
+ dryRun: false
2648
+ };
2649
+ }
2650
+ async function startRun(projectRoot, options) {
2651
+ if (!options.runtimeContext) {
2652
+ await ensureLocalControlBinaries(projectRoot);
2653
+ }
2654
+ requireBinary("br", "beads-rust (br) is required for epic auto-pick.");
2655
+ const epicId = options.epicId || await resolveDefaultEpic(projectRoot);
2656
+ if (!epicId) {
2657
+ throw new RemoteCliError("RIG_RUN_EPIC_NOT_FOUND", "Could not resolve an epic automatically. Pass --epic <id>.", 2);
2658
+ }
2659
+ const readyTasks = listReadyTasksForEpic(projectRoot, epicId);
2660
+ const selectedTasks = readyTasks.slice(0, options.mode === "parallel" ? Math.max(1, options.workers) : 1);
2661
+ if (selectedTasks.length === 0) {
2662
+ throw new RemoteCliError("RIG_RUN_NO_READY_TASKS", `No ready tasks found for epic ${epicId}.`, 2, { epicId });
2663
+ }
2664
+ const server = await ensureServerForRuns(projectRoot);
2665
+ const runIds = selectedTasks.map(() => randomUUID());
2666
+ const eventServerUrl = `${server.baseUrl}/events/stream`;
2667
+ if (options.dryRun) {
2668
+ for (let index = 0;index < selectedTasks.length; index += 1) {
2669
+ const task = selectedTasks[index];
2670
+ console.log(`[dry-run] POST ${server.baseUrl}/api/runs/task runId=${runIds[index]} taskId=${task.id} title=${JSON.stringify(task.title)}`);
2671
+ }
2672
+ return { epicId, runIds, baseUrl: server.baseUrl, eventServerUrl, exitCode: 0 };
2673
+ }
2674
+ for (let index = 0;index < selectedTasks.length; index += 1) {
2675
+ const task = selectedTasks[index];
2676
+ const response = await fetch(`${server.baseUrl}/api/runs/task`, {
2677
+ method: "POST",
2678
+ headers: {
2679
+ "content-type": "application/json",
2680
+ ...server.authToken ? { authorization: `Bearer ${server.authToken}` } : {}
2681
+ },
2682
+ body: JSON.stringify({
2683
+ runId: runIds[index],
2684
+ taskId: task.id,
2685
+ title: task.title,
2686
+ runtimeAdapter: process.env.RIG_RUNTIME_ADAPTER || "claude-code",
2687
+ executionTarget: "local"
2688
+ })
2689
+ });
2690
+ if (!response.ok) {
2691
+ const text = await response.text();
2692
+ throw new RemoteCliError("RIG_RUN_START_FAILED", `Failed to submit run for ${task.id}: ${text || response.statusText}`, 1, { epicId, taskId: task.id });
2693
+ }
2694
+ }
2695
+ return { epicId, runIds, baseUrl: server.baseUrl, eventServerUrl, exitCode: 0 };
2696
+ }
2697
+ function runStatus(projectRoot, runtimeContext) {
2698
+ const runs = listAuthorityRuns(projectRoot).map((entry) => {
2699
+ const record = readAuthorityRun(projectRoot, entry.runId);
2700
+ if (!record) {
2701
+ return null;
2702
+ }
2703
+ return {
2704
+ runId: entry.runId,
2705
+ taskId: record.taskId ?? null,
2706
+ title: String(record.title ?? record.taskId ?? entry.runId),
2707
+ status: String(record.status ?? "unknown")
2708
+ };
2709
+ }).filter((entry) => entry !== null).sort((left, right) => right.runId.localeCompare(left.runId));
2710
+ const activeRuns = runs.filter((entry) => ACTIVE_RUN_STATUSES.has(entry.status));
2711
+ return {
2712
+ activeRuns,
2713
+ recentRuns: runs.slice(0, 10)
2714
+ };
2715
+ }
2716
+ async function runResume(projectRoot, runtimeContext) {
2717
+ const resumable = listAuthorityRuns(projectRoot).map((entry) => readAuthorityRun(projectRoot, entry.runId)).filter((run2) => Boolean(run2)).filter((run2) => run2.mode === "local" && (run2.status === "stopped" || run2.status === "failed" || run2.status === "created")).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")));
2718
+ const run = resumable[0];
2719
+ if (!run) {
2720
+ throw new RemoteCliError("RIG_RUN_NOTHING_TO_RESUME", "No stopped local run is available to resume.", 2);
2721
+ }
2722
+ const server = await ensureServerForRuns(projectRoot);
2723
+ const response = await fetch(`${server.baseUrl}/api/runs/resume`, {
2724
+ method: "POST",
2725
+ headers: {
2726
+ "content-type": "application/json",
2727
+ ...server.authToken ? { authorization: `Bearer ${server.authToken}` } : {}
2728
+ },
2729
+ body: JSON.stringify({ runId: run.runId, createdAt: new Date().toISOString() })
2730
+ });
2731
+ if (!response.ok) {
2732
+ const text = await response.text();
2733
+ throw new RemoteCliError("RIG_RUN_RESUME_FAILED", text || response.statusText, 1, { runId: run.runId });
2734
+ }
2735
+ return { runId: run.runId };
2736
+ }
2737
+ async function runStop(projectRoot) {
2738
+ const activeRuns = listAuthorityRuns(projectRoot).map((entry) => readAuthorityRun(projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.mode === "local" && ACTIVE_RUN_STATUSES.has(String(run.status ?? "")));
2739
+ if (activeRuns.length === 0) {
2740
+ return { stopped: 0, remaining: [] };
2741
+ }
2742
+ let stopped = 0;
2743
+ const remainingRunIds = [];
2744
+ for (const run of activeRuns) {
2745
+ if (await stopAuthorityRunLocally(projectRoot, run)) {
2746
+ stopped += 1;
2747
+ continue;
2748
+ }
2749
+ remainingRunIds.push(run.runId);
2750
+ }
2751
+ if (remainingRunIds.length === 0) {
2752
+ return { stopped, remaining: [] };
2753
+ }
2754
+ const server = await ensureServerForRuns(projectRoot);
2755
+ const unresolved = [];
2756
+ for (const runId of remainingRunIds) {
2757
+ const response = await fetch(`${server.baseUrl}/api/runs/stop`, {
2758
+ method: "POST",
2759
+ headers: {
2760
+ "content-type": "application/json",
2761
+ ...server.authToken ? { authorization: `Bearer ${server.authToken}` } : {}
2762
+ },
2763
+ body: JSON.stringify({ runId, createdAt: new Date().toISOString() })
2764
+ });
2765
+ if (response.ok) {
2766
+ stopped += 1;
2767
+ } else {
2768
+ unresolved.push(runId);
2769
+ }
2770
+ }
2771
+ return { stopped, remaining: unresolved };
2772
+ }
2773
+ function buildRuntimePath(projectRoot) {
2774
+ const entries = [
2775
+ resolve14(projectRoot, ".rig/bin"),
2776
+ resolve14(projectRoot, "rig/tools"),
2777
+ process.env.PATH || ""
2778
+ ];
2779
+ return entries.filter(Boolean).join(":");
2780
+ }
2781
+ async function deleteRunState(projectRoot, options) {
2782
+ const run = readAuthorityRun(projectRoot, options.runId);
2783
+ if (!run) {
2784
+ throw new RemoteCliError("RIG_RUN_NOT_FOUND", `Run not found: ${options.runId}`, 2, {
2785
+ runId: options.runId
2786
+ });
2787
+ }
2788
+ const runDir = resolveAuthorityRunDir(projectRoot, options.runId);
2789
+ const runtimeIds = new Set;
2790
+ const runtimeRoots = new Set;
2791
+ if (options.purgeRuntime !== false && run.worktreePath) {
2792
+ const derivedRuntimeRoot = dirname11(run.worktreePath);
2793
+ runtimeRoots.add(derivedRuntimeRoot);
2794
+ const derivedRuntimeId = basename6(derivedRuntimeRoot);
2795
+ if (derivedRuntimeId) {
2796
+ runtimeIds.add(derivedRuntimeId);
2797
+ }
2798
+ for (const runtime of await listAgentRuntimes(projectRoot)) {
2799
+ if (runtime.workspaceDir === run.worktreePath || runtime.rootDir === derivedRuntimeRoot) {
2800
+ runtimeIds.add(runtime.id);
2801
+ runtimeRoots.add(runtime.rootDir);
2802
+ }
2803
+ }
2804
+ }
2805
+ for (const runtimeId of runtimeIds) {
2806
+ await cleanupAgentRuntime({ projectRoot, id: runtimeId });
2807
+ }
2808
+ for (const runtimeRoot of runtimeRoots) {
2809
+ rmSync5(runtimeRoot, { recursive: true, force: true });
2810
+ }
2811
+ rmSync5(runDir, { recursive: true, force: true });
2812
+ let taskArtifactsDeleted = false;
2813
+ if (options.purgeTaskArtifacts && run.taskId) {
2814
+ for (const taskArtifactsDir of resolveTaskArtifactDirs(projectRoot, run.taskId)) {
2815
+ if (!existsSync11(taskArtifactsDir))
2816
+ continue;
2817
+ rmSync5(taskArtifactsDir, { recursive: true, force: true });
2818
+ taskArtifactsDeleted = true;
2819
+ }
2820
+ }
2821
+ return {
2822
+ runId: options.runId,
2823
+ taskId: run.taskId ?? null,
2824
+ runDeleted: true,
2825
+ runtimeIds: Array.from(runtimeIds),
2826
+ runtimeRoots: Array.from(runtimeRoots),
2827
+ taskArtifactsDeleted
2828
+ };
2829
+ }
2830
+ async function cleanupRunState(projectRoot, options = {}) {
2831
+ const runs = listAuthorityRuns(projectRoot);
2832
+ const runIds = runs.map((run) => run.runId);
2833
+ const runtimeIds = new Set;
2834
+ for (const runId of runIds) {
2835
+ const result = await deleteRunState(projectRoot, {
2836
+ runId,
2837
+ purgeRuntime: options.includeRuntimes !== false,
2838
+ purgeTaskArtifacts: false
2839
+ });
2840
+ for (const runtimeId of result.runtimeIds) {
2841
+ runtimeIds.add(runtimeId);
2842
+ }
2843
+ }
2844
+ if (options.includeRuntimes !== false) {
2845
+ for (const agentsRoot of listAuthorityRuntimeAgentsRoots(projectRoot)) {
2846
+ if (!existsSync11(agentsRoot))
2847
+ continue;
2848
+ for (const entry of readdirSync2(agentsRoot, { withFileTypes: true })) {
2849
+ if (!entry.isDirectory())
2850
+ continue;
2851
+ rmSync5(resolve14(agentsRoot, entry.name), { recursive: true, force: true });
2852
+ }
2853
+ }
2854
+ }
2855
+ let artifactsCleared = false;
2856
+ if (options.includeArtifacts !== false) {
2857
+ const seen = new Set;
2858
+ for (const run of runs) {
2859
+ if (!run.taskId)
2860
+ continue;
2861
+ for (const artifactsRoot of resolveTaskArtifactDirs(projectRoot, run.taskId)) {
2862
+ if (seen.has(artifactsRoot) || !existsSync11(artifactsRoot))
2863
+ continue;
2864
+ seen.add(artifactsRoot);
2865
+ rmSync5(artifactsRoot, { recursive: true, force: true });
2866
+ artifactsCleared = true;
2867
+ }
2868
+ }
2869
+ for (const artifactsRoot of listAuthorityArtifactRoots(projectRoot)) {
2870
+ if (seen.has(artifactsRoot) || !existsSync11(artifactsRoot))
2871
+ continue;
2872
+ rmSync5(artifactsRoot, { recursive: true, force: true });
2873
+ artifactsCleared = true;
2874
+ }
2875
+ }
2876
+ let queueCleared = false;
2877
+ if (options.includeQueue !== false) {
2878
+ const queuePath = resolve14(resolveAuthorityPaths(projectRoot).stateDir, "task-queue.json");
2879
+ if (existsSync11(queuePath)) {
2880
+ rmSync5(queuePath, { force: true });
2881
+ queueCleared = true;
2882
+ }
2883
+ }
2884
+ return {
2885
+ runIds,
2886
+ runtimeIds: Array.from(runtimeIds),
2887
+ artifactsCleared,
2888
+ queueCleared
2889
+ };
2890
+ }
2891
+ async function resolveDefaultEpic(projectRoot) {
2892
+ const fromSession = await readSessionEpic(projectRoot);
2893
+ if (fromSession) {
2894
+ return fromSession;
2895
+ }
2896
+ const fromReady = epicFromReadyTasks(projectRoot);
2897
+ if (fromReady) {
2898
+ return fromReady;
2899
+ }
2900
+ const epics = listOpenEpics(projectRoot);
2901
+ return epics[0]?.id || "";
2902
+ }
2903
+ function listOpenEpics(projectRoot) {
2904
+ const list = runCapture(["br", "--no-db", "list", "--json"], projectRoot);
2905
+ if (list.exitCode !== 0 || !list.stdout.trim()) {
2906
+ return [];
2907
+ }
2908
+ const issues = parseJson(list.stdout, []);
2909
+ const epics = issues.filter((issue) => issue.issue_type === "epic" && issue.status === "open");
2910
+ sortByPriorityAndCreatedAt(epics);
2911
+ return epics.map((issue) => {
2912
+ if (typeof issue.id !== "string" || !issue.id) {
2913
+ return null;
2914
+ }
2915
+ return {
2916
+ id: issue.id,
2917
+ priority: Number.isFinite(issue.priority) ? Number(issue.priority) : undefined,
2918
+ createdAt: typeof issue.created_at === "string" ? issue.created_at : undefined
2919
+ };
2920
+ }).filter((issue) => issue !== null);
2921
+ }
2922
+ function listReadyTasksForEpic(projectRoot, epicId) {
2923
+ const ready = runCapture(["br", "--no-db", "ready", "--json"], projectRoot);
2924
+ if (ready.exitCode !== 0 || !ready.stdout.trim()) {
2925
+ return [];
2926
+ }
2927
+ const issues = parseJson(ready.stdout, []);
2928
+ const tasks = issues.filter((issue) => issue.issue_type === "task" && issue.status === "open");
2929
+ sortByPriorityAndCreatedAt(tasks);
2930
+ return tasks.filter((issue) => typeof issue.id === "string" && issue.id).filter((issue) => resolveParentEpic(projectRoot, issue.id) === epicId).map((issue) => ({
2931
+ id: issue.id,
2932
+ title: typeof issue.title === "string" && issue.title.trim().length > 0 ? issue.title : issue.id
2933
+ }));
2934
+ }
2935
+ var LOCAL_CONTROL_BINARY_SPECS = [
2936
+ { entrypoint: "packages/cli/bin/rig.ts", output: ".rig/bin/rig" },
2937
+ { entrypoint: "packages/runtime/bin/rig-agent-dispatch.ts", output: ".rig/bin/rig-agent" }
2938
+ ];
2939
+ function hasLocalControlDispatchSource(root) {
2940
+ return existsSync11(resolve14(root, "packages/runtime/bin/rig-agent-dispatch.ts"));
2941
+ }
2942
+ function resolveLocalControlBinarySourceRoot(projectRoot, options = {}) {
2943
+ const env = options.env ?? process.env;
2944
+ const candidates = [
2945
+ env.RIG_HOST_PROJECT_ROOT?.trim(),
2946
+ env.PROJECT_RIG_ROOT?.trim(),
2947
+ options.cwd ?? process.cwd(),
2948
+ resolve14(options.moduleDir ?? import.meta.dir, "../../../../.."),
2949
+ projectRoot
2950
+ ].filter((value) => Boolean(value));
2951
+ for (const candidate of candidates) {
2952
+ const root = resolve14(candidate);
2953
+ if (hasLocalControlDispatchSource(root)) {
2954
+ return root;
2955
+ }
2956
+ }
2957
+ return resolve14(projectRoot);
2958
+ }
2959
+ function collectLocalControlBinaryBuildTargets(projectRoot, sourceRoot) {
2960
+ return LOCAL_CONTROL_BINARY_SPECS.flatMap((spec) => {
2961
+ const sourcePath = resolve14(sourceRoot, spec.entrypoint);
2962
+ if (!existsSync11(sourcePath))
2963
+ return [];
2964
+ return [{
2965
+ entrypoint: spec.entrypoint,
2966
+ sourcePath,
2967
+ outputPath: resolve14(projectRoot, spec.output)
2968
+ }];
2969
+ });
2970
+ }
2971
+ async function resolvePreferredShellBinary(projectRoot, runtimeContext) {
2972
+ if (runtimeContext) {
2973
+ const runtimeAgent = resolve14(runtimeContext.binDir, "rig-agent");
2974
+ if (existsSync11(runtimeAgent)) {
2975
+ return runtimeAgent;
2976
+ }
2977
+ }
2978
+ const agent = resolve14(projectRoot, ".rig/bin/rig-agent");
2979
+ const sourceRoot = resolveLocalControlBinarySourceRoot(projectRoot);
2980
+ if (!existsSync11(agent) || localControlBinaryNeedsRebuild(sourceRoot, agent, "packages/runtime/bin/rig-agent-dispatch.ts")) {
2981
+ await ensureLocalControlBinaries(projectRoot, sourceRoot);
2982
+ }
2983
+ if (existsSync11(agent)) {
2984
+ return agent;
2985
+ }
2986
+ throw new RemoteCliError("RIG_RUN_MISSING_AGENT_BINARY", `Missing compiled agent dispatch binary at ${agent}. Run the local control binary build first.`, 2);
2987
+ }
2988
+ function localControlBinaryNeedsRebuild(projectRoot, outputPath, entrypoint) {
2989
+ if (!existsSync11(outputPath)) {
2990
+ return true;
2991
+ }
2992
+ let outputMtime = 0;
2993
+ try {
2994
+ outputMtime = statSync4(outputPath).mtimeMs;
2995
+ } catch {
2996
+ return true;
2997
+ }
2998
+ const sourceRoots = entrypoint === "packages/runtime/bin/rig-agent-dispatch.ts" ? [
2999
+ "packages/contracts/src",
3000
+ "packages/core/src",
3001
+ "packages/runtime/bin",
3002
+ "packages/runtime/src",
3003
+ "packages/runtime/native",
3004
+ "packages/standard-plugin/src"
3005
+ ] : [
3006
+ "packages/cli/bin",
3007
+ "packages/cli/src",
3008
+ "packages/runtime/src",
3009
+ "packages/server/src"
3010
+ ];
3011
+ for (const relativePath of sourceRoots) {
3012
+ if (sourceTreeHasNewerFiles(projectRoot, relativePath, outputMtime)) {
3013
+ return true;
3014
+ }
3015
+ }
3016
+ return false;
3017
+ }
3018
+ function sourceTreeHasNewerFiles(projectRoot, relativePath, outputMtime) {
3019
+ const rootPath = resolve14(projectRoot, relativePath);
3020
+ if (!existsSync11(rootPath)) {
3021
+ return false;
3022
+ }
3023
+ const pending = [rootPath];
3024
+ while (pending.length > 0) {
3025
+ const current = pending.pop();
3026
+ if (!current) {
3027
+ continue;
3028
+ }
3029
+ let currentStat;
3030
+ try {
3031
+ currentStat = statSync4(current);
3032
+ } catch {
3033
+ return true;
3034
+ }
3035
+ if (currentStat.isDirectory()) {
3036
+ let entries;
3037
+ try {
3038
+ entries = readdirSync2(current, { withFileTypes: true, encoding: "utf8" });
3039
+ } catch {
3040
+ return true;
3041
+ }
3042
+ for (const entry of entries) {
3043
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".worktrees" || entry.name === ".rig") {
3044
+ continue;
3045
+ }
3046
+ pending.push(resolve14(current, entry.name));
3047
+ }
3048
+ continue;
3049
+ }
3050
+ if (currentStat.mtimeMs > outputMtime) {
3051
+ return true;
3052
+ }
3053
+ }
3054
+ return false;
3055
+ }
3056
+ function requireBinary(name, message) {
3057
+ if (!Bun.which(name)) {
3058
+ throw new RemoteCliError("RIG_RUN_MISSING_BINARY", message, 2, { name });
3059
+ }
3060
+ }
3061
+ async function ensureLocalControlBinaries(projectRoot, sourceRoot = resolveLocalControlBinarySourceRoot(projectRoot)) {
3062
+ const targets = collectLocalControlBinaryBuildTargets(projectRoot, sourceRoot);
3063
+ const secretDefines = secretDefinesFromEnv(process.env, projectRoot);
3064
+ for (const target of targets) {
3065
+ mkdirSync5(dirname11(target.outputPath), { recursive: true });
3066
+ try {
3067
+ await buildRuntimeBinary({
3068
+ sourcePath: target.sourcePath,
3069
+ outputPath: target.outputPath,
3070
+ cwd: sourceRoot,
3071
+ define: target.entrypoint === "packages/runtime/bin/rig-agent-dispatch.ts" ? secretDefines : undefined
3072
+ });
3073
+ } catch (error) {
3074
+ throw new RemoteCliError("RIG_RUN_BINARY_BUILD_FAILED", `Failed to compile ${target.entrypoint}: ${error instanceof Error ? error.message : "unknown error"}`, 2);
3075
+ }
3076
+ }
3077
+ }
3078
+ async function readSessionEpic(projectRoot) {
3079
+ const file = resolve14(resolveAuthorityPaths(projectRoot).stateDir, "session-meta.json");
3080
+ if (!existsSync11(file)) {
3081
+ return "";
3082
+ }
3083
+ try {
3084
+ const parsed = await Bun.file(file).json();
3085
+ if (typeof parsed.epicId !== "string" || !parsed.epicId) {
3086
+ return "";
3087
+ }
3088
+ if (await sessionTasksExhausted(projectRoot)) {
3089
+ clearExhaustedSession(projectRoot);
3090
+ return "";
3091
+ }
3092
+ return parsed.epicId;
3093
+ } catch {
3094
+ return "";
3095
+ }
3096
+ }
3097
+ async function sessionTasksExhausted(projectRoot) {
3098
+ const sessionFile = resolve14(resolveAuthorityPaths(projectRoot).stateDir, "session.json");
3099
+ if (!existsSync11(sessionFile)) {
3100
+ return false;
3101
+ }
3102
+ try {
3103
+ const session = await Bun.file(sessionFile).json();
3104
+ const tasks = session?.trackerState?.tasks;
3105
+ if (!Array.isArray(tasks) || tasks.length === 0) {
3106
+ return true;
3107
+ }
3108
+ return tasks.every((t) => t.status === "completed");
3109
+ } catch {
3110
+ return false;
3111
+ }
3112
+ }
3113
+ function clearExhaustedSession(projectRoot) {
3114
+ for (const name of ["session-meta.json", "session.json"]) {
3115
+ try {
3116
+ rmSync5(resolve14(resolveAuthorityPaths(projectRoot).stateDir, name), { force: true });
3117
+ } catch {}
3118
+ }
3119
+ }
3120
+ function epicFromReadyTasks(projectRoot) {
3121
+ const ready = runCapture(["br", "--no-db", "ready", "--json"], projectRoot);
3122
+ if (ready.exitCode !== 0 || !ready.stdout.trim()) {
3123
+ return "";
3124
+ }
3125
+ const issues = parseJson(ready.stdout, []);
3126
+ const tasks = issues.filter((issue) => issue.issue_type === "task" && issue.status === "open");
3127
+ sortByPriorityAndCreatedAt(tasks);
3128
+ const firstTaskId = tasks[0]?.id;
3129
+ if (!firstTaskId) {
3130
+ return "";
3131
+ }
3132
+ return resolveParentEpic(projectRoot, firstTaskId);
3133
+ }
3134
+ function resolveParentEpic(projectRoot, taskId) {
3135
+ const deps = runCapture(["br", "--no-db", "dep", "list", taskId, "--json"], projectRoot);
3136
+ if (deps.exitCode !== 0 || !deps.stdout.trim()) {
3137
+ return "";
3138
+ }
3139
+ const parsed = parseJson(deps.stdout, []);
3140
+ for (const item of parsed) {
3141
+ const type = typeof item.type === "string" ? item.type : "";
3142
+ const dependsOn = typeof item.depends_on_id === "string" ? item.depends_on_id : "";
3143
+ if (type === "parent-child" && dependsOn) {
3144
+ return dependsOn;
3145
+ }
3146
+ }
3147
+ return "";
3148
+ }
3149
+ function sortByPriorityAndCreatedAt(issues) {
3150
+ issues.sort((a, b) => {
3151
+ const pa = Number.isFinite(a.priority) ? Number(a.priority) : Number.MAX_SAFE_INTEGER;
3152
+ const pb = Number.isFinite(b.priority) ? Number(b.priority) : Number.MAX_SAFE_INTEGER;
3153
+ if (pa !== pb) {
3154
+ return pa - pb;
3155
+ }
3156
+ const ca = typeof a.created_at === "string" ? Date.parse(a.created_at) : Number.MAX_SAFE_INTEGER;
3157
+ const cb = typeof b.created_at === "string" ? Date.parse(b.created_at) : Number.MAX_SAFE_INTEGER;
3158
+ return ca - cb;
3159
+ });
3160
+ }
3161
+ function resolvePublishedServerStatePath(projectRoot) {
3162
+ return resolve14(resolveAuthorityPaths(projectRoot).stateDir, "rig-server.json");
3163
+ }
3164
+ async function readPublishedServerState(projectRoot) {
3165
+ const filePath = resolvePublishedServerStatePath(projectRoot);
3166
+ if (!existsSync11(filePath)) {
3167
+ return null;
3168
+ }
3169
+ try {
3170
+ return await Bun.file(filePath).json();
3171
+ } catch {
3172
+ return null;
3173
+ }
3174
+ }
3175
+ async function waitForServerHealthy(baseUrl, timeoutMs = 15000, authToken = null) {
3176
+ const deadline = Date.now() + timeoutMs;
3177
+ while (Date.now() < deadline) {
3178
+ try {
3179
+ const response = await fetch(`${baseUrl}/health`, {
3180
+ headers: authToken ? { authorization: `Bearer ${authToken}` } : undefined
3181
+ });
3182
+ if (response.ok) {
3183
+ return true;
3184
+ }
3185
+ } catch {}
3186
+ await Bun.sleep(250);
3187
+ }
3188
+ return false;
3189
+ }
3190
+ async function waitForPublishedServer(projectRoot, authToken, timeoutMs = 15000) {
3191
+ const deadline = Date.now() + timeoutMs;
3192
+ while (Date.now() < deadline) {
3193
+ const published = await readPublishedServerState(projectRoot);
3194
+ if (published && published.authToken === authToken) {
3195
+ const baseUrl = `http://${published.host}:${published.port}`;
3196
+ if (await waitForServerHealthy(baseUrl, 500, published.authToken)) {
3197
+ return published;
3198
+ }
3199
+ }
3200
+ await Bun.sleep(250);
3201
+ }
3202
+ return null;
3203
+ }
3204
+ async function ensureServerForRuns(projectRoot) {
3205
+ const published = await readPublishedServerState(projectRoot);
3206
+ if (published) {
3207
+ const baseUrl = `http://${published.host}:${published.port}`;
3208
+ if (await waitForServerHealthy(baseUrl, 1500, published.authToken)) {
3209
+ return { baseUrl, authToken: published.authToken };
3210
+ }
3211
+ }
3212
+ const authToken = randomUUID().replace(/-/g, "");
3213
+ const child = Bun.spawn([
3214
+ "bun",
3215
+ "run",
3216
+ "packages/server/src/server.ts",
3217
+ "start",
3218
+ "--host",
3219
+ "127.0.0.1",
3220
+ "--port",
3221
+ "0",
3222
+ "--auth-token",
3223
+ authToken
3224
+ ], {
3225
+ cwd: projectRoot,
3226
+ env: {
3227
+ ...process.env,
3228
+ PROJECT_RIG_ROOT: projectRoot
3229
+ },
3230
+ stdin: "ignore",
3231
+ stdout: "ignore",
3232
+ stderr: "ignore",
3233
+ detached: true
3234
+ });
3235
+ writeFileSync5(resolve14(resolveAuthorityPaths(projectRoot).stateDir, "event-server.pid"), String(child.pid));
3236
+ const ready = await waitForPublishedServer(projectRoot, authToken);
3237
+ if (!ready) {
3238
+ throw new RemoteCliError("RIG_RUN_SERVER_TIMEOUT", "Timed out waiting for Rig server to start.", 1);
3239
+ }
3240
+ return {
3241
+ baseUrl: `http://${ready.host}:${ready.port}`,
3242
+ authToken: ready.authToken
3243
+ };
3244
+ }
3245
+ var __testOnly = {
3246
+ waitForServerHealthy
3247
+ };
3248
+ function parseJson(raw, fallback) {
3249
+ try {
3250
+ return JSON.parse(raw);
3251
+ } catch {
3252
+ return fallback;
3253
+ }
3254
+ }
3255
+ function parsePositiveInt(raw, fallback) {
3256
+ if (!raw) {
3257
+ return fallback;
3258
+ }
3259
+ const value = Number.parseInt(raw, 10);
3260
+ if (!Number.isFinite(value) || value <= 0) {
3261
+ return fallback;
3262
+ }
3263
+ return value;
3264
+ }
3265
+ export {
3266
+ startRun,
3267
+ runStop,
3268
+ runStatus,
3269
+ runResume,
3270
+ resolvePreferredShellBinary,
3271
+ resolveLocalControlBinarySourceRoot,
3272
+ resolveDefaultEpic,
3273
+ listReadyTasksForEpic,
3274
+ listOpenEpics,
3275
+ deleteRunState,
3276
+ defaultStartRunOptions,
3277
+ collectLocalControlBinaryBuildTargets,
3278
+ cleanupRunState,
3279
+ buildRuntimePath,
3280
+ __testOnly
3281
+ };