@h-rig/cli-surface-plugin 0.0.6-alpha.156 → 0.0.6-alpha.158

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 (112) hide show
  1. package/dist/src/app/drone-ui.d.ts +0 -11
  2. package/dist/src/app/drone-ui.js +0 -114
  3. package/dist/src/commands/_async-ui.d.ts +1 -1
  4. package/dist/src/commands/_cli-format.d.ts +0 -29
  5. package/dist/src/commands/_cli-format.js +59 -113
  6. package/dist/src/commands/_connection-state.d.ts +6 -33
  7. package/dist/src/commands/_connection-state.js +654 -138
  8. package/dist/src/commands/_doctor-checks.d.ts +2 -5
  9. package/dist/src/commands/_doctor-checks.js +10 -9
  10. package/dist/src/commands/_help-catalog.d.ts +2 -1
  11. package/dist/src/commands/_help-catalog.js +654 -7
  12. package/dist/src/commands/_inprocess-services.d.ts +5 -5
  13. package/dist/src/commands/_inprocess-services.js +1 -1
  14. package/dist/src/commands/_parsers.js +651 -12
  15. package/dist/src/commands/_paths.d.ts +0 -2
  16. package/dist/src/commands/_paths.js +2 -10
  17. package/dist/src/commands/_pi-install.d.ts +2 -12
  18. package/dist/src/commands/_pi-install.js +3 -36
  19. package/dist/src/commands/_policy.js +659 -20
  20. package/dist/src/commands/agent.d.ts +1 -1
  21. package/dist/src/commands/agent.js +675 -24
  22. package/dist/src/commands/config.d.ts +1 -1
  23. package/dist/src/commands/config.js +656 -21
  24. package/dist/src/commands/dist.d.ts +1 -1
  25. package/dist/src/commands/dist.js +828 -102
  26. package/dist/src/commands/doctor.d.ts +1 -1
  27. package/dist/src/commands/doctor.js +658 -12
  28. package/dist/src/commands/github.d.ts +1 -1
  29. package/dist/src/commands/github.js +658 -19
  30. package/dist/src/commands/inbox.d.ts +12 -8
  31. package/dist/src/commands/inbox.js +741 -22
  32. package/dist/src/commands/init.d.ts +17 -19
  33. package/dist/src/commands/init.js +836 -306
  34. package/dist/src/commands/inspect.d.ts +5 -6
  35. package/dist/src/commands/inspect.js +754 -42
  36. package/dist/src/commands/pi.d.ts +1 -1
  37. package/dist/src/commands/pi.js +655 -16
  38. package/dist/src/commands/plugin.d.ts +9 -9
  39. package/dist/src/commands/plugin.js +652 -13
  40. package/dist/src/commands/profile-and-review.d.ts +1 -1
  41. package/dist/src/commands/profile-and-review.js +655 -16
  42. package/dist/src/commands/queue.d.ts +1 -1
  43. package/dist/src/commands/queue.js +871 -12
  44. package/dist/src/commands/remote-client.d.ts +152 -0
  45. package/dist/src/commands/remote-client.js +475 -0
  46. package/dist/src/commands/remote.d.ts +1 -1
  47. package/dist/src/commands/remote.js +1100 -29
  48. package/dist/src/commands/repo-git-harness.d.ts +1 -1
  49. package/dist/src/commands/repo-git-harness.js +2321 -47
  50. package/dist/src/commands/run.d.ts +10 -6
  51. package/dist/src/commands/run.js +830 -50
  52. package/dist/src/commands/server.d.ts +1 -1
  53. package/dist/src/commands/server.js +649 -11
  54. package/dist/src/commands/setup.d.ts +2 -2
  55. package/dist/src/commands/setup.js +829 -18
  56. package/dist/src/commands/stats.d.ts +2 -4
  57. package/dist/src/commands/stats.js +1299 -20
  58. package/dist/src/commands/test.d.ts +1 -1
  59. package/dist/src/commands/test.js +648 -9
  60. package/dist/src/commands/triage.d.ts +2 -3
  61. package/dist/src/commands/triage.js +657 -11
  62. package/dist/src/commands/workspace.d.ts +1 -1
  63. package/dist/src/commands/workspace.js +1280 -15
  64. package/dist/src/control-plane/agent-binary-build.d.ts +9 -0
  65. package/dist/src/control-plane/agent-binary-build.js +88 -0
  66. package/dist/src/control-plane/embedded-native-assets.d.ts +7 -0
  67. package/dist/src/control-plane/embedded-native-assets.js +6 -0
  68. package/dist/src/control-plane/guard.d.ts +17 -0
  69. package/dist/src/control-plane/guard.js +684 -0
  70. package/dist/src/control-plane/harness-cli.d.ts +12 -0
  71. package/dist/src/control-plane/harness-cli.js +1623 -0
  72. package/dist/src/control-plane/native/git-ops.d.ts +67 -0
  73. package/dist/src/control-plane/native/git-ops.js +1381 -0
  74. package/dist/src/control-plane/native/github-auth-env.d.ts +1 -0
  75. package/dist/src/control-plane/native/github-auth-env.js +21 -0
  76. package/dist/src/control-plane/native/host-git.d.ts +4 -0
  77. package/dist/src/control-plane/native/host-git.js +51 -0
  78. package/dist/src/control-plane/priority-queue.d.ts +22 -0
  79. package/dist/src/control-plane/priority-queue.js +212 -0
  80. package/dist/src/control-plane/rigfig.d.ts +9 -0
  81. package/dist/src/control-plane/rigfig.js +70 -0
  82. package/dist/src/control-plane/scope.d.ts +3 -0
  83. package/dist/src/control-plane/scope.js +58 -0
  84. package/dist/src/control-plane/setup-status.d.ts +44 -0
  85. package/dist/src/control-plane/setup-status.js +164 -0
  86. package/dist/src/control-plane/task-data.d.ts +2 -0
  87. package/dist/src/control-plane/task-data.js +12 -0
  88. package/dist/src/control-plane/workspace-ops.d.ts +79 -0
  89. package/dist/src/control-plane/workspace-ops.js +639 -0
  90. package/dist/src/help-catalog-data.d.ts +7 -0
  91. package/dist/src/help-catalog-data.js +660 -0
  92. package/dist/src/kernel-dispatch.js +1 -3
  93. package/dist/src/plugin.js +10072 -30
  94. package/dist/src/runner.d.ts +7 -9
  95. package/dist/src/runner.js +750 -30
  96. package/package.json +12 -13
  97. package/dist/src/commands/_json-output.d.ts +0 -11
  98. package/dist/src/commands/_json-output.js +0 -54
  99. package/dist/src/commands/_pi-frontend.d.ts +0 -35
  100. package/dist/src/commands/_pi-frontend.js +0 -64
  101. package/dist/src/commands/_run-driver-helpers.d.ts +0 -26
  102. package/dist/src/commands/_run-driver-helpers.js +0 -132
  103. package/dist/src/commands/task-run-driver.d.ts +0 -93
  104. package/dist/src/commands/task-run-driver.js +0 -136
  105. package/dist/src/commands/task.d.ts +0 -46
  106. package/dist/src/commands/task.js +0 -555
  107. package/dist/src/provider-model.d.ts +0 -34
  108. package/dist/src/provider-model.js +0 -56
  109. package/dist/src/rig-config-package-deps.d.ts +0 -10
  110. package/dist/src/rig-config-package-deps.js +0 -272
  111. package/dist/src/version.d.ts +0 -8
  112. package/dist/src/version.js +0 -47
