@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,1111 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // packages/runtime/src/control-plane/controlled-bash.ts
5
+ import { readFileSync as readFileSync2 } from "fs";
6
+ import { appendFile, mkdir } from "fs/promises";
7
+ import { dirname as dirname3, resolve as resolve4 } from "path";
8
+
9
+ // packages/runtime/src/build-time-config.ts
10
+ function normalizeBuildConfig(value) {
11
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
12
+ return {};
13
+ }
14
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
15
+ }
16
+ function readBuildConfig() {
17
+ if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
18
+ return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
19
+ }
20
+ const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
21
+ if (!raw) {
22
+ return {};
23
+ }
24
+ try {
25
+ return normalizeBuildConfig(JSON.parse(raw));
26
+ } catch {
27
+ return {};
28
+ }
29
+ }
30
+
31
+ // packages/runtime/src/layout.ts
32
+ import { existsSync } from "fs";
33
+ import { basename, dirname, resolve } from "path";
34
+ var RIG_DEFINITION_DIRNAME = "rig";
35
+ var RIG_ARTIFACTS_DIRNAME = "artifacts";
36
+ function resolveMonorepoRoot(projectRoot) {
37
+ const normalizedProjectRoot = resolve(projectRoot);
38
+ const explicit = process.env.MONOREPO_ROOT?.trim();
39
+ if (explicit) {
40
+ const explicitRoot = resolve(explicit);
41
+ const explicitParent = dirname(explicitRoot);
42
+ if (basename(explicitParent) === ".worktrees") {
43
+ const owner = dirname(explicitParent);
44
+ const ownerHasGit = existsSync(resolve(owner, ".git"));
45
+ const ownerHasTaskConfig = existsSync(resolve(owner, ".rig", "task-config.json"));
46
+ const ownerHasRigConfig = existsSync(resolve(owner, "rig.config.ts"));
47
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
48
+ return owner;
49
+ }
50
+ throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
51
+ }
52
+ if (!existsSync(resolve(explicitRoot, ".git"))) {
53
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
54
+ }
55
+ const hasTaskConfig = existsSync(resolve(explicitRoot, ".rig", "task-config.json"));
56
+ const hasRigConfig = existsSync(resolve(explicitRoot, "rig.config.ts"));
57
+ if (!hasTaskConfig && !hasRigConfig) {
58
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
59
+ }
60
+ return explicitRoot;
61
+ }
62
+ const projectParent = dirname(normalizedProjectRoot);
63
+ if (basename(projectParent) === ".worktrees") {
64
+ const worktreeOwner = dirname(projectParent);
65
+ const ownerHasGit = existsSync(resolve(worktreeOwner, ".git"));
66
+ const ownerHasTaskConfig = existsSync(resolve(worktreeOwner, ".rig", "task-config.json"));
67
+ const ownerHasRigConfig = existsSync(resolve(worktreeOwner, "rig.config.ts"));
68
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
69
+ return worktreeOwner;
70
+ }
71
+ }
72
+ return normalizedProjectRoot;
73
+ }
74
+ function resolveRuntimeWorkspaceLayout(workspaceDir) {
75
+ const root = resolve(workspaceDir);
76
+ const rigRoot = resolve(root, ".rig");
77
+ const logsDir = resolve(rigRoot, "logs");
78
+ const stateDir = resolve(rigRoot, "state");
79
+ const runtimeDir = resolve(rigRoot, "runtime");
80
+ const binDir = resolve(rigRoot, "bin");
81
+ return {
82
+ workspaceDir: root,
83
+ rigRoot,
84
+ stateDir,
85
+ logsDir,
86
+ artifactsRoot: resolve(root, RIG_ARTIFACTS_DIRNAME),
87
+ runtimeDir,
88
+ homeDir: resolve(rigRoot, "home"),
89
+ tmpDir: resolve(rigRoot, "tmp"),
90
+ cacheDir: resolve(rigRoot, "cache"),
91
+ sessionDir: resolve(rigRoot, "session"),
92
+ binDir,
93
+ distDir: resolve(rigRoot, "dist"),
94
+ pluginBinDir: resolve(binDir, "plugins"),
95
+ contextPath: resolve(rigRoot, "runtime-context.json"),
96
+ controlPlaneEventsFile: resolve(logsDir, "control-plane.events.jsonl")
97
+ };
98
+ }
99
+ function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
100
+ const explicit = process.env.RIG_TASK_WORKSPACE?.trim();
101
+ if (!explicit) {
102
+ throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
103
+ }
104
+ return resolve(explicit);
105
+ }
106
+ function resolveRigLayout(projectRoot) {
107
+ const monorepoRoot = resolveMonorepoRoot(projectRoot);
108
+ const definitionRoot = resolve(projectRoot, RIG_DEFINITION_DIRNAME);
109
+ const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
110
+ const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
111
+ const policyDir = resolve(definitionRoot, "policy");
112
+ return {
113
+ projectRoot,
114
+ monorepoRoot,
115
+ definitionRoot,
116
+ runtimeWorkspaceRoot,
117
+ stateRoot: runtimeLayout.rigRoot,
118
+ artifactsRoot: runtimeLayout.artifactsRoot,
119
+ configPath: resolve(definitionRoot, "config.sh"),
120
+ taskConfigPath: resolve(runtimeWorkspaceRoot, ".rig", "task-config.json"),
121
+ policyDir,
122
+ policyFile: resolve(policyDir, "policy.json"),
123
+ pluginsDir: resolve(definitionRoot, "plugins"),
124
+ hooksDir: resolve(definitionRoot, "hooks"),
125
+ toolsDir: resolve(definitionRoot, "tools"),
126
+ templatesDir: resolve(definitionRoot, "templates"),
127
+ validationDir: resolve(definitionRoot, "validation"),
128
+ stateDir: runtimeLayout.stateDir,
129
+ logsDir: runtimeLayout.logsDir,
130
+ notificationsDir: resolve(definitionRoot, "notifications"),
131
+ runtimeDir: runtimeLayout.runtimeDir,
132
+ distDir: runtimeLayout.distDir,
133
+ binDir: runtimeLayout.binDir,
134
+ pluginBinDir: runtimeLayout.pluginBinDir,
135
+ keybindingsPath: resolve(definitionRoot, "keybindings.json"),
136
+ controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
137
+ };
138
+ }
139
+
140
+ // packages/runtime/src/control-plane/native/utils.ts
141
+ import { ptr as ptr2 } from "bun:ffi";
142
+
143
+ // packages/runtime/src/control-plane/native/runtime-native.ts
144
+ import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
145
+ import { copyFileSync, existsSync as existsSync2, mkdirSync, renameSync, rmSync, statSync } from "fs";
146
+ import { tmpdir } from "os";
147
+ import { dirname as dirname2, resolve as resolve2 } from "path";
148
+ var sharedNativeRuntimeOutputDir = resolve2(tmpdir(), "rig-native");
149
+ var sharedNativeRuntimeOutputPath = resolve2(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
150
+ var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
151
+ var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
152
+ function requireNativeRuntimeLibrary(feature) {
153
+ if (!nativeRuntimeLibrary) {
154
+ throw new Error(`Native Zig runtime is required for ${feature}`);
155
+ }
156
+ return nativeRuntimeLibrary;
157
+ }
158
+ async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
159
+ if (await buildNativeRuntimeLibrary(outputPath, options)) {
160
+ return outputPath;
161
+ }
162
+ return !options.force && existsSync2(outputPath) ? outputPath : null;
163
+ }
164
+ async function loadNativeRuntimeLibrary() {
165
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
166
+ return null;
167
+ }
168
+ for (const candidate of nativeRuntimeLibraryCandidates()) {
169
+ if (!candidate || !existsSync2(candidate)) {
170
+ continue;
171
+ }
172
+ const loaded = tryDlopenNativeRuntimeLibrary(candidate);
173
+ if (loaded) {
174
+ return loaded;
175
+ }
176
+ }
177
+ const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
178
+ if (!builtLibraryPath) {
179
+ return null;
180
+ }
181
+ return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
182
+ }
183
+ function nativePackageLibraryCandidates(fromDir, names) {
184
+ const candidates = [];
185
+ let cursor = resolve2(fromDir);
186
+ for (let index = 0;index < 8; index += 1) {
187
+ for (const name of names) {
188
+ candidates.push(resolve2(cursor, "native", `${process.platform}-${process.arch}`, name), resolve2(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve2(cursor, "native", name), resolve2(cursor, "native", "lib", name));
189
+ }
190
+ const parent = dirname2(cursor);
191
+ if (parent === cursor)
192
+ break;
193
+ cursor = parent;
194
+ }
195
+ return candidates;
196
+ }
197
+ function nativeRuntimeLibraryCandidates() {
198
+ const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
199
+ const execDir = process.execPath?.trim() ? dirname2(process.execPath.trim()) : "";
200
+ const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
201
+ return [...new Set([
202
+ explicit,
203
+ ...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
204
+ execDir ? resolve2(execDir, colocatedNativeRuntimeFileName) : "",
205
+ execDir ? resolve2(execDir, platformSpecific) : "",
206
+ execDir ? resolve2(execDir, "..", colocatedNativeRuntimeFileName) : "",
207
+ execDir ? resolve2(execDir, "..", platformSpecific) : "",
208
+ execDir ? resolve2(execDir, "lib", colocatedNativeRuntimeFileName) : "",
209
+ execDir ? resolve2(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
210
+ sharedNativeRuntimeOutputPath
211
+ ].filter(Boolean))];
212
+ }
213
+ function resolveNativeRuntimeSourcePath() {
214
+ const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
215
+ if (explicit && existsSync2(explicit)) {
216
+ return explicit;
217
+ }
218
+ const bundled = resolve2(import.meta.dir, "../../../native/snapshot.zig");
219
+ return existsSync2(bundled) ? bundled : null;
220
+ }
221
+ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
222
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
223
+ return false;
224
+ }
225
+ const zigBinary = Bun.which("zig");
226
+ const sourcePath = resolveNativeRuntimeSourcePath();
227
+ if (!zigBinary || !sourcePath) {
228
+ return false;
229
+ }
230
+ const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
231
+ try {
232
+ mkdirSync(dirname2(outputPath), { recursive: true });
233
+ const needsBuild = options.force === true || !existsSync2(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
234
+ if (!needsBuild) {
235
+ return true;
236
+ }
237
+ const build = Bun.spawn([
238
+ zigBinary,
239
+ "build-lib",
240
+ sourcePath,
241
+ "-dynamic",
242
+ "-O",
243
+ "ReleaseFast",
244
+ `-femit-bin=${tempOutputPath}`
245
+ ], {
246
+ cwd: import.meta.dir,
247
+ stdout: "pipe",
248
+ stderr: "pipe"
249
+ });
250
+ const exitCode = await build.exited;
251
+ if (exitCode !== 0 || !existsSync2(tempOutputPath)) {
252
+ rmSync(tempOutputPath, { force: true });
253
+ return false;
254
+ }
255
+ renameSync(tempOutputPath, outputPath);
256
+ return true;
257
+ } catch {
258
+ rmSync(tempOutputPath, { force: true });
259
+ return false;
260
+ }
261
+ }
262
+ function tryDlopenNativeRuntimeLibrary(outputPath) {
263
+ try {
264
+ return dlopen(outputPath, {
265
+ rig_scope_match: {
266
+ args: ["ptr", "ptr"],
267
+ returns: "u8"
268
+ },
269
+ snapshot_capture: {
270
+ args: ["ptr", "u64", "ptr", "u64"],
271
+ returns: "ptr"
272
+ },
273
+ snapshot_delta: {
274
+ args: ["ptr", "ptr"],
275
+ returns: "ptr"
276
+ },
277
+ snapshot_store_delta: {
278
+ args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
279
+ returns: "ptr"
280
+ },
281
+ snapshot_inspect_delta: {
282
+ args: ["ptr", "u64"],
283
+ returns: "ptr"
284
+ },
285
+ snapshot_apply_delta: {
286
+ args: ["ptr", "u64", "ptr", "u64"],
287
+ returns: "ptr"
288
+ },
289
+ snapshot_release: {
290
+ args: ["ptr"],
291
+ returns: "void"
292
+ },
293
+ runtime_hash_file: {
294
+ args: ["ptr", "u64"],
295
+ returns: "ptr"
296
+ },
297
+ runtime_hash_tree: {
298
+ args: ["ptr", "u64"],
299
+ returns: "ptr"
300
+ },
301
+ runtime_prepare_paths: {
302
+ args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
303
+ returns: "ptr"
304
+ },
305
+ runtime_link_dependency_layer: {
306
+ args: ["ptr", "u64", "ptr", "u64"],
307
+ returns: "ptr"
308
+ },
309
+ runtime_scan_worktrees: {
310
+ args: ["ptr", "u64"],
311
+ returns: "ptr"
312
+ }
313
+ });
314
+ } catch {
315
+ return null;
316
+ }
317
+ }
318
+
319
+ // packages/runtime/src/control-plane/native/scope-rules.ts
320
+ var activeRules = null;
321
+ function getScopeRules() {
322
+ return activeRules;
323
+ }
324
+
325
+ // packages/runtime/src/control-plane/native/utils.ts
326
+ function resolveMonorepoRoot2(projectRoot) {
327
+ return resolveMonorepoRoot(projectRoot);
328
+ }
329
+ var nativeScopeMatcher = null;
330
+ var scopeRegexCache = new Map;
331
+ function unique(values) {
332
+ return [...new Set(values)];
333
+ }
334
+ function normalizeRelativeScopePath(inputPath) {
335
+ let normalized = inputPath.replace(/^\.\//, "");
336
+ const rules = getScopeRules();
337
+ if (rules?.stripPrefixes) {
338
+ for (const prefix of rules.stripPrefixes) {
339
+ if (normalized.startsWith(prefix)) {
340
+ normalized = normalized.slice(prefix.length);
341
+ }
342
+ }
343
+ }
344
+ return normalized;
345
+ }
346
+ function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
347
+ let normalized = inputPath.replace(/^\.\//, "");
348
+ if (normalized.startsWith(projectRoot + "/")) {
349
+ normalized = normalized.slice(projectRoot.length + 1);
350
+ }
351
+ if (normalized.startsWith(monorepoRoot + "/")) {
352
+ normalized = normalized.slice(monorepoRoot.length + 1);
353
+ }
354
+ return normalizeRelativeScopePath(normalized);
355
+ }
356
+ function scopeMatches(path, scopes) {
357
+ const matcher = getNativeScopeMatcher();
358
+ const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
359
+ for (const scope of scopes) {
360
+ const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
361
+ for (const candidatePath of pathVariants) {
362
+ for (const candidateScope of scopeVariants) {
363
+ if (candidatePath === candidateScope) {
364
+ return true;
365
+ }
366
+ if (matcher.match(candidateScope, candidatePath)) {
367
+ return true;
368
+ }
369
+ }
370
+ }
371
+ }
372
+ return false;
373
+ }
374
+ function getNativeScopeMatcher() {
375
+ if (nativeScopeMatcher) {
376
+ return nativeScopeMatcher;
377
+ }
378
+ nativeScopeMatcher = createNativeScopeMatcher();
379
+ return nativeScopeMatcher;
380
+ }
381
+ function createNativeScopeMatcher() {
382
+ const library = requireNativeRuntimeLibrary("scope matching");
383
+ return {
384
+ match: (pattern, path) => {
385
+ const patternBuffer = Buffer.from(`${pattern}\x00`);
386
+ const pathBuffer = Buffer.from(`${path}\x00`);
387
+ return library.symbols.rig_scope_match(Number(ptr2(patternBuffer)), Number(ptr2(pathBuffer))) !== 0;
388
+ }
389
+ };
390
+ }
391
+
392
+ // packages/runtime/src/control-plane/runtime/guard.ts
393
+ import { optimizeNextInvocation } from "bun:jsc";
394
+ import { existsSync as existsSync3, readFileSync, statSync as statSync2 } from "fs";
395
+ import { resolve as resolve3 } from "path";
396
+
397
+ // packages/runtime/src/control-plane/runtime/guard-types.ts
398
+ var POLICY_VERSION = 1;
399
+
400
+ // packages/runtime/src/control-plane/runtime/guard.ts
401
+ var DEFAULT_SCOPE = {
402
+ fail_closed: true,
403
+ harness_paths_exempt: true,
404
+ runtime_paths_exempt: true
405
+ };
406
+ var DEFAULT_SANDBOX = {
407
+ mode: "enforce",
408
+ network: true,
409
+ read_deny: [],
410
+ write_allow_from_runtime: true
411
+ };
412
+ var DEFAULT_ISOLATION = {
413
+ default_mode: "worktree",
414
+ repo_symlink_fallback: false,
415
+ strict_provisioning: true,
416
+ fail_closed_on_provision_error: true
417
+ };
418
+ var DEFAULT_COMPLETION = {
419
+ derive_checks_from_scope: true,
420
+ checks: [],
421
+ typescript_config_probe: ["tsconfig.json"],
422
+ eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
423
+ };
424
+ var DEFAULT_RUNTIME_IMAGE = {
425
+ deps: {
426
+ monorepo_install: false,
427
+ hp_next_install: false
428
+ },
429
+ plugins_require_binaries: true
430
+ };
431
+ var DEFAULT_RUNTIME_SNAPSHOT = {
432
+ enabled: true
433
+ };
434
+ function defaultPolicy() {
435
+ return {
436
+ version: POLICY_VERSION,
437
+ mode: "enforce",
438
+ scope: { ...DEFAULT_SCOPE },
439
+ rules: [],
440
+ sandbox: { ...DEFAULT_SANDBOX },
441
+ isolation: { ...DEFAULT_ISOLATION },
442
+ completion: { ...DEFAULT_COMPLETION },
443
+ runtime_image: {
444
+ deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
445
+ plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
446
+ },
447
+ runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
448
+ };
449
+ }
450
+ var policyCache = null;
451
+ var policyCachePath = null;
452
+ var seededPolicyConfig = null;
453
+ var compiledRegexCache = new Map;
454
+ function loadPolicy(projectRoot) {
455
+ if (seededPolicyConfig) {
456
+ return seededPolicyConfig;
457
+ }
458
+ const configPath = resolve3(projectRoot, "rig/policy/policy.json");
459
+ if (!existsSync3(configPath)) {
460
+ return defaultPolicy();
461
+ }
462
+ let mtimeMs;
463
+ try {
464
+ mtimeMs = statSync2(configPath).mtimeMs;
465
+ } catch {
466
+ return defaultPolicy();
467
+ }
468
+ if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
469
+ return policyCache.config;
470
+ }
471
+ let parsed;
472
+ try {
473
+ parsed = JSON.parse(readFileSync(configPath, "utf-8"));
474
+ } catch {
475
+ return defaultPolicy();
476
+ }
477
+ const config = mergeWithDefaults(parsed);
478
+ policyCache = { mtimeMs, config };
479
+ policyCachePath = configPath;
480
+ return config;
481
+ }
482
+ function mergeWithDefaults(parsed) {
483
+ const base = defaultPolicy();
484
+ if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
485
+ base.mode = parsed.mode;
486
+ }
487
+ if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
488
+ const s = parsed.scope;
489
+ if (typeof s.fail_closed === "boolean")
490
+ base.scope.fail_closed = s.fail_closed;
491
+ if (typeof s.harness_paths_exempt === "boolean")
492
+ base.scope.harness_paths_exempt = s.harness_paths_exempt;
493
+ if (typeof s.runtime_paths_exempt === "boolean")
494
+ base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
495
+ }
496
+ if (Array.isArray(parsed.rules)) {
497
+ base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
498
+ }
499
+ if (Array.isArray(parsed.deny) && base.rules.length === 0) {
500
+ base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
501
+ }
502
+ if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
503
+ const sb = parsed.sandbox;
504
+ if (typeof sb.mode === "string" && isValidMode(sb.mode))
505
+ base.sandbox.mode = sb.mode;
506
+ if (typeof sb.network === "boolean")
507
+ base.sandbox.network = sb.network;
508
+ if (Array.isArray(sb.read_deny))
509
+ base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
510
+ if (typeof sb.write_allow_from_runtime === "boolean")
511
+ base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
512
+ }
513
+ if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
514
+ const iso = parsed.isolation;
515
+ if (iso.default_mode === "worktree")
516
+ base.isolation.default_mode = iso.default_mode;
517
+ if (typeof iso.repo_symlink_fallback === "boolean")
518
+ base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
519
+ if (typeof iso.strict_provisioning === "boolean")
520
+ base.isolation.strict_provisioning = iso.strict_provisioning;
521
+ if (typeof iso.fail_closed_on_provision_error === "boolean")
522
+ base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
523
+ }
524
+ if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
525
+ const comp = parsed.completion;
526
+ if (typeof comp.derive_checks_from_scope === "boolean")
527
+ base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
528
+ if (Array.isArray(comp.checks))
529
+ base.completion.checks = comp.checks.filter((v) => typeof v === "string");
530
+ if (Array.isArray(comp.typescript_config_probe))
531
+ base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
532
+ if (Array.isArray(comp.eslint_config_probe))
533
+ base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
534
+ }
535
+ if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
536
+ const runtimeImage = parsed.runtime_image;
537
+ if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
538
+ const deps = runtimeImage.deps;
539
+ if (typeof deps.monorepo_install === "boolean") {
540
+ base.runtime_image.deps.monorepo_install = deps.monorepo_install;
541
+ }
542
+ if (typeof deps.hp_next_install === "boolean") {
543
+ base.runtime_image.deps.hp_next_install = deps.hp_next_install;
544
+ }
545
+ }
546
+ if (typeof runtimeImage.plugins_require_binaries === "boolean") {
547
+ base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
548
+ }
549
+ }
550
+ if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
551
+ const runtimeSnapshot = parsed.runtime_snapshot;
552
+ if (typeof runtimeSnapshot.enabled === "boolean") {
553
+ base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
554
+ }
555
+ }
556
+ return base;
557
+ }
558
+ function isValidMode(value) {
559
+ return value === "off" || value === "observe" || value === "enforce";
560
+ }
561
+ function isValidRule(value) {
562
+ if (!value || typeof value !== "object" || Array.isArray(value))
563
+ return false;
564
+ const r = value;
565
+ return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
566
+ }
567
+ function migrateLegacyDeny(deny) {
568
+ const rules = [];
569
+ for (const entry of deny) {
570
+ if (typeof entry.id !== "string")
571
+ continue;
572
+ const match = {};
573
+ if (typeof entry.pattern === "string")
574
+ match.pattern = entry.pattern;
575
+ if (typeof entry.regex === "string")
576
+ match.regex = entry.regex;
577
+ if (!match.pattern && !match.regex)
578
+ continue;
579
+ rules.push({
580
+ id: entry.id,
581
+ category: "command",
582
+ match,
583
+ action: "block",
584
+ ...typeof entry.description === "string" ? { description: entry.description } : {}
585
+ });
586
+ }
587
+ return rules;
588
+ }
589
+ function precompilePolicyRuleRegexes(rules) {
590
+ return rules.map((rule) => {
591
+ const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
592
+ const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
593
+ return {
594
+ ...rule,
595
+ ...compiledRegex ? { compiledRegex } : {},
596
+ ...compiledUnlessRegex ? { compiledUnlessRegex } : {}
597
+ };
598
+ });
599
+ }
600
+ function getRegexUnsafeReason(pattern) {
601
+ if (pattern.length > 512) {
602
+ return "pattern exceeds max safe length (512 chars)";
603
+ }
604
+ if (/\\[1-9]/.test(pattern)) {
605
+ return "pattern uses backreferences";
606
+ }
607
+ if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
608
+ return "pattern contains nested quantifiers";
609
+ }
610
+ if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
611
+ return "pattern contains nested broad quantifiers";
612
+ }
613
+ return null;
614
+ }
615
+ function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
616
+ const cached = compiledRegexCache.get(pattern);
617
+ if (cached !== undefined) {
618
+ return cached ?? undefined;
619
+ }
620
+ const unsafeReason = getRegexUnsafeReason(pattern);
621
+ if (unsafeReason) {
622
+ if (logOnFailure) {
623
+ console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
624
+ }
625
+ compiledRegexCache.set(pattern, null);
626
+ return;
627
+ }
628
+ try {
629
+ const compiled = new RegExp(pattern);
630
+ compiledRegexCache.set(pattern, compiled);
631
+ return compiled;
632
+ } catch (error) {
633
+ if (logOnFailure) {
634
+ const message = error instanceof Error ? error.message : String(error);
635
+ console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
636
+ }
637
+ compiledRegexCache.set(pattern, null);
638
+ return;
639
+ }
640
+ }
641
+ function matchRule(rule, input) {
642
+ const { match } = rule;
643
+ if (match.pattern && input.includes(match.pattern)) {
644
+ return true;
645
+ }
646
+ if (match.regex) {
647
+ const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
648
+ if (!compiled) {
649
+ return false;
650
+ }
651
+ try {
652
+ return compiled.test(input);
653
+ } catch {
654
+ return false;
655
+ }
656
+ }
657
+ return false;
658
+ }
659
+ function matchRuleUnless(rule, command, taskId) {
660
+ if (!rule.unless)
661
+ return false;
662
+ if (rule.unless.regex) {
663
+ const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
664
+ if (!compiled) {
665
+ return false;
666
+ }
667
+ try {
668
+ if (compiled.test(command))
669
+ return true;
670
+ } catch {}
671
+ }
672
+ if (rule.unless.task_in && taskId) {
673
+ if (rule.unless.task_in.includes(taskId))
674
+ return true;
675
+ }
676
+ return false;
677
+ }
678
+ function resolveAction(mode, matched) {
679
+ if (matched.length === 0)
680
+ return "allow";
681
+ if (mode === "off")
682
+ return "allow";
683
+ if (mode === "observe")
684
+ return "warn";
685
+ return "block";
686
+ }
687
+ function resolveAbsolutePath(projectRoot, rawPath) {
688
+ if (rawPath.startsWith("/"))
689
+ return resolve3(rawPath);
690
+ return resolve3(projectRoot, rawPath);
691
+ }
692
+ function isHarnessPath(projectRoot, rawPath) {
693
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
694
+ const managedRoots = [
695
+ resolve3(projectRoot, "rig"),
696
+ resolve3(projectRoot, ".rig"),
697
+ resolve3(projectRoot, "artifacts")
698
+ ];
699
+ return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
700
+ }
701
+ function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
702
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
703
+ if (taskWorkspace) {
704
+ const workspaceRigRoot = resolve3(taskWorkspace, ".rig");
705
+ const workspaceArtifactsRoot = resolve3(taskWorkspace, "artifacts");
706
+ if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
707
+ return true;
708
+ }
709
+ }
710
+ const runtimeRoot = resolve3(projectRoot, ".rig/runtime/agents");
711
+ return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
712
+ }
713
+ function isTestFile(path) {
714
+ return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
715
+ }
716
+ function evaluate(context) {
717
+ const policy = loadPolicy(context.projectRoot);
718
+ switch (context.evaluation.type) {
719
+ case "tool-call":
720
+ return evaluateToolCall(policy, context);
721
+ case "command":
722
+ return evaluateCommand(policy, context);
723
+ case "content-write":
724
+ return evaluateContent(policy, context);
725
+ case "file-access":
726
+ return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
727
+ }
728
+ }
729
+ function evaluateScope(policy, context, filePath, access) {
730
+ const allowed = () => ({
731
+ allowed: true,
732
+ matchedRules: [],
733
+ action: "allow",
734
+ failClosed: false
735
+ });
736
+ if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
737
+ return allowed();
738
+ }
739
+ if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
740
+ return allowed();
741
+ }
742
+ if (!context.taskId) {
743
+ if (access === "write" && policy.scope.fail_closed) {
744
+ return {
745
+ allowed: false,
746
+ matchedRules: [],
747
+ action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
748
+ failClosed: true
749
+ };
750
+ }
751
+ return allowed();
752
+ }
753
+ const scopes = context.taskScopes || [];
754
+ if (scopes.length === 0) {
755
+ return allowed();
756
+ }
757
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
758
+ const absPath = resolve3(filePath);
759
+ if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
760
+ const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
761
+ const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
762
+ return {
763
+ allowed: policy.mode !== "enforce",
764
+ matchedRules: matched2,
765
+ action: resolveAction(policy.mode, matched2),
766
+ failClosed: false
767
+ };
768
+ }
769
+ }
770
+ const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
771
+ let normalizedPath = filePath;
772
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
773
+ normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
774
+ }
775
+ normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
776
+ if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
777
+ return allowed();
778
+ }
779
+ const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
780
+ const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
781
+ return {
782
+ allowed: policy.mode !== "enforce",
783
+ matchedRules: matched,
784
+ action: resolveAction(policy.mode, matched),
785
+ failClosed: false
786
+ };
787
+ }
788
+ function evaluateCommand(policy, context) {
789
+ const evaluation = context.evaluation;
790
+ if (evaluation.type !== "command") {
791
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
792
+ }
793
+ const command = evaluation.command;
794
+ const matchedRules = [];
795
+ for (const rule of policy.rules) {
796
+ if (rule.category !== "command")
797
+ continue;
798
+ if (!matchRule(rule, command))
799
+ continue;
800
+ if (matchRuleUnless(rule, command, context.taskId))
801
+ continue;
802
+ matchedRules.push({
803
+ id: rule.id,
804
+ category: rule.category,
805
+ description: rule.description,
806
+ reason: rule.description || `Matched rule ${rule.id}`
807
+ });
808
+ }
809
+ const writeTarget = extractWriteTarget(command);
810
+ if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
811
+ const scopeResult = evaluateScope(policy, context, writeTarget, "write");
812
+ if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
813
+ matchedRules.push(...scopeResult.matchedRules);
814
+ }
815
+ }
816
+ const action = resolveAction(policy.mode, matchedRules);
817
+ return {
818
+ allowed: action !== "block",
819
+ matchedRules,
820
+ action,
821
+ failClosed: false
822
+ };
823
+ }
824
+ function extractWriteTarget(command) {
825
+ const redirect = command.match(/>>?\s+([^\s;|&]+)/);
826
+ if (redirect?.[1])
827
+ return redirect[1];
828
+ const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
829
+ if (tee?.[2])
830
+ return tee[2];
831
+ return "";
832
+ }
833
+ function evaluateContent(policy, context) {
834
+ const evaluation = context.evaluation;
835
+ if (evaluation.type !== "content-write") {
836
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
837
+ }
838
+ const { content, file_path } = evaluation;
839
+ const matchedRules = [];
840
+ const scopeResult = evaluateScope(policy, context, file_path, "write");
841
+ if (scopeResult.matchedRules.length > 0) {
842
+ matchedRules.push(...scopeResult.matchedRules);
843
+ }
844
+ for (const rule of policy.rules) {
845
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
846
+ continue;
847
+ if (rule.applies_to === "test-files" && !isTestFile(file_path))
848
+ continue;
849
+ if (!matchRule(rule, content))
850
+ continue;
851
+ if (matchRuleUnless(rule, content, context.taskId))
852
+ continue;
853
+ matchedRules.push({
854
+ id: rule.id,
855
+ category: rule.category,
856
+ description: rule.description,
857
+ reason: rule.description || `Matched rule ${rule.id}`
858
+ });
859
+ }
860
+ const action = resolveAction(policy.mode, matchedRules);
861
+ return {
862
+ allowed: action !== "block",
863
+ matchedRules,
864
+ action,
865
+ failClosed: false
866
+ };
867
+ }
868
+ function evaluateToolCall(policy, context) {
869
+ const evaluation = context.evaluation;
870
+ if (evaluation.type !== "tool-call") {
871
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
872
+ }
873
+ const { tool_name, tool_input } = evaluation;
874
+ const allMatched = [];
875
+ const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
876
+ for (const fp of filePaths) {
877
+ const access = isWriteTool(tool_name) ? "write" : "read";
878
+ const scopeResult = evaluateScope(policy, context, fp, access);
879
+ if (scopeResult.matchedRules.length > 0) {
880
+ allMatched.push(...scopeResult.matchedRules);
881
+ }
882
+ }
883
+ const content = extractContentFromToolInput(tool_input);
884
+ if (content) {
885
+ const filePath = filePaths[0] || "";
886
+ const contentContext = {
887
+ ...context,
888
+ evaluation: { type: "content-write", file_path: filePath, content }
889
+ };
890
+ const contentPolicy = loadPolicy(context.projectRoot);
891
+ for (const rule of contentPolicy.rules) {
892
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
893
+ continue;
894
+ if (rule.applies_to === "test-files" && !isTestFile(filePath))
895
+ continue;
896
+ if (!matchRule(rule, content))
897
+ continue;
898
+ if (matchRuleUnless(rule, content, context.taskId))
899
+ continue;
900
+ allMatched.push({
901
+ id: rule.id,
902
+ category: rule.category,
903
+ description: rule.description,
904
+ reason: rule.description || `Matched rule ${rule.id}`
905
+ });
906
+ }
907
+ }
908
+ if (tool_name === "Bash") {
909
+ const command = String(tool_input.command || tool_input.cmd || "");
910
+ if (command) {
911
+ const cmdContext = {
912
+ ...context,
913
+ evaluation: { type: "command", command }
914
+ };
915
+ const cmdResult = evaluateCommand(policy, cmdContext);
916
+ if (cmdResult.matchedRules.length > 0) {
917
+ allMatched.push(...cmdResult.matchedRules);
918
+ }
919
+ }
920
+ }
921
+ const seen = new Set;
922
+ const deduplicated = [];
923
+ for (const rule of allMatched) {
924
+ if (!seen.has(rule.id)) {
925
+ seen.add(rule.id);
926
+ deduplicated.push(rule);
927
+ }
928
+ }
929
+ const action = resolveAction(policy.mode, deduplicated);
930
+ return {
931
+ allowed: action !== "block",
932
+ matchedRules: deduplicated,
933
+ action,
934
+ failClosed: false
935
+ };
936
+ }
937
+ function isWriteTool(toolName) {
938
+ return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
939
+ }
940
+ function extractFilePathsFromToolInput(toolName, input) {
941
+ const paths = [];
942
+ const add = (value) => {
943
+ if (typeof value === "string" && value.trim()) {
944
+ paths.push(value.trim());
945
+ }
946
+ };
947
+ if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
948
+ add(input.file_path);
949
+ add(input.path);
950
+ } else if (toolName === "Glob") {
951
+ add(input.path);
952
+ } else if (toolName === "Grep") {
953
+ add(input.path);
954
+ } else {
955
+ add(input.file_path);
956
+ add(input.path);
957
+ }
958
+ return paths;
959
+ }
960
+ function extractContentFromToolInput(input) {
961
+ if (typeof input.content === "string")
962
+ return input.content;
963
+ if (typeof input.new_string === "string")
964
+ return input.new_string;
965
+ return "";
966
+ }
967
+ var guardHotPathPrimed = false;
968
+ function primeGuardHotPaths() {
969
+ if (guardHotPathPrimed) {
970
+ return;
971
+ }
972
+ guardHotPathPrimed = true;
973
+ try {
974
+ optimizeNextInvocation(matchRule);
975
+ optimizeNextInvocation(evaluate);
976
+ } catch {}
977
+ }
978
+ primeGuardHotPaths();
979
+
980
+ // packages/runtime/src/control-plane/controlled-bash.ts
981
+ var BUILD_CONFIG = readBuildConfig();
982
+ var BAKED_PROJECT_ROOT = BUILD_CONFIG.AGENT_PROJECT_ROOT ?? "";
983
+ var BAKED_TS_API_TESTS_DIR = BUILD_CONFIG.AGENT_TS_API_TESTS_DIR ?? "";
984
+ var BAKED_RIG_AGENT_BIN = BUILD_CONFIG.AGENT_RIG_AGENT_BIN ?? "";
985
+ function resolveProjectRoot(fallbackDir) {
986
+ if (BAKED_PROJECT_ROOT) {
987
+ return BAKED_PROJECT_ROOT;
988
+ }
989
+ if (process.env.RIG_HOST_PROJECT_ROOT) {
990
+ return process.env.RIG_HOST_PROJECT_ROOT;
991
+ }
992
+ if (process.env.PROJECT_RIG_ROOT) {
993
+ return process.env.PROJECT_RIG_ROOT;
994
+ }
995
+ const candidates = [
996
+ resolve4(process.execPath, "..", ".."),
997
+ resolve4(dirname3(fallbackDir), ".."),
998
+ process.cwd()
999
+ ];
1000
+ for (const candidate of candidates) {
1001
+ try {
1002
+ if (readFileSync2(resolve4(candidate, ".gitignore"), "utf-8").includes("repos/")) {
1003
+ return candidate;
1004
+ }
1005
+ } catch {}
1006
+ try {
1007
+ readFileSync2(resolve4(candidate, "rig/config.sh"), "utf-8");
1008
+ return candidate;
1009
+ } catch {}
1010
+ }
1011
+ return resolve4(dirname3(fallbackDir), "..");
1012
+ }
1013
+ function resolveRealBash() {
1014
+ return process.env.RIG_REAL_BASH || "/bin/bash";
1015
+ }
1016
+ function runtimeShellEnv(projectRoot) {
1017
+ const layout = resolveRigLayout(projectRoot);
1018
+ const monorepoRoot = process.env.MONOREPO_ROOT?.trim() || resolveMonorepoRoot2(projectRoot);
1019
+ const rigAgentBin = BAKED_RIG_AGENT_BIN || resolve4(layout.binDir, "rig-agent");
1020
+ const tsApiTestsDir = BAKED_TS_API_TESTS_DIR || resolve4(layout.runtimeWorkspaceRoot, "TSAPITests");
1021
+ return {
1022
+ PROJECT_RIG_ROOT: projectRoot,
1023
+ RIG_TASK_WORKSPACE: layout.runtimeWorkspaceRoot,
1024
+ MONOREPO_ROOT: monorepoRoot,
1025
+ RIG_AGENT_BIN: rigAgentBin,
1026
+ TS_API_TESTS_DIR: tsApiTestsDir
1027
+ };
1028
+ }
1029
+ function toCommand(argv) {
1030
+ return argv.join(" ");
1031
+ }
1032
+ async function appendAuditLog(projectRoot, payload) {
1033
+ const logDir = resolveRigLayout(projectRoot).logsDir;
1034
+ const logFile = resolve4(logDir, "controlled-bash.jsonl");
1035
+ const line = `${JSON.stringify(payload)}
1036
+ `;
1037
+ await mkdir(dirname3(logFile), { recursive: true });
1038
+ await appendFile(logFile, line, "utf-8");
1039
+ }
1040
+ async function runControlledBash(args, options) {
1041
+ const projectRoot = resolveProjectRoot(options.projectRootFallbackDir);
1042
+ const requestedCwd = (() => {
1043
+ try {
1044
+ return process.cwd();
1045
+ } catch {
1046
+ return projectRoot;
1047
+ }
1048
+ })();
1049
+ const envMode = process.env.RIG_BASH_MODE;
1050
+ const mode = envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(projectRoot).mode;
1051
+ const realBash = resolveRealBash();
1052
+ const command = toCommand(args);
1053
+ const decision = evaluate({ projectRoot, evaluation: { type: "command", command } });
1054
+ const action = resolveAction(mode, decision.matchedRules);
1055
+ if (mode !== "off") {
1056
+ await appendAuditLog(projectRoot, {
1057
+ timestamp: new Date().toISOString(),
1058
+ mode,
1059
+ cwd: process.cwd(),
1060
+ pid: process.pid,
1061
+ ppid: process.ppid,
1062
+ argv: args,
1063
+ command,
1064
+ matchedRuleIds: decision.matchedRules.map((r) => r.id)
1065
+ });
1066
+ }
1067
+ if (action === "block") {
1068
+ await appendAuditLog(projectRoot, {
1069
+ timestamp: new Date().toISOString(),
1070
+ mode,
1071
+ action: "blocked",
1072
+ command,
1073
+ matchedRuleIds: decision.matchedRules.map((r) => r.id)
1074
+ });
1075
+ console.error(`[controlled-bash] blocked by policy rule(s): ${decision.matchedRules.map((r) => r.id).join(", ")}`);
1076
+ return 126;
1077
+ }
1078
+ if (action === "warn") {
1079
+ await appendAuditLog(projectRoot, {
1080
+ timestamp: new Date().toISOString(),
1081
+ mode,
1082
+ action: "warn",
1083
+ command,
1084
+ matchedRuleIds: decision.matchedRules.map((r) => r.id)
1085
+ });
1086
+ }
1087
+ const child = Bun.spawn([realBash, ...args], {
1088
+ stdin: "inherit",
1089
+ stdout: "inherit",
1090
+ stderr: "inherit",
1091
+ cwd: requestedCwd,
1092
+ env: {
1093
+ ...process.env,
1094
+ ...runtimeShellEnv(projectRoot),
1095
+ RIG_BASH_ACTIVE: "1"
1096
+ }
1097
+ });
1098
+ return await child.exited;
1099
+ }
1100
+ if (import.meta.main) {
1101
+ runControlledBash(process.argv.slice(2), { projectRootFallbackDir: import.meta.dir }).then((code) => {
1102
+ process.exit(code);
1103
+ }).catch((error) => {
1104
+ console.error("[controlled-bash] fatal error", error);
1105
+ process.exit(1);
1106
+ });
1107
+ }
1108
+ export {
1109
+ runControlledBash,
1110
+ resolveProjectRoot
1111
+ };