@@ -1,17 +1,656 @@
1
1
  // @bun
2
2
  // packages/cli-surface-plugin/src/runner.ts
3
- import { EventBus } from "@rig/runtime/control-plane/runtime/events";
4
- import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
5
- import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
6
- import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
3
+ import { EventBus } from "@rig/core/runtime-events";
4
+ import { CliError as RuntimeCliError } from "@rig/contracts";
7
5
 
6
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
7
+ import { optimizeNextInvocation } from "bun:jsc";
8
+ import { existsSync, readFileSync, statSync } from "fs";
9
+ import { resolve } from "path";
10
+
11
+ // packages/cli-surface-plugin/src/control-plane/scope.ts
12
+ import { getScopeRules } from "@rig/core/scope-rules";
13
+ var scopeRegexCache = new Map;
14
+ function unique(values) {
15
+ return [...new Set(values)];
16
+ }
17
+ function normalizeRelativeScopePath(inputPath) {
18
+ let normalized = inputPath.replace(/^\.\//, "");
19
+ const rules = getScopeRules();
20
+ if (rules?.stripPrefixes) {
21
+ for (const prefix of rules.stripPrefixes) {
22
+ if (normalized.startsWith(prefix)) {
23
+ normalized = normalized.slice(prefix.length);
24
+ }
25
+ }
26
+ }
27
+ return normalized;
28
+ }
29
+ function normalizePathToScope(projectRoot, monorepoRoot, inputPath) {
30
+ let normalized = inputPath.replace(/^\.\//, "");
31
+ if (normalized.startsWith(projectRoot + "/")) {
32
+ normalized = normalized.slice(projectRoot.length + 1);
33
+ }
34
+ if (normalized.startsWith(monorepoRoot + "/")) {
35
+ normalized = normalized.slice(monorepoRoot.length + 1);
36
+ }
37
+ return normalizeRelativeScopePath(normalized);
38
+ }
39
+ function scopeGlobToRegex(glob) {
40
+ const cached = scopeRegexCache.get(glob);
41
+ if (cached) {
42
+ return cached;
43
+ }
44
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
45
+ const compiled = new RegExp(`^${escaped}$`);
46
+ scopeRegexCache.set(glob, compiled);
47
+ return compiled;
48
+ }
49
+ function scopeMatches(path, scopes) {
50
+ const pathVariants = unique([path, normalizeRelativeScopePath(path)]);
51
+ for (const scope of scopes) {
52
+ const scopeVariants = unique([scope, normalizeRelativeScopePath(scope)]);
53
+ for (const candidatePath of pathVariants) {
54
+ for (const candidateScope of scopeVariants) {
55
+ if (candidatePath === candidateScope || scopeGlobToRegex(candidateScope).test(candidatePath)) {
56
+ return true;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+
64
+ // packages/cli-surface-plugin/src/control-plane/guard.ts
65
+ import {
66
+ POLICY_VERSION
67
+ } from "@rig/contracts";
68
+ var DEFAULT_SCOPE = {
69
+ fail_closed: true,
70
+ harness_paths_exempt: true,
71
+ runtime_paths_exempt: true
72
+ };
73
+ var DEFAULT_SANDBOX = {
74
+ mode: "enforce",
75
+ network: true,
76
+ read_deny: [],
77
+ write_allow_from_runtime: true
78
+ };
79
+ var DEFAULT_ISOLATION = {
80
+ default_mode: "worktree",
81
+ repo_symlink_fallback: false,
82
+ strict_provisioning: true,
83
+ fail_closed_on_provision_error: true
84
+ };
85
+ var DEFAULT_COMPLETION = {
86
+ derive_checks_from_scope: true,
87
+ checks: [],
88
+ typescript_config_probe: ["tsconfig.json"],
89
+ eslint_config_probe: [".eslintrc.js", ".eslintrc.json", "eslint.config.js"]
90
+ };
91
+ var DEFAULT_RUNTIME_IMAGE = {
92
+ deps: {
93
+ monorepo_install: false,
94
+ hp_next_install: false
95
+ },
96
+ plugins_require_binaries: true
97
+ };
98
+ var DEFAULT_RUNTIME_SNAPSHOT = {
99
+ enabled: true
100
+ };
101
+ function defaultPolicy() {
102
+ return {
103
+ version: POLICY_VERSION,
104
+ mode: "enforce",
105
+ scope: { ...DEFAULT_SCOPE },
106
+ rules: [],
107
+ sandbox: { ...DEFAULT_SANDBOX },
108
+ isolation: { ...DEFAULT_ISOLATION },
109
+ completion: { ...DEFAULT_COMPLETION },
110
+ runtime_image: {
111
+ deps: { ...DEFAULT_RUNTIME_IMAGE.deps },
112
+ plugins_require_binaries: DEFAULT_RUNTIME_IMAGE.plugins_require_binaries
113
+ },
114
+ runtime_snapshot: { ...DEFAULT_RUNTIME_SNAPSHOT }
115
+ };
116
+ }
117
+ var policyCache = null;
118
+ var policyCachePath = null;
119
+ var seededPolicyConfig = null;
120
+ var compiledRegexCache = new Map;
121
+ function loadPolicy(projectRoot) {
122
+ if (seededPolicyConfig) {
123
+ return seededPolicyConfig;
124
+ }
125
+ const configPath = resolve(projectRoot, "rig/policy/policy.json");
126
+ if (!existsSync(configPath)) {
127
+ return defaultPolicy();
128
+ }
129
+ let mtimeMs;
130
+ try {
131
+ mtimeMs = statSync(configPath).mtimeMs;
132
+ } catch {
133
+ return defaultPolicy();
134
+ }
135
+ if (policyCache && policyCachePath === configPath && policyCache.mtimeMs === mtimeMs) {
136
+ return policyCache.config;
137
+ }
138
+ let parsed;
139
+ try {
140
+ parsed = JSON.parse(readFileSync(configPath, "utf-8"));
141
+ } catch {
142
+ return defaultPolicy();
143
+ }
144
+ const config = mergeWithDefaults(parsed);
145
+ policyCache = { mtimeMs, config };
146
+ policyCachePath = configPath;
147
+ return config;
148
+ }
149
+ function mergeWithDefaults(parsed) {
150
+ const base = defaultPolicy();
151
+ if (typeof parsed.mode === "string" && isValidMode(parsed.mode)) {
152
+ base.mode = parsed.mode;
153
+ }
154
+ if (parsed.scope && typeof parsed.scope === "object" && !Array.isArray(parsed.scope)) {
155
+ const s = parsed.scope;
156
+ if (typeof s.fail_closed === "boolean")
157
+ base.scope.fail_closed = s.fail_closed;
158
+ if (typeof s.harness_paths_exempt === "boolean")
159
+ base.scope.harness_paths_exempt = s.harness_paths_exempt;
160
+ if (typeof s.runtime_paths_exempt === "boolean")
161
+ base.scope.runtime_paths_exempt = s.runtime_paths_exempt;
162
+ }
163
+ if (Array.isArray(parsed.rules)) {
164
+ base.rules = precompilePolicyRuleRegexes(parsed.rules.filter(isValidRule));
165
+ }
166
+ if (Array.isArray(parsed.deny) && base.rules.length === 0) {
167
+ base.rules = precompilePolicyRuleRegexes(migrateLegacyDeny(parsed.deny));
168
+ }
169
+ if (parsed.sandbox && typeof parsed.sandbox === "object" && !Array.isArray(parsed.sandbox)) {
170
+ const sb = parsed.sandbox;
171
+ if (typeof sb.mode === "string" && isValidMode(sb.mode))
172
+ base.sandbox.mode = sb.mode;
173
+ if (typeof sb.network === "boolean")
174
+ base.sandbox.network = sb.network;
175
+ if (Array.isArray(sb.read_deny))
176
+ base.sandbox.read_deny = sb.read_deny.filter((v) => typeof v === "string");
177
+ if (typeof sb.write_allow_from_runtime === "boolean")
178
+ base.sandbox.write_allow_from_runtime = sb.write_allow_from_runtime;
179
+ }
180
+ if (parsed.isolation && typeof parsed.isolation === "object" && !Array.isArray(parsed.isolation)) {
181
+ const iso = parsed.isolation;
182
+ if (iso.default_mode === "worktree")
183
+ base.isolation.default_mode = iso.default_mode;
184
+ if (typeof iso.repo_symlink_fallback === "boolean")
185
+ base.isolation.repo_symlink_fallback = iso.repo_symlink_fallback;
186
+ if (typeof iso.strict_provisioning === "boolean")
187
+ base.isolation.strict_provisioning = iso.strict_provisioning;
188
+ if (typeof iso.fail_closed_on_provision_error === "boolean")
189
+ base.isolation.fail_closed_on_provision_error = iso.fail_closed_on_provision_error;
190
+ }
191
+ if (parsed.completion && typeof parsed.completion === "object" && !Array.isArray(parsed.completion)) {
192
+ const comp = parsed.completion;
193
+ if (typeof comp.derive_checks_from_scope === "boolean")
194
+ base.completion.derive_checks_from_scope = comp.derive_checks_from_scope;
195
+ if (Array.isArray(comp.checks))
196
+ base.completion.checks = comp.checks.filter((v) => typeof v === "string");
197
+ if (Array.isArray(comp.typescript_config_probe))
198
+ base.completion.typescript_config_probe = comp.typescript_config_probe.filter((v) => typeof v === "string");
199
+ if (Array.isArray(comp.eslint_config_probe))
200
+ base.completion.eslint_config_probe = comp.eslint_config_probe.filter((v) => typeof v === "string");
201
+ }
202
+ if (parsed.runtime_image && typeof parsed.runtime_image === "object" && !Array.isArray(parsed.runtime_image)) {
203
+ const runtimeImage = parsed.runtime_image;
204
+ if (runtimeImage.deps && typeof runtimeImage.deps === "object" && !Array.isArray(runtimeImage.deps)) {
205
+ const deps = runtimeImage.deps;
206
+ if (typeof deps.monorepo_install === "boolean") {
207
+ base.runtime_image.deps.monorepo_install = deps.monorepo_install;
208
+ }
209
+ if (typeof deps.hp_next_install === "boolean") {
210
+ base.runtime_image.deps.hp_next_install = deps.hp_next_install;
211
+ }
212
+ }
213
+ if (typeof runtimeImage.plugins_require_binaries === "boolean") {
214
+ base.runtime_image.plugins_require_binaries = runtimeImage.plugins_require_binaries;
215
+ }
216
+ }
217
+ if (parsed.runtime_snapshot && typeof parsed.runtime_snapshot === "object" && !Array.isArray(parsed.runtime_snapshot)) {
218
+ const runtimeSnapshot = parsed.runtime_snapshot;
219
+ if (typeof runtimeSnapshot.enabled === "boolean") {
220
+ base.runtime_snapshot.enabled = runtimeSnapshot.enabled;
221
+ }
222
+ }
223
+ return base;
224
+ }
225
+ function isValidMode(value) {
226
+ return value === "off" || value === "observe" || value === "enforce";
227
+ }
228
+ function isValidRule(value) {
229
+ if (!value || typeof value !== "object" || Array.isArray(value))
230
+ return false;
231
+ const r = value;
232
+ return typeof r.id === "string" && typeof r.category === "string" && r.match != null && typeof r.match === "object";
233
+ }
234
+ function migrateLegacyDeny(deny) {
235
+ const rules = [];
236
+ for (const entry of deny) {
237
+ if (typeof entry.id !== "string")
238
+ continue;
239
+ const match = {};
240
+ if (typeof entry.pattern === "string")
241
+ match.pattern = entry.pattern;
242
+ if (typeof entry.regex === "string")
243
+ match.regex = entry.regex;
244
+ if (!match.pattern && !match.regex)
245
+ continue;
246
+ rules.push({
247
+ id: entry.id,
248
+ category: "command",
249
+ match,
250
+ action: "block",
251
+ ...typeof entry.description === "string" ? { description: entry.description } : {}
252
+ });
253
+ }
254
+ return rules;
255
+ }
256
+ function precompilePolicyRuleRegexes(rules) {
257
+ return rules.map((rule) => {
258
+ const compiledRegex = rule.match.regex ? compileSafeRegex(rule.match.regex, `rules.${rule.id}.match.regex`, true) : undefined;
259
+ const compiledUnlessRegex = rule.unless?.regex ? compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, true) : undefined;
260
+ return {
261
+ ...rule,
262
+ ...compiledRegex ? { compiledRegex } : {},
263
+ ...compiledUnlessRegex ? { compiledUnlessRegex } : {}
264
+ };
265
+ });
266
+ }
267
+ function getRegexUnsafeReason(pattern) {
268
+ if (pattern.length > 512) {
269
+ return "pattern exceeds max safe length (512 chars)";
270
+ }
271
+ if (/\\[1-9]/.test(pattern)) {
272
+ return "pattern uses backreferences";
273
+ }
274
+ if (/\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
275
+ return "pattern contains nested quantifiers";
276
+ }
277
+ if (/\((?:[^()\\]|\\.)*\.\\?[+*](?:[^()\\]|\\.)*\)\s*[*+{]/.test(pattern)) {
278
+ return "pattern contains nested broad quantifiers";
279
+ }
280
+ return null;
281
+ }
282
+ function compileSafeRegex(pattern, sourceLabel, logOnFailure) {
283
+ const cached = compiledRegexCache.get(pattern);
284
+ if (cached !== undefined) {
285
+ return cached ?? undefined;
286
+ }
287
+ const unsafeReason = getRegexUnsafeReason(pattern);
288
+ if (unsafeReason) {
289
+ if (logOnFailure) {
290
+ console.warn(`[policy] Skipping unsafe regex in ${sourceLabel}: ${unsafeReason}`);
291
+ }
292
+ compiledRegexCache.set(pattern, null);
293
+ return;
294
+ }
295
+ try {
296
+ const compiled = new RegExp(pattern);
297
+ compiledRegexCache.set(pattern, compiled);
298
+ return compiled;
299
+ } catch (error) {
300
+ if (logOnFailure) {
301
+ const message = error instanceof Error ? error.message : String(error);
302
+ console.warn(`[policy] Skipping invalid regex in ${sourceLabel}: ${message}`);
303
+ }
304
+ compiledRegexCache.set(pattern, null);
305
+ return;
306
+ }
307
+ }
308
+ function matchRule(rule, input) {
309
+ const { match } = rule;
310
+ if (match.pattern && input.includes(match.pattern)) {
311
+ return true;
312
+ }
313
+ if (match.regex) {
314
+ const compiled = rule.compiledRegex || compileSafeRegex(match.regex, `rules.${rule.id}.match.regex`, false);
315
+ if (!compiled) {
316
+ return false;
317
+ }
318
+ try {
319
+ return compiled.test(input);
320
+ } catch {
321
+ return false;
322
+ }
323
+ }
324
+ return false;
325
+ }
326
+ function matchRuleUnless(rule, command, taskId) {
327
+ if (!rule.unless)
328
+ return false;
329
+ if (rule.unless.regex) {
330
+ const compiled = rule.compiledUnlessRegex || compileSafeRegex(rule.unless.regex, `rules.${rule.id}.unless.regex`, false);
331
+ if (!compiled) {
332
+ return false;
333
+ }
334
+ try {
335
+ if (compiled.test(command))
336
+ return true;
337
+ } catch {}
338
+ }
339
+ if (rule.unless.task_in && taskId) {
340
+ if (rule.unless.task_in.includes(taskId))
341
+ return true;
342
+ }
343
+ return false;
344
+ }
345
+ function resolveAction(mode, matched) {
346
+ if (matched.length === 0)
347
+ return "allow";
348
+ if (mode === "off")
349
+ return "allow";
350
+ if (mode === "observe")
351
+ return "warn";
352
+ return "block";
353
+ }
354
+ function resolveAbsolutePath(projectRoot, rawPath) {
355
+ if (rawPath.startsWith("/"))
356
+ return resolve(rawPath);
357
+ return resolve(projectRoot, rawPath);
358
+ }
359
+ function isHarnessPath(projectRoot, rawPath) {
360
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
361
+ const managedRoots = [
362
+ resolve(projectRoot, "rig"),
363
+ resolve(projectRoot, ".rig"),
364
+ resolve(projectRoot, "artifacts")
365
+ ];
366
+ return managedRoots.some((root) => absPath === root || absPath.startsWith(root + "/"));
367
+ }
368
+ function isRuntimePath(projectRoot, rawPath, taskWorkspace) {
369
+ const absPath = resolveAbsolutePath(projectRoot, rawPath);
370
+ if (taskWorkspace) {
371
+ const workspaceRigRoot = resolve(taskWorkspace, ".rig");
372
+ const workspaceArtifactsRoot = resolve(taskWorkspace, "artifacts");
373
+ if (absPath === workspaceRigRoot || absPath.startsWith(workspaceRigRoot + "/") || absPath === workspaceArtifactsRoot || absPath.startsWith(workspaceArtifactsRoot + "/")) {
374
+ return true;
375
+ }
376
+ }
377
+ const runtimeRoot = resolve(projectRoot, ".rig/runtime/agents");
378
+ return absPath === runtimeRoot || absPath.startsWith(runtimeRoot + "/");
379
+ }
380
+ function isTestFile(path) {
381
+ return /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(path) || /\/(__tests__|tests|test)\//.test(path);
382
+ }
383
+ function evaluate(context) {
384
+ const policy = loadPolicy(context.projectRoot);
385
+ switch (context.evaluation.type) {
386
+ case "tool-call":
387
+ return evaluateToolCall(policy, context);
388
+ case "command":
389
+ return evaluateCommand(policy, context);
390
+ case "content-write":
391
+ return evaluateContent(policy, context);
392
+ case "file-access":
393
+ return evaluateScope(policy, context, context.evaluation.file_path, context.evaluation.access);
394
+ }
395
+ }
396
+ function evaluateScope(policy, context, filePath, access) {
397
+ const allowed = () => ({
398
+ allowed: true,
399
+ matchedRules: [],
400
+ action: "allow",
401
+ failClosed: false
402
+ });
403
+ if (policy.scope.harness_paths_exempt && isHarnessPath(context.projectRoot, filePath)) {
404
+ return allowed();
405
+ }
406
+ if (policy.scope.runtime_paths_exempt && isRuntimePath(context.projectRoot, filePath, context.taskWorkspace)) {
407
+ return allowed();
408
+ }
409
+ if (!context.taskId) {
410
+ if (access === "write" && policy.scope.fail_closed) {
411
+ return {
412
+ allowed: false,
413
+ matchedRules: [],
414
+ action: resolveAction(policy.mode, [{ id: "scope:no-task", category: "command", reason: "No active task; fail-closed for write operations" }]),
415
+ failClosed: true
416
+ };
417
+ }
418
+ return allowed();
419
+ }
420
+ const scopes = context.taskScopes || [];
421
+ if (scopes.length === 0) {
422
+ return allowed();
423
+ }
424
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith("/")) {
425
+ const absPath = resolve(filePath);
426
+ if (!absPath.startsWith(context.taskWorkspace + "/") && !isHarnessPath(context.projectRoot, filePath)) {
427
+ const reason2 = `Absolute path '${filePath}' is outside task runtime boundary. Allowed root: ${context.taskWorkspace}`;
428
+ const matched2 = [{ id: "scope:workspace-boundary", category: "command", reason: reason2 }];
429
+ return {
430
+ allowed: policy.mode !== "enforce",
431
+ matchedRules: matched2,
432
+ action: resolveAction(policy.mode, matched2),
433
+ failClosed: false
434
+ };
435
+ }
436
+ }
437
+ const monorepoRoot = context.monorepoRoot || process.env.MONOREPO_ROOT?.trim() || context.taskWorkspace || context.projectRoot;
438
+ let normalizedPath = filePath;
439
+ if (context.taskWorkspace && context.taskWorkspace !== context.projectRoot && filePath.startsWith(context.taskWorkspace + "/")) {
440
+ normalizedPath = filePath.slice(context.taskWorkspace.length + 1);
441
+ }
442
+ normalizedPath = normalizePathToScope(context.projectRoot, monorepoRoot, normalizedPath);
443
+ if (scopeMatches(filePath, scopes) || scopeMatches(normalizedPath, scopes)) {
444
+ return allowed();
445
+ }
446
+ const reason = `File '${filePath}' (normalized: '${normalizedPath}') is outside scope of task ${context.taskId}`;
447
+ const matched = [{ id: "scope:out-of-scope", category: "command", reason }];
448
+ return {
449
+ allowed: policy.mode !== "enforce",
450
+ matchedRules: matched,
451
+ action: resolveAction(policy.mode, matched),
452
+ failClosed: false
453
+ };
454
+ }
455
+ function evaluateCommand(policy, context) {
456
+ const evaluation = context.evaluation;
457
+ if (evaluation.type !== "command") {
458
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
459
+ }
460
+ const command = evaluation.command;
461
+ const matchedRules = [];
462
+ for (const rule of policy.rules) {
463
+ if (rule.category !== "command")
464
+ continue;
465
+ if (!matchRule(rule, command))
466
+ continue;
467
+ if (matchRuleUnless(rule, command, context.taskId))
468
+ continue;
469
+ matchedRules.push({
470
+ id: rule.id,
471
+ category: rule.category,
472
+ ...rule.description !== undefined ? { description: rule.description } : {},
473
+ reason: rule.description || `Matched rule ${rule.id}`
474
+ });
475
+ }
476
+ const writeTarget = extractWriteTarget(command);
477
+ if (writeTarget && !/^\/dev\//.test(writeTarget) && !/^\/proc\//.test(writeTarget)) {
478
+ const scopeResult = evaluateScope(policy, context, writeTarget, "write");
479
+ if (!scopeResult.allowed || scopeResult.matchedRules.length > 0) {
480
+ matchedRules.push(...scopeResult.matchedRules);
481
+ }
482
+ }
483
+ const action = resolveAction(policy.mode, matchedRules);
484
+ return {
485
+ allowed: action !== "block",
486
+ matchedRules,
487
+ action,
488
+ failClosed: false
489
+ };
490
+ }
491
+ function extractWriteTarget(command) {
492
+ const redirect = command.match(/>>?\s+([^\s;|&]+)/);
493
+ if (redirect?.[1])
494
+ return redirect[1];
495
+ const tee = command.match(/tee\s+(-a\s+)?([^\s;|&]+)/);
496
+ if (tee?.[2])
497
+ return tee[2];
498
+ return "";
499
+ }
500
+ function evaluateContent(policy, context) {
501
+ const evaluation = context.evaluation;
502
+ if (evaluation.type !== "content-write") {
503
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
504
+ }
505
+ const { content, file_path } = evaluation;
506
+ const matchedRules = [];
507
+ const scopeResult = evaluateScope(policy, context, file_path, "write");
508
+ if (scopeResult.matchedRules.length > 0) {
509
+ matchedRules.push(...scopeResult.matchedRules);
510
+ }
511
+ for (const rule of policy.rules) {
512
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
513
+ continue;
514
+ if (rule.applies_to === "test-files" && !isTestFile(file_path))
515
+ continue;
516
+ if (!matchRule(rule, content))
517
+ continue;
518
+ if (matchRuleUnless(rule, content, context.taskId))
519
+ continue;
520
+ matchedRules.push({
521
+ id: rule.id,
522
+ category: rule.category,
523
+ ...rule.description !== undefined ? { description: rule.description } : {},
524
+ reason: rule.description || `Matched rule ${rule.id}`
525
+ });
526
+ }
527
+ const action = resolveAction(policy.mode, matchedRules);
528
+ return {
529
+ allowed: action !== "block",
530
+ matchedRules,
531
+ action,
532
+ failClosed: false
533
+ };
534
+ }
535
+ function evaluateToolCall(policy, context) {
536
+ const evaluation = context.evaluation;
537
+ if (evaluation.type !== "tool-call") {
538
+ return { allowed: true, matchedRules: [], action: "allow", failClosed: false };
539
+ }
540
+ const { tool_name, tool_input } = evaluation;
541
+ const allMatched = [];
542
+ const filePaths = extractFilePathsFromToolInput(tool_name, tool_input);
543
+ for (const fp of filePaths) {
544
+ const access = isWriteTool(tool_name) ? "write" : "read";
545
+ const scopeResult = evaluateScope(policy, context, fp, access);
546
+ if (scopeResult.matchedRules.length > 0) {
547
+ allMatched.push(...scopeResult.matchedRules);
548
+ }
549
+ }
550
+ const content = extractContentFromToolInput(tool_input);
551
+ if (content) {
552
+ const filePath = filePaths[0] || "";
553
+ const contentContext = {
554
+ ...context,
555
+ evaluation: { type: "content-write", file_path: filePath, content }
556
+ };
557
+ const contentPolicy = loadPolicy(context.projectRoot);
558
+ for (const rule of contentPolicy.rules) {
559
+ if (rule.category !== "content" && rule.category !== "import" && rule.category !== "test-integrity")
560
+ continue;
561
+ if (rule.applies_to === "test-files" && !isTestFile(filePath))
562
+ continue;
563
+ if (!matchRule(rule, content))
564
+ continue;
565
+ if (matchRuleUnless(rule, content, context.taskId))
566
+ continue;
567
+ allMatched.push({
568
+ id: rule.id,
569
+ category: rule.category,
570
+ ...rule.description !== undefined ? { description: rule.description } : {},
571
+ reason: rule.description || `Matched rule ${rule.id}`
572
+ });
573
+ }
574
+ }
575
+ if (tool_name === "Bash") {
576
+ const command = String(tool_input.command || tool_input.cmd || "");
577
+ if (command) {
578
+ const cmdContext = {
579
+ ...context,
580
+ evaluation: { type: "command", command }
581
+ };
582
+ const cmdResult = evaluateCommand(policy, cmdContext);
583
+ if (cmdResult.matchedRules.length > 0) {
584
+ allMatched.push(...cmdResult.matchedRules);
585
+ }
586
+ }
587
+ }
588
+ const seen = new Set;
589
+ const deduplicated = [];
590
+ for (const rule of allMatched) {
591
+ if (!seen.has(rule.id)) {
592
+ seen.add(rule.id);
593
+ deduplicated.push(rule);
594
+ }
595
+ }
596
+ const action = resolveAction(policy.mode, deduplicated);
597
+ return {
598
+ allowed: action !== "block",
599
+ matchedRules: deduplicated,
600
+ action,
601
+ failClosed: false
602
+ };
603
+ }
604
+ function isWriteTool(toolName) {
605
+ return toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit";
606
+ }
607
+ function extractFilePathsFromToolInput(toolName, input) {
608
+ const paths = [];
609
+ const add = (value) => {
610
+ if (typeof value === "string" && value.trim()) {
611
+ paths.push(value.trim());
612
+ }
613
+ };
614
+ if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
615
+ add(input.file_path);
616
+ add(input.path);
617
+ } else if (toolName === "Glob") {
618
+ add(input.path);
619
+ } else if (toolName === "Grep") {
620
+ add(input.path);
621
+ } else {
622
+ add(input.file_path);
623
+ add(input.path);
624
+ }
625
+ return paths;
626
+ }
627
+ function extractContentFromToolInput(input) {
628
+ if (typeof input.content === "string")
629
+ return input.content;
630
+ if (typeof input.new_string === "string")
631
+ return input.new_string;
632
+ return "";
633
+ }
634
+ var guardHotPathPrimed = false;
635
+ function primeGuardHotPaths() {
636
+ if (guardHotPathPrimed) {
637
+ return;
638
+ }
639
+ guardHotPathPrimed = true;
640
+ try {
641
+ optimizeNextInvocation(matchRule);
642
+ optimizeNextInvocation(evaluate);
643
+ } catch {}
644
+ }
645
+ primeGuardHotPaths();
646
+
647
+ // packages/cli-surface-plugin/src/control-plane/agent-binary-build.ts
648
+ import { runtimeProvisioningEnv } from "@rig/core/runtime-provisioning-env";
649
+
650
+ // packages/cli-surface-plugin/src/runner.ts
8
651
  class CliError extends RuntimeCliError {
9
- hint;
10
652
  constructor(message, exitCode = 1, options = {}) {
11
- super(message, exitCode);
12
- if (options.hint?.trim()) {
13
- this.hint = options.hint.trim();
14
- }
653
+ super(message, exitCode, options);
15
654
  }
16
655
  }
17
656
  function takeFlag(args, flag) {
@@ -54,13 +693,11 @@ Usage: ${usage}`);
54
693
  }
55
694
 
56
695
  // packages/cli-surface-plugin/src/commands/agent.ts
57
- import {
58
- agentId,
59
- cleanupAgentRuntime,
60
- ensureAgentRuntime,
61
- listAgentRuntimes,
62
- runInAgentRuntime
63
- } from "@rig/runtime/control-plane/runtime/isolation";
696
+ import { agentId } from "@rig/core/safe-identifiers";
697
+ import { ISOLATION_BACKEND, REPO_OPERATIONS_CAPABILITY } from "@rig/contracts";
698
+ import { defineCapability } from "@rig/core/capability";
699
+ import { requireCapabilityForRoot } from "@rig/core/capability-loaders";
700
+ import { buildPluginHostContext } from "@rig/core/plugin-host-context";
64
701
 
65
702
  // packages/cli-surface-plugin/src/withMutedConsole.ts
66
703
  function isPromise(value) {
@@ -116,7 +753,19 @@ function parseIsolationMode(value, allowOff) {
116
753
  }
117
754
 
118
755
  // packages/cli-surface-plugin/src/commands/agent.ts
119
- import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/project-main-pre-run-sync";
756
+ var MISSING_ISOLATION_BACKEND_HINT = "Install @rig/isolation-plugin (it ships in the default bundle) so runtime provisioning can resolve a backend.";
757
+ var RepoOperationsCap = defineCapability(REPO_OPERATIONS_CAPABILITY);
758
+ async function resolveRepoOperations(projectRoot) {
759
+ const hostCtx = await buildPluginHostContext(projectRoot);
760
+ const repoOps = hostCtx ? await RepoOperationsCap.resolve(hostCtx.pluginHost) : null;
761
+ if (!repoOps) {
762
+ throw new CliError(`No REPO_OPERATIONS capability is registered for project root "${projectRoot}".`, 2, { hint: "Install @rig/repos-plugin (it ships in the default bundle) before running repo operations." });
763
+ }
764
+ return repoOps;
765
+ }
766
+ async function resolveIsolationBackend(projectRoot) {
767
+ return requireCapabilityForRoot(projectRoot, defineCapability(ISOLATION_BACKEND), `No ISOLATION_BACKEND capability is registered for project root "${projectRoot}". ` + MISSING_ISOLATION_BACKEND_HINT);
768
+ }
120
769
  function splitAtDoubleDash(args) {
121
770
  const separatorIndex = args.indexOf("--");
122
771
  if (separatorIndex === -1) {
@@ -147,7 +796,8 @@ async function runProjectMainSyncPreflight(context, options) {
147
796
  }
148
797
  return;
149
798
  }
150
- const result = await ensureProjectMainFreshBeforeRun({
799
+ const repoOps = await resolveRepoOperations(context.projectRoot);
800
+ const result = await repoOps.ensureProjectMainFreshBeforeRun({
151
801
  projectRoot: context.projectRoot,
152
802
  disabled: options.disabled,
153
803
  runBootstrap: async () => {
@@ -179,10 +829,11 @@ async function runProjectMainSyncPreflight(context, options) {
179
829
  }
180
830
  async function executeAgent(context, args) {
181
831
  const [command = "list", ...rest] = args;
832
+ const backend = await resolveIsolationBackend(context.projectRoot);
182
833
  switch (command) {
183
834
  case "list": {
184
835
  requireNoExtraArgs(rest, "rig agent list");
185
- const runtimes = await listAgentRuntimes(context.projectRoot);
836
+ const runtimes = await backend.listAgentRuntimes(context.projectRoot);
186
837
  if (context.outputMode === "text") {
187
838
  if (runtimes.length === 0) {
188
839
  console.log("No agent runtimes.");
@@ -209,7 +860,7 @@ async function executeAgent(context, args) {
209
860
  if (!taskId) {
210
861
  throw new CliError("Usage: rig agent prepare --task <id> [--id <id>] [--mode worktree]");
211
862
  }
212
- const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
863
+ const runtime = await withMutedConsole(context.outputMode === "json", () => backend.ensureAgentRuntime({
213
864
  projectRoot: context.projectRoot,
214
865
  id,
215
866
  taskId,
@@ -245,14 +896,14 @@ async function executeAgent(context, args) {
245
896
  }
246
897
  await runProjectMainSyncPreflight(context, { disabled: skipProjectSyncResult.value });
247
898
  try {
248
- const runtime = await withMutedConsole(context.outputMode === "json", () => ensureAgentRuntime({
899
+ const runtime = await withMutedConsole(context.outputMode === "json", () => backend.ensureAgentRuntime({
249
900
  projectRoot: context.projectRoot,
250
901
  id,
251
902
  taskId,
252
903
  mode
253
904
  }));
254
905
  await attachEventBusToProvisionedRuntime(context, runtime.workspaceDir);
255
- const result = await runInAgentRuntime({
906
+ const result = await backend.runInAgentRuntime({
256
907
  projectRoot: context.projectRoot,
257
908
  runtime,
258
909
  command: commandParts,
@@ -291,10 +942,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
291
942
  if (!allResult.value && !idResult.value) {
292
943
  throw new CliError("Provide --id <id> or --all.", 1, { hint: "Run `rig agent list` to find agent ids." });
293
944
  }
294
- const runtimes = await listAgentRuntimes(context.projectRoot);
945
+ const runtimes = await backend.listAgentRuntimes(context.projectRoot);
295
946
  const targets = allResult.value ? runtimes.map((runtime) => runtime.id) : [idResult.value];
296
947
  for (const id of targets) {
297
- await cleanupAgentRuntime({
948
+ await backend.cleanupAgentRuntime({
298
949
  projectRoot: context.projectRoot,
299
950
  id
300
951
  });