@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,4189 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // packages/runtime/src/control-plane/hooks/inject-context.ts
5
+ import { existsSync as existsSync20, readFileSync as readFileSync12 } from "fs";
6
+ import { resolve as resolve21 } from "path";
7
+ import { resolveProjectRoot } from "@rig/hook-kit";
8
+
9
+ // packages/runtime/src/control-plane/memory-sync/db.ts
10
+ import { Database } from "bun:sqlite";
11
+ import { mkdirSync } from "fs";
12
+ import { dirname } from "path";
13
+ var SCHEMA_STATEMENTS = [
14
+ `CREATE TABLE IF NOT EXISTS memory_events (
15
+ event_id TEXT PRIMARY KEY,
16
+ event_type TEXT NOT NULL,
17
+ canonical_key TEXT NOT NULL,
18
+ summary TEXT,
19
+ kind TEXT,
20
+ category TEXT,
21
+ confidence REAL,
22
+ source_run_id TEXT,
23
+ source_task_id TEXT,
24
+ branch TEXT,
25
+ source_canonical_key TEXT,
26
+ replacement_canonical_key TEXT,
27
+ retrieval_query TEXT,
28
+ retrieval_rank INTEGER,
29
+ feedback_outcome TEXT,
30
+ details_json TEXT,
31
+ created_at TEXT NOT NULL
32
+ )`,
33
+ `CREATE TABLE IF NOT EXISTS memory_items (
34
+ canonical_key TEXT PRIMARY KEY,
35
+ summary TEXT NOT NULL,
36
+ kind TEXT,
37
+ category TEXT,
38
+ status TEXT NOT NULL,
39
+ confidence REAL NOT NULL,
40
+ source_run_id TEXT,
41
+ source_task_id TEXT,
42
+ branch TEXT,
43
+ details_json TEXT,
44
+ created_at TEXT NOT NULL,
45
+ updated_at TEXT NOT NULL,
46
+ last_event_id TEXT NOT NULL,
47
+ superseded_by TEXT,
48
+ embedding TEXT
49
+ )`,
50
+ `CREATE TABLE IF NOT EXISTS memory_links (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ from_key TEXT NOT NULL,
53
+ to_key TEXT NOT NULL,
54
+ relation TEXT NOT NULL,
55
+ source_event_id TEXT NOT NULL,
56
+ created_at TEXT NOT NULL
57
+ )`,
58
+ `CREATE TABLE IF NOT EXISTS task_runs (
59
+ run_id TEXT PRIMARY KEY,
60
+ task_id TEXT,
61
+ branch TEXT,
62
+ first_event_id TEXT,
63
+ last_event_id TEXT,
64
+ created_at TEXT NOT NULL,
65
+ updated_at TEXT NOT NULL
66
+ )`,
67
+ `CREATE TABLE IF NOT EXISTS retrieval_feedback (
68
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
69
+ canonical_key TEXT NOT NULL,
70
+ outcome TEXT NOT NULL,
71
+ retrieval_rank INTEGER,
72
+ source_run_id TEXT,
73
+ source_task_id TEXT,
74
+ query_text TEXT,
75
+ source_event_id TEXT NOT NULL,
76
+ created_at TEXT NOT NULL
77
+ )`
78
+ ];
79
+ var MEMORY_EVENT_COLUMNS = [
80
+ ["summary", "TEXT"],
81
+ ["kind", "TEXT"],
82
+ ["category", "TEXT"],
83
+ ["confidence", "REAL"],
84
+ ["source_run_id", "TEXT"],
85
+ ["source_task_id", "TEXT"],
86
+ ["branch", "TEXT"],
87
+ ["source_canonical_key", "TEXT"],
88
+ ["replacement_canonical_key", "TEXT"],
89
+ ["retrieval_query", "TEXT"],
90
+ ["retrieval_rank", "INTEGER"],
91
+ ["feedback_outcome", "TEXT"],
92
+ ["details_json", "TEXT"]
93
+ ];
94
+ var MEMORY_ITEM_COLUMNS = [
95
+ ["kind", "TEXT"],
96
+ ["category", "TEXT"],
97
+ ["branch", "TEXT"],
98
+ ["details_json", "TEXT"],
99
+ ["superseded_by", "TEXT"],
100
+ ["embedding", "TEXT"]
101
+ ];
102
+ function parseJsonRecord(value) {
103
+ if (typeof value !== "string" || value.length === 0) {
104
+ return null;
105
+ }
106
+ return JSON.parse(value);
107
+ }
108
+ function parseEmbedding(value) {
109
+ if (typeof value !== "string" || value.length === 0) {
110
+ return null;
111
+ }
112
+ return JSON.parse(value);
113
+ }
114
+ function rowString(row, key) {
115
+ const value = row[key];
116
+ return value == null ? null : String(value);
117
+ }
118
+ function normalizeStatement(statement, args) {
119
+ if (typeof statement === "string") {
120
+ return { sql: statement, args };
121
+ }
122
+ return {
123
+ sql: statement.sql,
124
+ args: statement.args ?? args
125
+ };
126
+ }
127
+ function normalizeBindings(bindings) {
128
+ return (bindings ?? []).map((binding) => binding === undefined ? null : binding);
129
+ }
130
+ function statementVerb(sql) {
131
+ const match = sql.trim().match(/^[A-Za-z]+/);
132
+ return match ? match[0].toUpperCase() : "";
133
+ }
134
+ function isMutationStatement(sql) {
135
+ return new Set([
136
+ "ALTER",
137
+ "ATTACH",
138
+ "BEGIN",
139
+ "COMMIT",
140
+ "CREATE",
141
+ "DELETE",
142
+ "DETACH",
143
+ "DROP",
144
+ "END",
145
+ "INSERT",
146
+ "REINDEX",
147
+ "RELEASE",
148
+ "REPLACE",
149
+ "ROLLBACK",
150
+ "SAVEPOINT",
151
+ "UPDATE",
152
+ "VACUUM"
153
+ ]).has(statementVerb(sql));
154
+ }
155
+ function executeSqlite(sqlite, statement, args) {
156
+ const normalized = normalizeStatement(statement, args);
157
+ const bindings = normalizeBindings(normalized.args);
158
+ if (isMutationStatement(normalized.sql)) {
159
+ const result = bindings.length > 0 ? sqlite.run(normalized.sql, ...bindings) : sqlite.run(normalized.sql);
160
+ return {
161
+ rows: [],
162
+ rowsAffected: Number(result.changes)
163
+ };
164
+ }
165
+ const query = sqlite.query(normalized.sql);
166
+ const rows = bindings.length > 0 ? query.all(...bindings) : query.all();
167
+ return {
168
+ rows,
169
+ rowsAffected: 0
170
+ };
171
+ }
172
+ function createTransaction(sqlite, mode) {
173
+ sqlite.run(mode === "write" ? "BEGIN IMMEDIATE" : "BEGIN");
174
+ let active = true;
175
+ return {
176
+ async execute(statement, args) {
177
+ if (!active) {
178
+ throw new Error("memory transaction is closed");
179
+ }
180
+ return executeSqlite(sqlite, statement, args);
181
+ },
182
+ async commit() {
183
+ if (!active) {
184
+ return;
185
+ }
186
+ sqlite.run("COMMIT");
187
+ active = false;
188
+ },
189
+ async rollback() {
190
+ if (!active) {
191
+ return;
192
+ }
193
+ sqlite.run("ROLLBACK");
194
+ active = false;
195
+ },
196
+ close() {
197
+ if (!active) {
198
+ return;
199
+ }
200
+ try {
201
+ sqlite.run("ROLLBACK");
202
+ } catch {} finally {
203
+ active = false;
204
+ }
205
+ }
206
+ };
207
+ }
208
+ function createMemoryDbClient(sqlite) {
209
+ return {
210
+ async execute(statement, args) {
211
+ return executeSqlite(sqlite, statement, args);
212
+ },
213
+ async transaction(mode) {
214
+ return createTransaction(sqlite, mode);
215
+ },
216
+ close() {
217
+ sqlite.close();
218
+ }
219
+ };
220
+ }
221
+ async function listColumns(executor, tableName) {
222
+ const result = await executor.execute(`PRAGMA table_info(${tableName})`);
223
+ return new Set(result.rows.map((row) => String(row.name)));
224
+ }
225
+ async function ensureColumns(executor, tableName, columns) {
226
+ const existing = await listColumns(executor, tableName);
227
+ for (const [columnName, definition] of columns) {
228
+ if (!existing.has(columnName)) {
229
+ await executor.execute(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${definition}`);
230
+ }
231
+ }
232
+ }
233
+ async function ensureSchema(db) {
234
+ for (const statement of SCHEMA_STATEMENTS) {
235
+ await db.client.execute(statement);
236
+ }
237
+ await ensureColumns(db.client, "memory_events", MEMORY_EVENT_COLUMNS);
238
+ await ensureColumns(db.client, "memory_items", MEMORY_ITEM_COLUMNS);
239
+ }
240
+ function toMemoryItemRow(row) {
241
+ return {
242
+ canonicalKey: String(row.canonical_key),
243
+ summary: String(row.summary),
244
+ kind: rowString(row, "kind"),
245
+ category: rowString(row, "category"),
246
+ status: String(row.status),
247
+ confidence: Number(row.confidence),
248
+ sourceRunId: rowString(row, "source_run_id"),
249
+ sourceTaskId: rowString(row, "source_task_id"),
250
+ branch: rowString(row, "branch"),
251
+ details: parseJsonRecord(row.details_json),
252
+ createdAt: String(row.created_at),
253
+ updatedAt: String(row.updated_at),
254
+ lastEventId: String(row.last_event_id),
255
+ supersededBy: rowString(row, "superseded_by"),
256
+ embedding: parseEmbedding(row.embedding)
257
+ };
258
+ }
259
+ async function openMemoryDb(dbPath) {
260
+ mkdirSync(dirname(dbPath), { recursive: true });
261
+ const sqlite = new Database(dbPath, { create: true, strict: true });
262
+ const client = createMemoryDbClient(sqlite);
263
+ const db = {
264
+ path: dbPath,
265
+ client,
266
+ async close() {
267
+ client.close();
268
+ }
269
+ };
270
+ await ensureSchema(db);
271
+ return db;
272
+ }
273
+ async function listActiveMemoryItems(db) {
274
+ const result = await db.client.execute(`
275
+ SELECT
276
+ canonical_key,
277
+ summary,
278
+ kind,
279
+ category,
280
+ status,
281
+ confidence,
282
+ source_run_id,
283
+ source_task_id,
284
+ branch,
285
+ details_json,
286
+ created_at,
287
+ updated_at,
288
+ last_event_id,
289
+ superseded_by,
290
+ embedding
291
+ FROM memory_items
292
+ WHERE status = 'active'
293
+ ORDER BY canonical_key
294
+ `);
295
+ return result.rows.map((row) => toMemoryItemRow(row));
296
+ }
297
+
298
+ // packages/runtime/src/control-plane/memory-sync/embed.ts
299
+ import { createHash } from "crypto";
300
+ var DEFAULT_EMBEDDING_API_BASE_URL = "https://api.openai.com/v1";
301
+ var DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
302
+ var DETERMINISTIC_EMBEDDER_MODE = "deterministic";
303
+ var DETERMINISTIC_VECTOR_DIMS = 32;
304
+ function tokenizeForDeterministicEmbedding(text) {
305
+ const tokens = text.toLowerCase().match(/[a-z0-9_./:-]+/g) ?? [];
306
+ return tokens.length > 0 ? tokens : [text.toLowerCase()];
307
+ }
308
+ function deterministicTokenVector(token) {
309
+ const hash = createHash("sha256").update(token).digest();
310
+ const vector = new Array(DETERMINISTIC_VECTOR_DIMS).fill(0);
311
+ for (let index = 0;index < 8; index += 1) {
312
+ const slot = (hash[index] ?? 0) % DETERMINISTIC_VECTOR_DIMS;
313
+ const sign = ((hash[index + 8] ?? 0) & 1) === 0 ? 1 : -1;
314
+ const magnitude = ((hash[index + 16] ?? 0) + 1) / 256;
315
+ vector[slot] = (vector[slot] ?? 0) + sign * magnitude;
316
+ }
317
+ return vector;
318
+ }
319
+ function normalizeVector(vector) {
320
+ const norm = Math.sqrt(vector.reduce((sum, value) => sum + value * value, 0));
321
+ if (norm === 0) {
322
+ return vector.map(() => 0);
323
+ }
324
+ return vector.map((value) => Number((value / norm).toFixed(6)));
325
+ }
326
+ function createDeterministicMemoryEmbedder() {
327
+ return {
328
+ async embed(texts) {
329
+ return texts.map((text) => {
330
+ const vector = new Array(DETERMINISTIC_VECTOR_DIMS).fill(0);
331
+ for (const token of tokenizeForDeterministicEmbedding(text)) {
332
+ const tokenVector = deterministicTokenVector(token);
333
+ for (let index = 0;index < tokenVector.length; index += 1) {
334
+ vector[index] = (vector[index] ?? 0) + (tokenVector[index] ?? 0);
335
+ }
336
+ }
337
+ return normalizeVector(vector);
338
+ });
339
+ }
340
+ };
341
+ }
342
+ function embeddingResponseMessage(payload, status) {
343
+ if (typeof payload === "object" && payload !== null) {
344
+ const maybeMessage = payload.error?.message;
345
+ if (maybeMessage) {
346
+ return maybeMessage;
347
+ }
348
+ }
349
+ return `memory embedding request failed with status ${status}`;
350
+ }
351
+ function createOpenAiMemoryEmbedder(options) {
352
+ const apiKey = options.apiKey.trim();
353
+ const model = options.model?.trim() || DEFAULT_EMBEDDING_MODEL;
354
+ const apiBaseUrl = (options.apiBaseUrl?.trim() || DEFAULT_EMBEDDING_API_BASE_URL).replace(/\/+$/, "");
355
+ const fetchImpl = options.fetchImpl ?? fetch;
356
+ if (!apiKey) {
357
+ throw new Error("memory embedding api key must be non-empty");
358
+ }
359
+ return {
360
+ async embed(texts) {
361
+ if (texts.length === 0) {
362
+ return [];
363
+ }
364
+ const response = await fetchImpl(`${apiBaseUrl}/embeddings`, {
365
+ method: "POST",
366
+ headers: {
367
+ authorization: `Bearer ${apiKey}`,
368
+ "content-type": "application/json"
369
+ },
370
+ body: JSON.stringify({
371
+ input: texts,
372
+ model
373
+ })
374
+ });
375
+ const payload = await response.json();
376
+ if (!response.ok) {
377
+ throw new Error(embeddingResponseMessage(payload, response.status));
378
+ }
379
+ if (!Array.isArray(payload.data) || payload.data.length !== texts.length) {
380
+ throw new Error(`memory embedding provider returned ${payload.data?.length ?? 0} embeddings for ${texts.length} inputs`);
381
+ }
382
+ const byIndex = new Map;
383
+ for (const row of payload.data) {
384
+ if (typeof row.index !== "number" || !Array.isArray(row.embedding)) {
385
+ throw new Error("memory embedding provider returned an invalid response payload");
386
+ }
387
+ byIndex.set(row.index, row.embedding);
388
+ }
389
+ return texts.map((_text, index) => {
390
+ const embedding = byIndex.get(index);
391
+ if (!embedding) {
392
+ throw new Error(`memory embedding provider omitted embedding ${index}`);
393
+ }
394
+ return embedding;
395
+ });
396
+ }
397
+ };
398
+ }
399
+ function createConfiguredMemoryEmbedder(options = {}) {
400
+ const env = options.env ?? process.env;
401
+ const mode = env.RIG_MEMORY_EMBEDDER?.trim();
402
+ if (mode === DETERMINISTIC_EMBEDDER_MODE) {
403
+ return createDeterministicMemoryEmbedder();
404
+ }
405
+ const apiKey = env.OPENAI_API_KEY?.trim();
406
+ if (!apiKey) {
407
+ throw new Error("memory embeddings require OPENAI_API_KEY or RIG_MEMORY_EMBEDDER=deterministic");
408
+ }
409
+ return createOpenAiMemoryEmbedder({
410
+ apiKey,
411
+ model: env.RIG_MEMORY_EMBEDDING_MODEL?.trim() || DEFAULT_EMBEDDING_MODEL,
412
+ apiBaseUrl: options.apiBaseUrl ?? env.RIG_MEMORY_EMBEDDING_API_BASE_URL?.trim() ?? DEFAULT_EMBEDDING_API_BASE_URL,
413
+ fetchImpl: options.fetchImpl
414
+ });
415
+ }
416
+
417
+ // packages/runtime/src/control-plane/runtime/context.ts
418
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
419
+ import { dirname as dirname2, resolve } from "path";
420
+ var RUNTIME_CONTEXT_ENV = "RIG_RUNTIME_CONTEXT_FILE";
421
+ var DEFAULT_RUNTIME_MEMORY_RETRIEVAL = {
422
+ topK: 5,
423
+ lexicalWeight: 0.35,
424
+ vectorWeight: 0.45,
425
+ recencyWeight: 0.1,
426
+ confidenceWeight: 0.1
427
+ };
428
+ var runtimeContextStringFields = [
429
+ "runtimeId",
430
+ "taskId",
431
+ "role",
432
+ "workspaceDir",
433
+ "stateDir",
434
+ "logsDir",
435
+ "sessionDir",
436
+ "sessionFile",
437
+ "policyFile",
438
+ "binDir",
439
+ "createdAt"
440
+ ];
441
+ var runtimeContextArrayFields = ["scopes", "validation"];
442
+ var runtimeContextOptionalStringFields = [
443
+ "artifactRoot",
444
+ "hostProjectRoot",
445
+ "monorepoMainRoot",
446
+ "monorepoBaseRef",
447
+ "monorepoBaseCommit"
448
+ ];
449
+ function loadRuntimeContext(path) {
450
+ const absPath = resolve(path);
451
+ if (!existsSync(absPath)) {
452
+ throw new Error(`RuntimeTaskContext file not found: ${absPath}`);
453
+ }
454
+ let raw;
455
+ try {
456
+ raw = JSON.parse(readFileSync(absPath, "utf8"));
457
+ } catch (err) {
458
+ throw new Error(`Failed to parse RuntimeTaskContext at ${absPath}: ${String(err)}`);
459
+ }
460
+ if (typeof raw !== "object" || raw === null) {
461
+ throw new Error(`RuntimeTaskContext at ${absPath} is not an object`);
462
+ }
463
+ const obj = raw;
464
+ for (const field of runtimeContextStringFields) {
465
+ if (typeof obj[field] !== "string" || obj[field].length === 0) {
466
+ throw new Error(`RuntimeTaskContext field "${field}" must be a non-empty string (at ${absPath})`);
467
+ }
468
+ }
469
+ for (const field of runtimeContextArrayFields) {
470
+ if (!Array.isArray(obj[field])) {
471
+ throw new Error(`RuntimeTaskContext field "${field}" must be an array (at ${absPath})`);
472
+ }
473
+ if (!obj[field].every((entry) => typeof entry === "string")) {
474
+ throw new Error(`RuntimeTaskContext field "${field}" must be a string[] (at ${absPath})`);
475
+ }
476
+ }
477
+ for (const field of runtimeContextOptionalStringFields) {
478
+ if (field in obj && obj[field] !== undefined && typeof obj[field] !== "string") {
479
+ throw new Error(`RuntimeTaskContext field "${field}" must be a string when present (at ${absPath})`);
480
+ }
481
+ }
482
+ if (obj.browser !== undefined) {
483
+ if (typeof obj.browser !== "object" || obj.browser === null || Array.isArray(obj.browser)) {
484
+ throw new Error(`RuntimeTaskContext field "browser" must be an object when present (at ${absPath})`);
485
+ }
486
+ const browser = obj.browser;
487
+ for (const field of [
488
+ "preset",
489
+ "mode",
490
+ "stateDir",
491
+ "defaultProfile",
492
+ "effectiveProfile",
493
+ "defaultAttachUrl",
494
+ "effectiveAttachUrl",
495
+ "launchHelper",
496
+ "checkHelper",
497
+ "attachInfoHelper",
498
+ "e2eHelper",
499
+ "resetProfileHelper"
500
+ ]) {
501
+ if (typeof browser[field] !== "string" || browser[field].length === 0) {
502
+ throw new Error(`RuntimeTaskContext field "browser.${field}" must be a non-empty string (at ${absPath})`);
503
+ }
504
+ }
505
+ for (const field of ["devCommand", "launchCommand", "checkCommand", "e2eCommand"]) {
506
+ if (browser[field] !== undefined && typeof browser[field] !== "string") {
507
+ throw new Error(`RuntimeTaskContext field "browser.${field}" must be a string when present (at ${absPath})`);
508
+ }
509
+ }
510
+ if (typeof browser.required !== "boolean") {
511
+ throw new Error(`RuntimeTaskContext field "browser.required" must be a boolean (at ${absPath})`);
512
+ }
513
+ }
514
+ if (obj.memory !== undefined) {
515
+ if (typeof obj.memory !== "object" || obj.memory === null || Array.isArray(obj.memory)) {
516
+ throw new Error(`RuntimeTaskContext field "memory" must be an object when present (at ${absPath})`);
517
+ }
518
+ const memory = obj.memory;
519
+ for (const field of ["canonicalPath", "canonicalRef", "canonicalBaseOid", "hydratedPath"]) {
520
+ if (typeof memory[field] !== "string" || memory[field].length === 0) {
521
+ throw new Error(`RuntimeTaskContext field "memory.${field}" must be a non-empty string (at ${absPath})`);
522
+ }
523
+ }
524
+ if (typeof memory.createdFresh !== "boolean") {
525
+ throw new Error(`RuntimeTaskContext field "memory.createdFresh" must be a boolean (at ${absPath})`);
526
+ }
527
+ if (typeof memory.retrieval !== "object" || memory.retrieval === null || Array.isArray(memory.retrieval)) {
528
+ throw new Error(`RuntimeTaskContext field "memory.retrieval" must be an object (at ${absPath})`);
529
+ }
530
+ const retrieval = memory.retrieval;
531
+ for (const field of ["topK", "lexicalWeight", "vectorWeight", "recencyWeight", "confidenceWeight"]) {
532
+ if (typeof retrieval[field] !== "number" || Number.isNaN(retrieval[field])) {
533
+ throw new Error(`RuntimeTaskContext field "memory.retrieval.${field}" must be a number (at ${absPath})`);
534
+ }
535
+ }
536
+ }
537
+ if (obj.initialDirtyFiles !== undefined) {
538
+ if (typeof obj.initialDirtyFiles !== "object" || obj.initialDirtyFiles === null || Array.isArray(obj.initialDirtyFiles)) {
539
+ throw new Error(`RuntimeTaskContext field "initialDirtyFiles" must be an object when present (at ${absPath})`);
540
+ }
541
+ const dirtyFiles = obj.initialDirtyFiles;
542
+ for (const key of ["project", "monorepo"]) {
543
+ if (dirtyFiles[key] === undefined) {
544
+ continue;
545
+ }
546
+ if (!Array.isArray(dirtyFiles[key]) || !dirtyFiles[key].every((entry) => typeof entry === "string")) {
547
+ throw new Error(`RuntimeTaskContext field "initialDirtyFiles.${key}" must be a string[] when present (at ${absPath})`);
548
+ }
549
+ }
550
+ }
551
+ if (obj.initialHeadCommits !== undefined) {
552
+ if (typeof obj.initialHeadCommits !== "object" || obj.initialHeadCommits === null || Array.isArray(obj.initialHeadCommits)) {
553
+ throw new Error(`RuntimeTaskContext field "initialHeadCommits" must be an object when present (at ${absPath})`);
554
+ }
555
+ const headCommits = obj.initialHeadCommits;
556
+ for (const key of ["project", "monorepo"]) {
557
+ if (headCommits[key] === undefined) {
558
+ continue;
559
+ }
560
+ if (typeof headCommits[key] !== "string") {
561
+ throw new Error(`RuntimeTaskContext field "initialHeadCommits.${key}" must be a string when present (at ${absPath})`);
562
+ }
563
+ }
564
+ }
565
+ return obj;
566
+ }
567
+ function loadRuntimeContextFromEnv(env = process.env) {
568
+ const contextFile = env[RUNTIME_CONTEXT_ENV];
569
+ if (contextFile) {
570
+ return loadRuntimeContext(contextFile);
571
+ }
572
+ const inferred = findRuntimeContextFile(process.cwd());
573
+ if (!inferred) {
574
+ return null;
575
+ }
576
+ return loadRuntimeContext(inferred);
577
+ }
578
+ function findRuntimeContextFile(startPath) {
579
+ let current = resolve(startPath);
580
+ while (true) {
581
+ const candidate = resolve(current, "runtime-context.json");
582
+ if (existsSync(candidate) && isAgentRuntimeContextPath(candidate)) {
583
+ return candidate;
584
+ }
585
+ const parent = dirname2(current);
586
+ if (parent === current) {
587
+ return "";
588
+ }
589
+ current = parent;
590
+ }
591
+ }
592
+ function isAgentRuntimeContextPath(path) {
593
+ const normalized = path.replace(/\\/g, "/");
594
+ return /\/\.rig\/runtime-context\.json$/.test(normalized);
595
+ }
596
+
597
+ // packages/runtime/src/control-plane/memory-sync/query.ts
598
+ var DEFAULT_RESULT_LIMIT = DEFAULT_RUNTIME_MEMORY_RETRIEVAL.topK;
599
+ var DAY_MS = 24 * 60 * 60 * 1000;
600
+ var MIN_VECTOR_MATCH_SCORE = 0.2;
601
+ function tokenize(text) {
602
+ return (text.toLowerCase().match(/[a-z0-9_./:-]+/g) ?? []).flatMap((token) => {
603
+ const split = token.split(/[./:_-]+/).filter(Boolean);
604
+ return split.length > 0 ? [token, ...split] : [token];
605
+ });
606
+ }
607
+ function lexicalScore(query, item) {
608
+ const queryTokens = [...new Set(tokenize(query))];
609
+ if (queryTokens.length === 0) {
610
+ return 0;
611
+ }
612
+ const haystackTokens = new Set(tokenize(item.summary));
613
+ let matched = 0;
614
+ for (const token of queryTokens) {
615
+ if (haystackTokens.has(token)) {
616
+ matched += 1;
617
+ }
618
+ }
619
+ return matched / queryTokens.length;
620
+ }
621
+ function cosineSimilarity(left, right) {
622
+ if (left.length === 0 || left.length !== right.length) {
623
+ return 0;
624
+ }
625
+ let dot = 0;
626
+ let leftNorm = 0;
627
+ let rightNorm = 0;
628
+ for (let index = 0;index < left.length; index += 1) {
629
+ const l = left[index] ?? 0;
630
+ const r = right[index] ?? 0;
631
+ dot += l * r;
632
+ leftNorm += l * l;
633
+ rightNorm += r * r;
634
+ }
635
+ if (leftNorm === 0 || rightNorm === 0) {
636
+ return 0;
637
+ }
638
+ return dot / Math.sqrt(leftNorm * rightNorm);
639
+ }
640
+ function recencyScore(item, now) {
641
+ const updatedAt = Date.parse(item.updatedAt);
642
+ if (Number.isNaN(updatedAt)) {
643
+ return 0;
644
+ }
645
+ const ageMs = Math.max(0, now.getTime() - updatedAt);
646
+ return 1 / (1 + ageMs / DAY_MS);
647
+ }
648
+ async function maybeEmbedQuery(items, query, embedder) {
649
+ if (!items.some((item) => Array.isArray(item.embedding) && item.embedding.length > 0)) {
650
+ return null;
651
+ }
652
+ try {
653
+ const resolvedEmbedder = embedder ?? createConfiguredMemoryEmbedder();
654
+ const [queryEmbedding] = await resolvedEmbedder.embed([query]);
655
+ return queryEmbedding ?? null;
656
+ } catch {
657
+ return null;
658
+ }
659
+ }
660
+ async function queryRelevantMemory(db, input) {
661
+ const retrieval = input.retrieval ?? DEFAULT_RUNTIME_MEMORY_RETRIEVAL;
662
+ const now = input.now instanceof Date ? input.now : new Date(input.now ?? Date.now());
663
+ const items = await listActiveMemoryItems(db);
664
+ if (items.length === 0) {
665
+ return [];
666
+ }
667
+ const queryEmbedding = await maybeEmbedQuery(items, input.query, input.embedder);
668
+ const scored = items.map((item) => {
669
+ const lexical = lexicalScore(input.query, item);
670
+ const vector = queryEmbedding && item.embedding ? cosineSimilarity(queryEmbedding, item.embedding) : 0;
671
+ const recency = recencyScore(item, now);
672
+ const score = retrieval.lexicalWeight * lexical + retrieval.vectorWeight * vector + retrieval.recencyWeight * recency + retrieval.confidenceWeight * item.confidence;
673
+ return {
674
+ canonicalKey: item.canonicalKey,
675
+ summary: item.summary,
676
+ kind: item.kind,
677
+ category: item.category,
678
+ confidence: item.confidence,
679
+ updatedAt: item.updatedAt,
680
+ score,
681
+ lexicalScore: lexical,
682
+ vectorScore: vector,
683
+ recencyScore: recency
684
+ };
685
+ }).filter((item) => item.lexicalScore > 0 || item.vectorScore >= MIN_VECTOR_MATCH_SCORE).sort((left, right) => right.score - left.score || Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || right.confidence - left.confidence || left.canonicalKey.localeCompare(right.canonicalKey));
686
+ return scored.slice(0, input.limit ?? retrieval.topK ?? DEFAULT_RESULT_LIMIT);
687
+ }
688
+ function formatMemoryQueryResults(results) {
689
+ if (results.length === 0) {
690
+ return "No shared memories matched.";
691
+ }
692
+ return results.map((result, index) => `${index + 1}. [${result.canonicalKey}] ${result.summary} (confidence ${result.confidence.toFixed(2)})`).join(`
693
+ `);
694
+ }
695
+
696
+ // packages/runtime/src/control-plane/native/profile-ops.ts
697
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
698
+
699
+ // packages/runtime/src/control-plane/native/utils.ts
700
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
701
+ import { resolve as resolve4 } from "path";
702
+
703
+ // packages/runtime/src/layout.ts
704
+ import { existsSync as existsSync2 } from "fs";
705
+ import { basename, dirname as dirname3, resolve as resolve2 } from "path";
706
+ var RIG_DEFINITION_DIRNAME = "rig";
707
+ var RIG_ARTIFACTS_DIRNAME = "artifacts";
708
+ function resolveMonorepoRoot(projectRoot) {
709
+ const normalizedProjectRoot = resolve2(projectRoot);
710
+ const explicit = process.env.MONOREPO_ROOT?.trim();
711
+ if (explicit) {
712
+ const explicitRoot = resolve2(explicit);
713
+ const explicitParent = dirname3(explicitRoot);
714
+ if (basename(explicitParent) === ".worktrees") {
715
+ const owner = dirname3(explicitParent);
716
+ const ownerHasGit = existsSync2(resolve2(owner, ".git"));
717
+ const ownerHasTaskConfig = existsSync2(resolve2(owner, ".rig", "task-config.json"));
718
+ const ownerHasRigConfig = existsSync2(resolve2(owner, "rig.config.ts"));
719
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
720
+ return owner;
721
+ }
722
+ throw new Error(`MONOREPO_ROOT points to worktree ${explicitRoot}, but the owner checkout is incomplete at ${owner}.`);
723
+ }
724
+ if (!existsSync2(resolve2(explicitRoot, ".git"))) {
725
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but no git checkout was found there.`);
726
+ }
727
+ const hasTaskConfig = existsSync2(resolve2(explicitRoot, ".rig", "task-config.json"));
728
+ const hasRigConfig = existsSync2(resolve2(explicitRoot, "rig.config.ts"));
729
+ if (!hasTaskConfig && !hasRigConfig) {
730
+ throw new Error(`MONOREPO_ROOT points to ${explicitRoot}, but neither .rig/task-config.json nor rig.config.ts exists there.`);
731
+ }
732
+ return explicitRoot;
733
+ }
734
+ const projectParent = dirname3(normalizedProjectRoot);
735
+ if (basename(projectParent) === ".worktrees") {
736
+ const worktreeOwner = dirname3(projectParent);
737
+ const ownerHasGit = existsSync2(resolve2(worktreeOwner, ".git"));
738
+ const ownerHasTaskConfig = existsSync2(resolve2(worktreeOwner, ".rig", "task-config.json"));
739
+ const ownerHasRigConfig = existsSync2(resolve2(worktreeOwner, "rig.config.ts"));
740
+ if (ownerHasGit && (ownerHasTaskConfig || ownerHasRigConfig)) {
741
+ return worktreeOwner;
742
+ }
743
+ }
744
+ return normalizedProjectRoot;
745
+ }
746
+ function resolveRuntimeWorkspaceLayout(workspaceDir) {
747
+ const root = resolve2(workspaceDir);
748
+ const rigRoot = resolve2(root, ".rig");
749
+ const logsDir = resolve2(rigRoot, "logs");
750
+ const stateDir = resolve2(rigRoot, "state");
751
+ const runtimeDir = resolve2(rigRoot, "runtime");
752
+ const binDir = resolve2(rigRoot, "bin");
753
+ return {
754
+ workspaceDir: root,
755
+ rigRoot,
756
+ stateDir,
757
+ logsDir,
758
+ artifactsRoot: resolve2(root, RIG_ARTIFACTS_DIRNAME),
759
+ runtimeDir,
760
+ homeDir: resolve2(rigRoot, "home"),
761
+ tmpDir: resolve2(rigRoot, "tmp"),
762
+ cacheDir: resolve2(rigRoot, "cache"),
763
+ sessionDir: resolve2(rigRoot, "session"),
764
+ binDir,
765
+ distDir: resolve2(rigRoot, "dist"),
766
+ pluginBinDir: resolve2(binDir, "plugins"),
767
+ contextPath: resolve2(rigRoot, "runtime-context.json"),
768
+ controlPlaneEventsFile: resolve2(logsDir, "control-plane.events.jsonl")
769
+ };
770
+ }
771
+ function resolveActiveRuntimeWorkspaceRoot(monorepoRoot) {
772
+ const explicit = process.env.RIG_TASK_WORKSPACE?.trim();
773
+ if (!explicit) {
774
+ throw new Error("No active runtime workspace. Set RIG_TASK_WORKSPACE or provision a task runtime first.");
775
+ }
776
+ return resolve2(explicit);
777
+ }
778
+ function resolveRigLayout(projectRoot) {
779
+ const monorepoRoot = resolveMonorepoRoot(projectRoot);
780
+ const definitionRoot = resolve2(projectRoot, RIG_DEFINITION_DIRNAME);
781
+ const runtimeWorkspaceRoot = resolveActiveRuntimeWorkspaceRoot(monorepoRoot);
782
+ const runtimeLayout = resolveRuntimeWorkspaceLayout(runtimeWorkspaceRoot);
783
+ const policyDir = resolve2(definitionRoot, "policy");
784
+ return {
785
+ projectRoot,
786
+ monorepoRoot,
787
+ definitionRoot,
788
+ runtimeWorkspaceRoot,
789
+ stateRoot: runtimeLayout.rigRoot,
790
+ artifactsRoot: runtimeLayout.artifactsRoot,
791
+ configPath: resolve2(definitionRoot, "config.sh"),
792
+ taskConfigPath: resolve2(runtimeWorkspaceRoot, ".rig", "task-config.json"),
793
+ policyDir,
794
+ policyFile: resolve2(policyDir, "policy.json"),
795
+ pluginsDir: resolve2(definitionRoot, "plugins"),
796
+ hooksDir: resolve2(definitionRoot, "hooks"),
797
+ toolsDir: resolve2(definitionRoot, "tools"),
798
+ templatesDir: resolve2(definitionRoot, "templates"),
799
+ validationDir: resolve2(definitionRoot, "validation"),
800
+ stateDir: runtimeLayout.stateDir,
801
+ logsDir: runtimeLayout.logsDir,
802
+ notificationsDir: resolve2(definitionRoot, "notifications"),
803
+ runtimeDir: runtimeLayout.runtimeDir,
804
+ distDir: runtimeLayout.distDir,
805
+ binDir: runtimeLayout.binDir,
806
+ pluginBinDir: runtimeLayout.pluginBinDir,
807
+ keybindingsPath: resolve2(definitionRoot, "keybindings.json"),
808
+ controlPlaneEventsFile: runtimeLayout.controlPlaneEventsFile
809
+ };
810
+ }
811
+
812
+ // packages/runtime/src/control-plane/native/runtime-native.ts
813
+ import { dlopen, ptr, suffix, toBuffer } from "bun:ffi";
814
+ import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync, rmSync, statSync } from "fs";
815
+ import { tmpdir } from "os";
816
+ import { dirname as dirname4, resolve as resolve3 } from "path";
817
+ var sharedNativeRuntimeOutputDir = resolve3(tmpdir(), "rig-native");
818
+ var sharedNativeRuntimeOutputPath = resolve3(sharedNativeRuntimeOutputDir, `runtime-native-${process.platform}-${process.arch}.${suffix}`);
819
+ var colocatedNativeRuntimeFileName = `runtime-native.${suffix}`;
820
+ var nativeRuntimeLibrary = await loadNativeRuntimeLibrary();
821
+ async function ensureNativeRuntimeLibraryPath(outputPath = sharedNativeRuntimeOutputPath, options = {}) {
822
+ if (await buildNativeRuntimeLibrary(outputPath, options)) {
823
+ return outputPath;
824
+ }
825
+ return !options.force && existsSync3(outputPath) ? outputPath : null;
826
+ }
827
+ async function loadNativeRuntimeLibrary() {
828
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
829
+ return null;
830
+ }
831
+ for (const candidate of nativeRuntimeLibraryCandidates()) {
832
+ if (!candidate || !existsSync3(candidate)) {
833
+ continue;
834
+ }
835
+ const loaded = tryDlopenNativeRuntimeLibrary(candidate);
836
+ if (loaded) {
837
+ return loaded;
838
+ }
839
+ }
840
+ const builtLibraryPath = await ensureNativeRuntimeLibraryPath(sharedNativeRuntimeOutputPath, { force: true });
841
+ if (!builtLibraryPath) {
842
+ return null;
843
+ }
844
+ return tryDlopenNativeRuntimeLibrary(builtLibraryPath);
845
+ }
846
+ function nativePackageLibraryCandidates(fromDir, names) {
847
+ const candidates = [];
848
+ let cursor = resolve3(fromDir);
849
+ for (let index = 0;index < 8; index += 1) {
850
+ for (const name of names) {
851
+ candidates.push(resolve3(cursor, "native", `${process.platform}-${process.arch}`, name), resolve3(cursor, "native", `${process.platform}-${process.arch}`, "lib", name), resolve3(cursor, "native", name), resolve3(cursor, "native", "lib", name));
852
+ }
853
+ const parent = dirname4(cursor);
854
+ if (parent === cursor)
855
+ break;
856
+ cursor = parent;
857
+ }
858
+ return candidates;
859
+ }
860
+ function nativeRuntimeLibraryCandidates() {
861
+ const explicit = process.env.RIG_NATIVE_RUNTIME_LIB?.trim() || "";
862
+ const execDir = process.execPath?.trim() ? dirname4(process.execPath.trim()) : "";
863
+ const platformSpecific = `runtime-native-${process.platform}-${process.arch}.${suffix}`;
864
+ return [...new Set([
865
+ explicit,
866
+ ...nativePackageLibraryCandidates(import.meta.dir, [colocatedNativeRuntimeFileName, platformSpecific]),
867
+ execDir ? resolve3(execDir, colocatedNativeRuntimeFileName) : "",
868
+ execDir ? resolve3(execDir, platformSpecific) : "",
869
+ execDir ? resolve3(execDir, "..", colocatedNativeRuntimeFileName) : "",
870
+ execDir ? resolve3(execDir, "..", platformSpecific) : "",
871
+ execDir ? resolve3(execDir, "lib", colocatedNativeRuntimeFileName) : "",
872
+ execDir ? resolve3(execDir, "..", "lib", colocatedNativeRuntimeFileName) : "",
873
+ sharedNativeRuntimeOutputPath
874
+ ].filter(Boolean))];
875
+ }
876
+ function resolveNativeRuntimeSourcePath() {
877
+ const explicit = process.env.RIG_NATIVE_RUNTIME_SOURCE?.trim();
878
+ if (explicit && existsSync3(explicit)) {
879
+ return explicit;
880
+ }
881
+ const bundled = resolve3(import.meta.dir, "../../../native/snapshot.zig");
882
+ return existsSync3(bundled) ? bundled : null;
883
+ }
884
+ async function buildNativeRuntimeLibrary(outputPath, options = {}) {
885
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
886
+ return false;
887
+ }
888
+ const zigBinary = Bun.which("zig");
889
+ const sourcePath = resolveNativeRuntimeSourcePath();
890
+ if (!zigBinary || !sourcePath) {
891
+ return false;
892
+ }
893
+ const tempOutputPath = `${outputPath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
894
+ try {
895
+ mkdirSync3(dirname4(outputPath), { recursive: true });
896
+ const needsBuild = options.force === true || !existsSync3(outputPath) || statSync(sourcePath).mtimeMs > statSync(outputPath).mtimeMs;
897
+ if (!needsBuild) {
898
+ return true;
899
+ }
900
+ const build = Bun.spawn([
901
+ zigBinary,
902
+ "build-lib",
903
+ sourcePath,
904
+ "-dynamic",
905
+ "-O",
906
+ "ReleaseFast",
907
+ `-femit-bin=${tempOutputPath}`
908
+ ], {
909
+ cwd: import.meta.dir,
910
+ stdout: "pipe",
911
+ stderr: "pipe"
912
+ });
913
+ const exitCode = await build.exited;
914
+ if (exitCode !== 0 || !existsSync3(tempOutputPath)) {
915
+ rmSync(tempOutputPath, { force: true });
916
+ return false;
917
+ }
918
+ renameSync(tempOutputPath, outputPath);
919
+ return true;
920
+ } catch {
921
+ rmSync(tempOutputPath, { force: true });
922
+ return false;
923
+ }
924
+ }
925
+ function tryDlopenNativeRuntimeLibrary(outputPath) {
926
+ try {
927
+ return dlopen(outputPath, {
928
+ rig_scope_match: {
929
+ args: ["ptr", "ptr"],
930
+ returns: "u8"
931
+ },
932
+ snapshot_capture: {
933
+ args: ["ptr", "u64", "ptr", "u64"],
934
+ returns: "ptr"
935
+ },
936
+ snapshot_delta: {
937
+ args: ["ptr", "ptr"],
938
+ returns: "ptr"
939
+ },
940
+ snapshot_store_delta: {
941
+ args: ["ptr", "ptr", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
942
+ returns: "ptr"
943
+ },
944
+ snapshot_inspect_delta: {
945
+ args: ["ptr", "u64"],
946
+ returns: "ptr"
947
+ },
948
+ snapshot_apply_delta: {
949
+ args: ["ptr", "u64", "ptr", "u64"],
950
+ returns: "ptr"
951
+ },
952
+ snapshot_release: {
953
+ args: ["ptr"],
954
+ returns: "void"
955
+ },
956
+ runtime_hash_file: {
957
+ args: ["ptr", "u64"],
958
+ returns: "ptr"
959
+ },
960
+ runtime_hash_tree: {
961
+ args: ["ptr", "u64"],
962
+ returns: "ptr"
963
+ },
964
+ runtime_prepare_paths: {
965
+ args: ["ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64", "ptr", "u64"],
966
+ returns: "ptr"
967
+ },
968
+ runtime_link_dependency_layer: {
969
+ args: ["ptr", "u64", "ptr", "u64"],
970
+ returns: "ptr"
971
+ },
972
+ runtime_scan_worktrees: {
973
+ args: ["ptr", "u64"],
974
+ returns: "ptr"
975
+ }
976
+ });
977
+ } catch {
978
+ return null;
979
+ }
980
+ }
981
+
982
+ // packages/runtime/src/control-plane/native/scope-rules.ts
983
+ var activeRules = null;
984
+ function setScopeRules(rules) {
985
+ activeRules = rules ?? null;
986
+ }
987
+
988
+ // packages/runtime/src/control-plane/native/utils.ts
989
+ function resolveMonorepoRoot2(projectRoot) {
990
+ return resolveMonorepoRoot(projectRoot);
991
+ }
992
+ var scopeRegexCache = new Map;
993
+ function runCapture(command, cwd, env) {
994
+ const result = Bun.spawnSync(command, {
995
+ cwd,
996
+ env: env ? { ...process.env, ...env } : process.env,
997
+ stdout: "pipe",
998
+ stderr: "pipe"
999
+ });
1000
+ return {
1001
+ exitCode: result.exitCode,
1002
+ stdout: result.stdout.toString(),
1003
+ stderr: result.stderr.toString()
1004
+ };
1005
+ }
1006
+ function readJsonFile(path, fallback) {
1007
+ if (!existsSync4(path)) {
1008
+ return fallback;
1009
+ }
1010
+ try {
1011
+ return JSON.parse(readFileSync2(path, "utf-8"));
1012
+ } catch {
1013
+ return fallback;
1014
+ }
1015
+ }
1016
+ function nowIso() {
1017
+ return new Date().toISOString();
1018
+ }
1019
+ function unique(values) {
1020
+ return [...new Set(values)];
1021
+ }
1022
+ function resolveHarnessPaths(projectRoot) {
1023
+ const hasRuntimeWorkspace = Boolean(process.env.RIG_TASK_WORKSPACE?.trim());
1024
+ const monorepoRoot = resolveMonorepoRoot2(projectRoot);
1025
+ const harnessRoot = resolve4(projectRoot, "rig");
1026
+ const stateRoot = resolve4(projectRoot, ".rig");
1027
+ const layout = hasRuntimeWorkspace ? resolveRigLayout(projectRoot) : null;
1028
+ const stateDir = layout?.stateDir ?? resolve4(stateRoot, "state");
1029
+ const logsDir = layout?.logsDir ?? resolve4(stateRoot, "logs");
1030
+ const artifactsDir = layout?.artifactsRoot ?? resolve4(monorepoRoot, "artifacts");
1031
+ const taskConfigPath = layout?.taskConfigPath ?? resolve4(monorepoRoot, ".rig", "task-config.json");
1032
+ const binDir = layout?.binDir ?? resolve4(stateRoot, "bin");
1033
+ return {
1034
+ harnessRoot,
1035
+ stateDir: process.env.RIG_STATE_DIR || stateDir,
1036
+ artifactsDir,
1037
+ logsDir: process.env.RIG_LOGS_DIR || logsDir,
1038
+ binDir,
1039
+ hooksDir: resolve4(harnessRoot, "hooks"),
1040
+ validationDir: resolve4(harnessRoot, "validation"),
1041
+ taskConfigPath,
1042
+ sessionPath: process.env.RIG_SESSION_FILE || resolve4(stateRoot, "session", "session.json"),
1043
+ monorepoRoot,
1044
+ tsApiTestsDir: process.env.TS_API_TESTS_DIR || resolve4(monorepoRoot, "TSAPITests"),
1045
+ taskRepoCommitsPath: resolve4(stateDir, "task-repo-commits.json"),
1046
+ baseRepoPinsPath: resolve4(stateDir, "base-repo-pins.json"),
1047
+ failedApproachesPath: resolve4(stateDir, "failed_approaches.md"),
1048
+ agentProfilePath: resolve4(stateDir, "agent-profile.json"),
1049
+ reviewProfilePath: resolve4(stateDir, "review-profile.json")
1050
+ };
1051
+ }
1052
+
1053
+ // packages/runtime/src/control-plane/native/profile-ops.ts
1054
+ var DEFAULTS = {
1055
+ model: parseEnvOrDefault(process.env.DEFAULT_AGENT_MODEL, ["claude", "gpt-codex", "pi"], "pi"),
1056
+ runtime: parseEnvOrDefault(process.env.DEFAULT_AGENT_RUNTIME, ["claude-code", "codex-app-server", "pi"], "pi"),
1057
+ plugin: parseEnvOrDefault(process.env.DEFAULT_AGENT_PLUGIN, ["claude", "codex", "pi"], "pi"),
1058
+ reviewMode: parseEnvOrDefault(process.env.AI_REVIEW_MODE, ["off", "advisory", "required"], "advisory"),
1059
+ reviewProvider: parseEnvOrDefault(process.env.AI_REVIEW_PROVIDER, ["greptile"], "greptile")
1060
+ };
1061
+ function parseEnvOrDefault(value, allowed, fallback) {
1062
+ if (!value) {
1063
+ return fallback;
1064
+ }
1065
+ return allowed.includes(value) ? value : fallback;
1066
+ }
1067
+ async function readProfileFile(path) {
1068
+ if (!existsSync5(path)) {
1069
+ return null;
1070
+ }
1071
+ try {
1072
+ return await Bun.file(path).json();
1073
+ } catch {
1074
+ return null;
1075
+ }
1076
+ }
1077
+ async function showProfile(projectRoot, compact = false) {
1078
+ const paths = resolveHarnessPaths(projectRoot);
1079
+ const profile = await loadProfile(paths.agentProfilePath);
1080
+ if (compact) {
1081
+ console.log(`model=${profile.model} runtime=${profile.runtime} plugin=${profile.agent_plugin}`);
1082
+ return;
1083
+ }
1084
+ console.log("Execution Profile:");
1085
+ console.log(` model: ${profile.model}`);
1086
+ console.log(` runtime: ${profile.runtime}`);
1087
+ console.log(` plugin: ${profile.agent_plugin}`);
1088
+ }
1089
+ async function showReviewProfile(projectRoot) {
1090
+ const paths = resolveHarnessPaths(projectRoot);
1091
+ const profile = await loadReviewProfile(paths.reviewProfilePath);
1092
+ console.log("AI Review Profile:");
1093
+ console.log(` mode: ${profile.mode}`);
1094
+ console.log(` provider: ${profile.provider}`);
1095
+ console.log(` file: ${paths.reviewProfilePath}`);
1096
+ }
1097
+ async function loadProfile(path) {
1098
+ const parsed = await readProfileFile(path);
1099
+ if (parsed) {
1100
+ return {
1101
+ model: parsed.model || DEFAULTS.model,
1102
+ runtime: parsed.runtime || DEFAULTS.runtime,
1103
+ agent_plugin: parsed.agent_plugin || DEFAULTS.plugin,
1104
+ updated_at: parsed.updated_at || new Date().toISOString()
1105
+ };
1106
+ }
1107
+ return {
1108
+ model: DEFAULTS.model,
1109
+ runtime: DEFAULTS.runtime,
1110
+ agent_plugin: DEFAULTS.plugin,
1111
+ updated_at: new Date().toISOString()
1112
+ };
1113
+ }
1114
+ async function loadReviewProfile(path) {
1115
+ const parsed = await readProfileFile(path);
1116
+ if (parsed) {
1117
+ return {
1118
+ mode: parsed.mode || DEFAULTS.reviewMode,
1119
+ provider: parsed.provider || DEFAULTS.reviewProvider,
1120
+ updated_at: parsed.updated_at || new Date().toISOString()
1121
+ };
1122
+ }
1123
+ return {
1124
+ mode: DEFAULTS.reviewMode,
1125
+ provider: DEFAULTS.reviewProvider,
1126
+ updated_at: new Date().toISOString()
1127
+ };
1128
+ }
1129
+
1130
+ // packages/runtime/src/control-plane/native/repo-ops.ts
1131
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync11, readdirSync as readdirSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
1132
+ import { basename as basename7, dirname as dirname11, resolve as resolve20 } from "path";
1133
+
1134
+ // packages/runtime/src/control-plane/state-sync/types.ts
1135
+ var SUPPORTED_TASK_STATE_SCHEMA_VERSION = 1;
1136
+ var CANONICAL_TASK_LIFECYCLE_STATUSES = new Set([
1137
+ "draft",
1138
+ "open",
1139
+ "ready",
1140
+ "queued",
1141
+ "in_progress",
1142
+ "under_review",
1143
+ "blocked",
1144
+ "completed",
1145
+ "cancelled"
1146
+ ]);
1147
+ function normalizeTaskLifecycleStatus(status) {
1148
+ switch (status) {
1149
+ case "draft":
1150
+ case "open":
1151
+ case "ready":
1152
+ case "queued":
1153
+ case "in_progress":
1154
+ case "under_review":
1155
+ case "blocked":
1156
+ case "completed":
1157
+ case "cancelled":
1158
+ return status;
1159
+ case "closed":
1160
+ return "completed";
1161
+ case "running":
1162
+ return "in_progress";
1163
+ case "failed":
1164
+ return "ready";
1165
+ default:
1166
+ return null;
1167
+ }
1168
+ }
1169
+ function normalizeTaskStateMetadataStatus(status) {
1170
+ if (CANONICAL_TASK_LIFECYCLE_STATUSES.has(status)) {
1171
+ return status;
1172
+ }
1173
+ switch (status) {
1174
+ case "closed":
1175
+ return "completed";
1176
+ case "running":
1177
+ return "in_progress";
1178
+ case "failed":
1179
+ return "ready";
1180
+ default:
1181
+ return;
1182
+ }
1183
+ }
1184
+ function canonicalizeTaskStateMetadata(raw) {
1185
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
1186
+ return null;
1187
+ }
1188
+ const metadata = raw;
1189
+ const claimId = typeof metadata.claimId === "string" && metadata.claimId.length > 0 ? metadata.claimId : undefined;
1190
+ const status = normalizeTaskStateMetadataStatus(metadata.status);
1191
+ if (!status) {
1192
+ return null;
1193
+ }
1194
+ return {
1195
+ ...claimId ? { claimId } : {},
1196
+ status,
1197
+ ...typeof metadata.ownerId === "string" && metadata.ownerId.length > 0 ? { ownerId: metadata.ownerId } : {},
1198
+ ...typeof metadata.claimedAt === "string" && metadata.claimedAt.length > 0 ? { claimedAt: metadata.claimedAt } : {},
1199
+ ...typeof metadata.lastEvidenceAt === "string" && metadata.lastEvidenceAt.length > 0 ? { lastEvidenceAt: metadata.lastEvidenceAt } : {},
1200
+ ...typeof metadata.runId === "string" && metadata.runId.length > 0 ? { runId: metadata.runId } : {},
1201
+ ...typeof metadata.branchName === "string" && metadata.branchName.length > 0 ? { branchName: metadata.branchName } : {},
1202
+ ...typeof metadata.prNumber === "number" ? { prNumber: metadata.prNumber } : {},
1203
+ ...typeof metadata.prUrl === "string" && metadata.prUrl.length > 0 ? { prUrl: metadata.prUrl } : {},
1204
+ ...typeof metadata.reviewState === "string" && metadata.reviewState.length > 0 ? { reviewState: metadata.reviewState } : {},
1205
+ ...typeof metadata.blockerReason === "string" && metadata.blockerReason.length > 0 ? { blockerReason: metadata.blockerReason } : {},
1206
+ ...typeof metadata.sourceCommit === "string" && metadata.sourceCommit.length > 0 ? { sourceCommit: metadata.sourceCommit } : {}
1207
+ };
1208
+ }
1209
+ function discardMismatchedTaskStateMetadata(input) {
1210
+ input.taskId;
1211
+ const canonicalMetadata = canonicalizeTaskStateMetadata(input.metadata);
1212
+ if (!canonicalMetadata || !input.lifecycleStatus) {
1213
+ return null;
1214
+ }
1215
+ const metadataStatus = canonicalMetadata.status ?? null;
1216
+ if (metadataStatus && metadataStatus !== input.lifecycleStatus) {
1217
+ return null;
1218
+ }
1219
+ return canonicalMetadata;
1220
+ }
1221
+ function readTaskStateMetadataEnvelope(raw) {
1222
+ if (!raw || typeof raw !== "object") {
1223
+ return { schemaVersion: SUPPORTED_TASK_STATE_SCHEMA_VERSION, supported: true, baseTrackerCommit: null, tasks: {} };
1224
+ }
1225
+ const envelope = raw;
1226
+ const schemaVersion = typeof envelope.schemaVersion === "number" ? envelope.schemaVersion : SUPPORTED_TASK_STATE_SCHEMA_VERSION;
1227
+ if (schemaVersion !== SUPPORTED_TASK_STATE_SCHEMA_VERSION) {
1228
+ return { schemaVersion, supported: false, baseTrackerCommit: null, tasks: {} };
1229
+ }
1230
+ const rawTasks = envelope.tasks && typeof envelope.tasks === "object" && !Array.isArray(envelope.tasks) ? envelope.tasks : {};
1231
+ const tasks = Object.fromEntries(Object.entries(rawTasks).map(([taskId, metadata]) => [taskId, canonicalizeTaskStateMetadata(metadata)]).filter((entry) => entry[1] != null));
1232
+ return {
1233
+ schemaVersion,
1234
+ supported: true,
1235
+ baseTrackerCommit: typeof envelope.baseTrackerCommit === "string" && envelope.baseTrackerCommit.length > 0 ? envelope.baseTrackerCommit : null,
1236
+ tasks
1237
+ };
1238
+ }
1239
+
1240
+ // packages/runtime/src/control-plane/repos/registry.ts
1241
+ function createRepoRegistry(entries) {
1242
+ const map = new Map;
1243
+ for (const e of entries) {
1244
+ if (map.has(e.id))
1245
+ throw new Error(`repo already registered: ${e.id}`);
1246
+ map.set(e.id, { ...e });
1247
+ }
1248
+ const ordered = Array.from(map.values());
1249
+ return {
1250
+ getById: (id) => map.get(id),
1251
+ list: () => ordered
1252
+ };
1253
+ }
1254
+ var MANAGED_REPOS = new Map;
1255
+ function setManagedRepos(entries) {
1256
+ const next = new Map;
1257
+ for (const e of entries) {
1258
+ if (next.has(e.id)) {
1259
+ throw new Error(`managed repo already registered: ${e.id}`);
1260
+ }
1261
+ next.set(e.id, e);
1262
+ }
1263
+ MANAGED_REPOS = next;
1264
+ }
1265
+ function getManagedRepoEntry(repoId) {
1266
+ const entry = MANAGED_REPOS.get(repoId);
1267
+ if (!entry) {
1268
+ throw new Error(`managed repo not registered: ${repoId}. Plugins contribute repos via RigPlugin.contributes.repoSources; ` + `make sure a plugin declares this id and the plugin host has been initialized.`);
1269
+ }
1270
+ return entry;
1271
+ }
1272
+ function listManagedRepoEntries() {
1273
+ return Array.from(MANAGED_REPOS.values());
1274
+ }
1275
+ function repoRegistrationToManagedEntry(reg) {
1276
+ if (!reg.defaultBranch) {
1277
+ return null;
1278
+ }
1279
+ return {
1280
+ id: reg.id,
1281
+ alias: reg.defaultPath ?? reg.id,
1282
+ defaultBranch: reg.defaultBranch,
1283
+ defaultRemoteUrl: reg.url,
1284
+ remoteEnvVar: reg.remoteEnvVar,
1285
+ checkoutEnvVar: reg.checkoutEnvVar
1286
+ };
1287
+ }
1288
+ // packages/runtime/src/control-plane/repos/layout.ts
1289
+ import { existsSync as existsSync6 } from "fs";
1290
+ import { basename as basename2, dirname as dirname5, join, resolve as resolve5 } from "path";
1291
+ function resolveRepoStateDir(projectRoot) {
1292
+ const normalizedProjectRoot = resolve5(projectRoot);
1293
+ const projectParent = dirname5(normalizedProjectRoot);
1294
+ if (basename2(projectParent) === ".worktrees") {
1295
+ const ownerRoot = dirname5(projectParent);
1296
+ const ownerHasRepoMarkers = existsSync6(resolve5(ownerRoot, ".git")) || existsSync6(resolve5(ownerRoot, ".rig", "state"));
1297
+ if (ownerHasRepoMarkers) {
1298
+ return resolve5(ownerRoot, ".rig", "state");
1299
+ }
1300
+ }
1301
+ return resolve5(projectRoot, ".rig", "state");
1302
+ }
1303
+ function resolveManagedRepoLayout(projectRoot, repoId) {
1304
+ const normalizedProjectRoot = resolve5(projectRoot);
1305
+ const entry = getManagedRepoEntry(repoId);
1306
+ const stateDir = resolveRepoStateDir(normalizedProjectRoot);
1307
+ const metadataRelativePath = join("repos", entry.id);
1308
+ const metadataRoot = resolve5(stateDir, metadataRelativePath);
1309
+ const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
1310
+ const runsInsideTaskWorktree = runtimeWorkspace && resolve5(runtimeWorkspace) === normalizedProjectRoot || basename2(dirname5(normalizedProjectRoot)) === ".worktrees";
1311
+ const isPrimaryManagedRepo = listManagedRepoEntries()[0]?.id === repoId;
1312
+ const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ? resolve5(process.env[entry.checkoutEnvVar].trim()) : resolve5(normalizedProjectRoot, entry.alias);
1313
+ return {
1314
+ projectRoot: normalizedProjectRoot,
1315
+ repoId: entry.id,
1316
+ alias: entry.alias,
1317
+ defaultBranch: entry.defaultBranch,
1318
+ remoteUrl: entry.remoteEnvVar && process.env[entry.remoteEnvVar]?.trim() ? process.env[entry.remoteEnvVar].trim() : entry.defaultRemoteUrl,
1319
+ checkoutRoot,
1320
+ worktreesRoot: resolve5(checkoutRoot, ".worktrees"),
1321
+ stateDir,
1322
+ metadataRoot,
1323
+ metadataRelativePath,
1324
+ mirrorRoot: resolve5(metadataRoot, "mirror.git"),
1325
+ mirrorStatePath: resolve5(metadataRoot, "mirror-state.json"),
1326
+ mirrorStateRelativePath: join(metadataRelativePath, "mirror-state.json")
1327
+ };
1328
+ }
1329
+ function resolveMonorepoRepoLayout(projectRoot) {
1330
+ const entries = listManagedRepoEntries();
1331
+ if (entries.length === 0) {
1332
+ throw new Error("resolveMonorepoRepoLayout: no managed repos registered. Either contribute one via " + "RigPlugin.contributes.repoSources (with defaultBranch set), or avoid calling this " + "function for projects where the project root IS the monorepo.");
1333
+ }
1334
+ const primary = entries[0];
1335
+ return resolveManagedRepoLayout(projectRoot, primary.id);
1336
+ }
1337
+ // packages/runtime/src/control-plane/repos/mirror/bootstrap.ts
1338
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, realpathSync } from "fs";
1339
+ import { resolve as resolve7 } from "path";
1340
+
1341
+ // packages/runtime/src/control-plane/authority-files.ts
1342
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, appendFileSync, copyFileSync as copyFileSync2, statSync as statSync2, readdirSync, chmodSync } from "fs";
1343
+ import { dirname as dirname6, join as join2, relative, resolve as resolve6 } from "path";
1344
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
1345
+ function resolveAuthorityProjectStateDir(projectRoot) {
1346
+ const explicit = process.env.RIG_STATE_DIR?.trim();
1347
+ if (explicit) {
1348
+ return resolve6(explicit);
1349
+ }
1350
+ return resolve6(resolve6(projectRoot), ".rig", "state");
1351
+ }
1352
+ function readJsonAtPath(path, fallback) {
1353
+ if (!existsSync7(path)) {
1354
+ return fallback;
1355
+ }
1356
+ try {
1357
+ return JSON.parse(readFileSync3(path, "utf-8"));
1358
+ } catch {
1359
+ return fallback;
1360
+ }
1361
+ }
1362
+ function writeJsonAtPath(path, value) {
1363
+ mkdirSync5(dirname6(path), { recursive: true });
1364
+ writeFileSync3(path, `${JSON.stringify(value, null, 2)}
1365
+ `, "utf8");
1366
+ return path;
1367
+ }
1368
+ function readAuthorityProjectStateJson(projectRoot, relativePath, fallback) {
1369
+ return readJsonAtPath(resolve6(resolveAuthorityProjectStateDir(projectRoot), relativePath), fallback);
1370
+ }
1371
+ function writeAuthorityProjectStateJson(projectRoot, relativePath, value) {
1372
+ return writeJsonAtPath(resolve6(resolveAuthorityProjectStateDir(projectRoot), relativePath), value);
1373
+ }
1374
+
1375
+ // packages/runtime/src/control-plane/repos/mirror/state.ts
1376
+ var STATE_VERSION = 1;
1377
+ function defaultMirrorState(projectRoot, repoId) {
1378
+ const layout = resolveManagedRepoLayout(projectRoot, repoId);
1379
+ return {
1380
+ version: STATE_VERSION,
1381
+ repoId,
1382
+ remoteUrl: layout.remoteUrl,
1383
+ defaultBranch: layout.defaultBranch
1384
+ };
1385
+ }
1386
+ function readManagedRepoMirrorState(projectRoot, repoId) {
1387
+ const layout = resolveManagedRepoLayout(projectRoot, repoId);
1388
+ return readAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, null);
1389
+ }
1390
+ function writeManagedRepoMirrorState(projectRoot, repoId, patch) {
1391
+ const current = readManagedRepoMirrorState(projectRoot, repoId) || defaultMirrorState(projectRoot, repoId);
1392
+ const next = {
1393
+ ...current,
1394
+ ...patch,
1395
+ version: STATE_VERSION,
1396
+ repoId
1397
+ };
1398
+ const layout = resolveManagedRepoLayout(projectRoot, repoId);
1399
+ writeAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, next);
1400
+ return next;
1401
+ }
1402
+
1403
+ // packages/runtime/src/control-plane/repos/mirror/bootstrap.ts
1404
+ function nowIso2() {
1405
+ return new Date().toISOString();
1406
+ }
1407
+ function runGit(command, cwd) {
1408
+ const result = Bun.spawnSync(command, {
1409
+ cwd,
1410
+ stdout: "pipe",
1411
+ stderr: "pipe",
1412
+ env: process.env
1413
+ });
1414
+ return {
1415
+ exitCode: result.exitCode,
1416
+ stdout: result.stdout.toString(),
1417
+ stderr: result.stderr.toString()
1418
+ };
1419
+ }
1420
+ function ensureGitSuccess(result, command) {
1421
+ if (result.exitCode !== 0) {
1422
+ throw new Error(result.stderr || result.stdout || `git command failed: ${command.join(" ")}`);
1423
+ }
1424
+ }
1425
+ function isUsableRemoteUrl(candidate, layout) {
1426
+ return candidate.length > 0 && candidate !== layout.mirrorRoot && candidate !== layout.checkoutRoot;
1427
+ }
1428
+ function sameExistingPath(left, right) {
1429
+ try {
1430
+ return realpathSync(left) === realpathSync(right);
1431
+ } catch {
1432
+ return resolve7(left) === resolve7(right);
1433
+ }
1434
+ }
1435
+ function repoLooksUsable(repoRoot, projectRoot) {
1436
+ const probe = runGit(["git", "-C", repoRoot, "rev-parse", "--show-toplevel"], projectRoot);
1437
+ return probe.exitCode === 0 && sameExistingPath(probe.stdout.trim(), repoRoot);
1438
+ }
1439
+ function checkoutLooksUsable(layout) {
1440
+ return repoLooksUsable(layout.checkoutRoot, layout.projectRoot);
1441
+ }
1442
+ function resolveMirrorRemoteUrl(layout) {
1443
+ const entry = getManagedRepoEntry(layout.repoId);
1444
+ const explicit = entry.remoteEnvVar ? process.env[entry.remoteEnvVar]?.trim() : "";
1445
+ if (explicit) {
1446
+ return explicit;
1447
+ }
1448
+ const persisted = readManagedRepoMirrorState(layout.projectRoot, layout.repoId)?.remoteUrl?.trim();
1449
+ if (persisted && isUsableRemoteUrl(persisted, layout)) {
1450
+ return persisted;
1451
+ }
1452
+ const mirrorOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
1453
+ if (mirrorOrigin.exitCode === 0) {
1454
+ const currentOrigin = mirrorOrigin.stdout.trim();
1455
+ if (isUsableRemoteUrl(currentOrigin, layout)) {
1456
+ return currentOrigin;
1457
+ }
1458
+ }
1459
+ if (repoLooksUsable(layout.projectRoot, layout.projectRoot)) {
1460
+ const projectOrigin = runGit(["git", "-C", layout.projectRoot, "remote", "get-url", "origin"], layout.projectRoot);
1461
+ if (projectOrigin.exitCode === 0) {
1462
+ const currentOrigin = projectOrigin.stdout.trim();
1463
+ if (isUsableRemoteUrl(currentOrigin, layout)) {
1464
+ return currentOrigin;
1465
+ }
1466
+ }
1467
+ }
1468
+ if (existsSync8(resolve7(layout.checkoutRoot, ".git")) && checkoutLooksUsable(layout)) {
1469
+ const checkoutOrigin = runGit(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
1470
+ if (checkoutOrigin.exitCode === 0) {
1471
+ const currentOrigin = checkoutOrigin.stdout.trim();
1472
+ if (isUsableRemoteUrl(currentOrigin, layout)) {
1473
+ return currentOrigin;
1474
+ }
1475
+ }
1476
+ }
1477
+ return layout.remoteUrl;
1478
+ }
1479
+ function ensureManagedRepoMirror(projectRoot, repoId) {
1480
+ const layout = resolveManagedRepoLayout(projectRoot, repoId);
1481
+ mkdirSync6(layout.metadataRoot, { recursive: true });
1482
+ const remoteUrl = resolveMirrorRemoteUrl(layout);
1483
+ if (!existsSync8(resolve7(layout.mirrorRoot, "HEAD"))) {
1484
+ ensureGitSuccess(runGit(["git", "init", "--bare", layout.mirrorRoot], layout.projectRoot), ["git", "init", "--bare", layout.mirrorRoot]);
1485
+ }
1486
+ const getOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
1487
+ if (getOrigin.exitCode === 0) {
1488
+ const currentOrigin = getOrigin.stdout.trim();
1489
+ if (currentOrigin !== remoteUrl) {
1490
+ ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl]);
1491
+ }
1492
+ } else {
1493
+ ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl]);
1494
+ }
1495
+ writeManagedRepoMirrorState(projectRoot, repoId, {
1496
+ remoteUrl,
1497
+ defaultBranch: layout.defaultBranch,
1498
+ initializedAt: nowIso2()
1499
+ });
1500
+ return layout;
1501
+ }
1502
+ // packages/runtime/src/control-plane/repos/mirror/refresh.ts
1503
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, realpathSync as realpathSync2, rmSync as rmSync2 } from "fs";
1504
+ import { resolve as resolve8 } from "path";
1505
+ function nowIso3() {
1506
+ return new Date().toISOString();
1507
+ }
1508
+ function runGit2(command, cwd) {
1509
+ const result = Bun.spawnSync(command, {
1510
+ cwd,
1511
+ stdout: "pipe",
1512
+ stderr: "pipe",
1513
+ env: process.env
1514
+ });
1515
+ return {
1516
+ exitCode: result.exitCode,
1517
+ stdout: result.stdout.toString(),
1518
+ stderr: result.stderr.toString()
1519
+ };
1520
+ }
1521
+ function ensureGitSuccess2(result, command) {
1522
+ if (result.exitCode !== 0) {
1523
+ throw new Error(result.stderr || result.stdout || `git command failed: ${command.join(" ")}`);
1524
+ }
1525
+ return result.stdout.trim();
1526
+ }
1527
+ function sameExistingPath2(left, right) {
1528
+ try {
1529
+ return realpathSync2(left) === realpathSync2(right);
1530
+ } catch {
1531
+ return resolve8(left) === resolve8(right);
1532
+ }
1533
+ }
1534
+ function ensureMirrorHead(layout) {
1535
+ ensureGitSuccess2(runGit2([
1536
+ "git",
1537
+ "--git-dir",
1538
+ layout.mirrorRoot,
1539
+ "fetch",
1540
+ "--prune",
1541
+ "origin",
1542
+ "+refs/heads/*:refs/heads/*",
1543
+ "+refs/tags/*:refs/tags/*"
1544
+ ], layout.projectRoot), [
1545
+ "git",
1546
+ "--git-dir",
1547
+ layout.mirrorRoot,
1548
+ "fetch",
1549
+ "--prune",
1550
+ "origin",
1551
+ "+refs/heads/*:refs/heads/*",
1552
+ "+refs/tags/*:refs/tags/*"
1553
+ ]);
1554
+ const headRef = `refs/heads/${layout.defaultBranch}`;
1555
+ const headCommit = ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "rev-parse", headRef], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "rev-parse", headRef]);
1556
+ const remoteUrl = ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"]);
1557
+ ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "symbolic-ref", "HEAD", headRef], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "symbolic-ref", "HEAD", headRef]);
1558
+ writeManagedRepoMirrorState(layout.projectRoot, layout.repoId, {
1559
+ remoteUrl,
1560
+ defaultBranch: layout.defaultBranch,
1561
+ lastSyncedAt: nowIso3(),
1562
+ headRef,
1563
+ headCommit
1564
+ });
1565
+ return headCommit;
1566
+ }
1567
+ function refreshManagedRepoMirror(projectRoot, repoId) {
1568
+ const layout = ensureManagedRepoMirror(projectRoot, repoId);
1569
+ const headCommit = ensureMirrorHead(layout);
1570
+ return { layout, headCommit };
1571
+ }
1572
+ function checkoutLooksUsable2(layout) {
1573
+ const probe = runGit2(["git", "-C", layout.checkoutRoot, "rev-parse", "--show-toplevel"], layout.projectRoot);
1574
+ return probe.exitCode === 0 && sameExistingPath2(probe.stdout.trim(), layout.checkoutRoot);
1575
+ }
1576
+ function ensureCheckoutFromMirror(layout) {
1577
+ mkdirSync7(resolve8(layout.checkoutRoot, ".."), { recursive: true });
1578
+ const gitPath = resolve8(layout.checkoutRoot, ".git");
1579
+ if (existsSync9(layout.checkoutRoot) && (!existsSync9(gitPath) || !checkoutLooksUsable2(layout))) {
1580
+ rmSync2(layout.checkoutRoot, { recursive: true, force: true });
1581
+ }
1582
+ if (!existsSync9(gitPath)) {
1583
+ ensureGitSuccess2(runGit2(["git", "clone", layout.mirrorRoot, layout.checkoutRoot], layout.projectRoot), ["git", "clone", layout.mirrorRoot, layout.checkoutRoot]);
1584
+ }
1585
+ const getOrigin = runGit2(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
1586
+ if (getOrigin.exitCode === 0) {
1587
+ const currentOrigin = getOrigin.stdout.trim();
1588
+ if (currentOrigin !== layout.mirrorRoot) {
1589
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "remote", "set-url", "origin", layout.mirrorRoot], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "remote", "set-url", "origin", layout.mirrorRoot]);
1590
+ }
1591
+ } else {
1592
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "remote", "add", "origin", layout.mirrorRoot], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "remote", "add", "origin", layout.mirrorRoot]);
1593
+ }
1594
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "fetch", "origin", layout.defaultBranch], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "fetch", "origin", layout.defaultBranch]);
1595
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "reset", "--hard"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "reset", "--hard"]);
1596
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "clean", "-fd"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "clean", "-fd"]);
1597
+ ensureGitSuccess2(runGit2([
1598
+ "git",
1599
+ "-C",
1600
+ layout.checkoutRoot,
1601
+ "checkout",
1602
+ "--force",
1603
+ "-B",
1604
+ layout.defaultBranch,
1605
+ `origin/${layout.defaultBranch}`
1606
+ ], layout.projectRoot), [
1607
+ "git",
1608
+ "-C",
1609
+ layout.checkoutRoot,
1610
+ "checkout",
1611
+ "--force",
1612
+ "-B",
1613
+ layout.defaultBranch,
1614
+ `origin/${layout.defaultBranch}`
1615
+ ]);
1616
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "reset", "--hard", `origin/${layout.defaultBranch}`], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "reset", "--hard", `origin/${layout.defaultBranch}`]);
1617
+ ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "clean", "-fd"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "clean", "-fd"]);
1618
+ return ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "rev-parse", "HEAD"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "rev-parse", "HEAD"]);
1619
+ }
1620
+ function syncManagedRepo(projectRoot, repoId) {
1621
+ const { layout, headCommit } = refreshManagedRepoMirror(projectRoot, repoId);
1622
+ const checkoutCommit = ensureCheckoutFromMirror(layout);
1623
+ if (checkoutCommit !== headCommit) {
1624
+ throw new Error(`Managed repo checkout ${layout.alias} is out of sync with mirror: expected ${headCommit}, got ${checkoutCommit}.`);
1625
+ }
1626
+ return { layout, headCommit };
1627
+ }
1628
+ // packages/runtime/src/control-plane/native/task-ops.ts
1629
+ import { appendFileSync as appendFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
1630
+ import { resolve as resolve19 } from "path";
1631
+
1632
+ // packages/runtime/src/build-time-config.ts
1633
+ function normalizeBuildConfig(value) {
1634
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1635
+ return {};
1636
+ }
1637
+ return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
1638
+ }
1639
+ function readBuildConfig() {
1640
+ if (typeof __RIG_BUILD_CONFIG__ !== "undefined") {
1641
+ return normalizeBuildConfig(__RIG_BUILD_CONFIG__);
1642
+ }
1643
+ const raw = process.env.RIG_BUILD_CONFIG_JSON?.trim();
1644
+ if (!raw) {
1645
+ return {};
1646
+ }
1647
+ try {
1648
+ return normalizeBuildConfig(JSON.parse(raw));
1649
+ } catch {
1650
+ return {};
1651
+ }
1652
+ }
1653
+
1654
+ // packages/runtime/src/control-plane/runtime/tooling/shell.ts
1655
+ import { tmpdir as tmpdir2 } from "os";
1656
+ import { basename as basename3, dirname as dirname7, resolve as resolve9 } from "path";
1657
+ var sharedNativeShellOutputDir = resolve9(tmpdir2(), "rig-native");
1658
+ var sharedNativeShellOutputPath = resolve9(sharedNativeShellOutputDir, `rig-shell-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1659
+ function runtimeToolGatewayNames() {
1660
+ return [
1661
+ "bash",
1662
+ "sh",
1663
+ "zsh",
1664
+ "git",
1665
+ "bun",
1666
+ "node",
1667
+ "python3",
1668
+ "rg",
1669
+ "grep",
1670
+ "sed",
1671
+ "cat",
1672
+ "ls",
1673
+ "find",
1674
+ "tsc",
1675
+ "gh",
1676
+ "mkdir",
1677
+ "rm",
1678
+ "mv",
1679
+ "cp",
1680
+ "touch",
1681
+ "pwd",
1682
+ "head",
1683
+ "tail",
1684
+ "wc",
1685
+ "sort",
1686
+ "uniq",
1687
+ "awk",
1688
+ "xargs",
1689
+ "dirname",
1690
+ "basename",
1691
+ "realpath",
1692
+ "env",
1693
+ "jq",
1694
+ "tee",
1695
+ "which"
1696
+ ];
1697
+ }
1698
+ // packages/runtime/src/control-plane/runtime/tooling/file-tools.ts
1699
+ import { tmpdir as tmpdir3 } from "os";
1700
+ import { basename as basename4, dirname as dirname8, resolve as resolve10 } from "path";
1701
+ var sharedNativeToolsOutputDir = resolve10(tmpdir3(), "rig-native");
1702
+ var sharedNativeToolsOutputPath = resolve10(sharedNativeToolsOutputDir, `rig-tools-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
1703
+ function runtimeFileToolNames() {
1704
+ return [
1705
+ "rig-read",
1706
+ "rig-write",
1707
+ "rig-edit",
1708
+ "rig-glob",
1709
+ "rig-grep"
1710
+ ];
1711
+ }
1712
+
1713
+ // packages/runtime/src/control-plane/runtime/tooling/gateway.ts
1714
+ function runtimeGatewayToolNames() {
1715
+ return runtimeToolGatewayNames();
1716
+ }
1717
+ // packages/runtime/src/control-plane/browser-contract.ts
1718
+ import { resolve as resolve11 } from "path";
1719
+ var DEFAULT_BROWSER_ATTACH_URL = "http://127.0.0.1:9333";
1720
+ var DEFAULT_BROWSER_MODE = "persistent";
1721
+ var RUNTIME_BROWSER_HELPERS = {
1722
+ launch: "rig-browser-launch",
1723
+ check: "rig-browser-check",
1724
+ attachInfo: "rig-browser-attach-info",
1725
+ e2e: "rig-browser-e2e",
1726
+ resetProfile: "rig-browser-reset-profile"
1727
+ };
1728
+ var BASE_REMOTE_DEBUGGING_PORT = 9222;
1729
+ var REMOTE_DEBUGGING_PORT_SPREAD = 4000;
1730
+ function hashString(input) {
1731
+ let hash = 0;
1732
+ for (let index = 0;index < input.length; index += 1) {
1733
+ hash = (hash << 5) - hash + input.charCodeAt(index) | 0;
1734
+ }
1735
+ return Math.abs(hash);
1736
+ }
1737
+ function derivePortFromProfile(profileName) {
1738
+ return BASE_REMOTE_DEBUGGING_PORT + hashString(profileName) % REMOTE_DEBUGGING_PORT_SPREAD;
1739
+ }
1740
+ function parseAttachUrl(attachUrl) {
1741
+ try {
1742
+ return new URL((attachUrl || DEFAULT_BROWSER_ATTACH_URL).trim() || DEFAULT_BROWSER_ATTACH_URL);
1743
+ } catch {
1744
+ return new URL(DEFAULT_BROWSER_ATTACH_URL);
1745
+ }
1746
+ }
1747
+ function sanitizeRuntimeSuffix(runtimeId) {
1748
+ return runtimeId.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 16) || "runtime";
1749
+ }
1750
+ function resolveBrowserStateDir(projectRoot, configuredStateDir) {
1751
+ const trimmed = configuredStateDir?.trim() || ".tmp/rig-browser";
1752
+ if (trimmed.startsWith("/")) {
1753
+ return resolve11(trimmed);
1754
+ }
1755
+ return resolve11(projectRoot || process.cwd(), trimmed);
1756
+ }
1757
+ function resolveTaskBrowserContext(browser, options = {}) {
1758
+ if (!browser?.required) {
1759
+ return;
1760
+ }
1761
+ const defaultProfile = browser.profile?.trim() || "default";
1762
+ const mode = browser.mode?.trim() || DEFAULT_BROWSER_MODE;
1763
+ const defaultAttach = parseAttachUrl(browser.attach_url);
1764
+ const shouldDeriveRuntimeProfile = Boolean(options.runtimeId?.trim()) && mode !== "shared";
1765
+ const effectiveProfile = shouldDeriveRuntimeProfile ? `${defaultProfile}-${sanitizeRuntimeSuffix(options.runtimeId.trim())}` : defaultProfile;
1766
+ const effectivePort = shouldDeriveRuntimeProfile ? derivePortFromProfile(effectiveProfile) : Number(defaultAttach.port || "80");
1767
+ const effectiveAttach = new URL(defaultAttach.toString());
1768
+ effectiveAttach.port = String(effectivePort);
1769
+ return {
1770
+ required: true,
1771
+ preset: browser.preset?.trim() || "default",
1772
+ mode,
1773
+ stateDir: resolveBrowserStateDir(options.hostProjectRoot, browser.state_dir),
1774
+ defaultProfile,
1775
+ effectiveProfile,
1776
+ defaultAttachUrl: defaultAttach.toString(),
1777
+ effectiveAttachUrl: effectiveAttach.toString(),
1778
+ devCommand: browser.dev_command?.trim() || undefined,
1779
+ launchCommand: browser.launch_command?.trim() || undefined,
1780
+ checkCommand: browser.check_command?.trim() || undefined,
1781
+ e2eCommand: browser.e2e_command?.trim() || undefined,
1782
+ launchHelper: RUNTIME_BROWSER_HELPERS.launch,
1783
+ checkHelper: RUNTIME_BROWSER_HELPERS.check,
1784
+ attachInfoHelper: RUNTIME_BROWSER_HELPERS.attachInfo,
1785
+ e2eHelper: RUNTIME_BROWSER_HELPERS.e2e,
1786
+ resetProfileHelper: RUNTIME_BROWSER_HELPERS.resetProfile
1787
+ };
1788
+ }
1789
+ function buildBrowserGuidanceLines(browser) {
1790
+ const lines = [
1791
+ "This task requires Rig Browser.",
1792
+ `Launch the browser: \`${browser.launchHelper}\`${browser.devCommand ? " or `rig-browser-launch --dev`" : ""}.`,
1793
+ `Check the browser contract: \`${browser.checkHelper}\`.`,
1794
+ `Show attach details: \`${browser.attachInfoHelper}\`.`,
1795
+ `Attach Chrome DevTools MCP to ${browser.effectiveAttachUrl}.`,
1796
+ `Preset: ${browser.preset}.`,
1797
+ `Profile: ${browser.effectiveProfile}.`,
1798
+ `State dir: ${browser.stateDir}.`,
1799
+ `Reset the active profile with \`${browser.resetProfileHelper}\`.`
1800
+ ];
1801
+ if (browser.e2eCommand) {
1802
+ lines.push(`Run app-owned browser e2e with \`${browser.e2eHelper}\`.`);
1803
+ }
1804
+ if (browser.defaultProfile !== browser.effectiveProfile) {
1805
+ lines.push(`Base profile: ${browser.defaultProfile}. Runtime launches derive an isolated effective profile.`);
1806
+ }
1807
+ return lines;
1808
+ }
1809
+
1810
+ // packages/runtime/src/control-plane/plugin-host-context.ts
1811
+ import { createPluginHost } from "@rig/core";
1812
+ import { loadConfig } from "@rig/core/load-config";
1813
+
1814
+ // packages/runtime/src/control-plane/task-source.ts
1815
+ function createTaskSourceRegistry() {
1816
+ const byId = new Map;
1817
+ const order = [];
1818
+ return {
1819
+ register(s) {
1820
+ if (byId.has(s.id))
1821
+ throw new Error(`task source already registered: ${s.id}`);
1822
+ byId.set(s.id, s);
1823
+ order.push(s);
1824
+ },
1825
+ resolveById(id) {
1826
+ const s = byId.get(id);
1827
+ if (!s)
1828
+ throw new Error(`task source not registered: ${id}`);
1829
+ return s;
1830
+ },
1831
+ resolveByKind(kind) {
1832
+ for (const s of order)
1833
+ if (s.kind === kind)
1834
+ return s;
1835
+ throw new Error(`no task source registered for kind: ${kind}`);
1836
+ },
1837
+ list: () => order
1838
+ };
1839
+ }
1840
+
1841
+ // packages/runtime/src/control-plane/task-source-bootstrap.ts
1842
+ function formatRegisteredKinds(pluginHost) {
1843
+ const kinds = pluginHost ? pluginHost.listExecutableTaskSources().map((source) => source.kind) : [];
1844
+ return kinds.length > 0 ? kinds.join(", ") : "none";
1845
+ }
1846
+ function buildTaskSourceRegistry(config, pluginHost) {
1847
+ const registry = createTaskSourceRegistry();
1848
+ const taskSourceConfig = config.taskSource;
1849
+ const factory = pluginHost?.resolveTaskSourceFactoryByKind(taskSourceConfig.kind);
1850
+ if (!factory) {
1851
+ throw new Error(`No task source factory registered for kind "${taskSourceConfig.kind}". ` + `Registered kinds: ${formatRegisteredKinds(pluginHost)}. ` + "Load a plugin that contributes an executable task source factory for this kind.");
1852
+ }
1853
+ registry.register(factory.factory(taskSourceConfig));
1854
+ return registry;
1855
+ }
1856
+
1857
+ // packages/runtime/src/control-plane/agent-roles.ts
1858
+ function createAgentRoleRegistry(pluginRoles, configOverrides) {
1859
+ const map = new Map;
1860
+ for (const r of pluginRoles) {
1861
+ if (map.has(r.id))
1862
+ throw new Error(`agent role already registered: ${r.id}`);
1863
+ const override = configOverrides?.[r.id];
1864
+ const model = override?.model ?? r.defaultModel;
1865
+ if (!model) {
1866
+ throw new Error(`agent role "${r.id}" has no model \u2014 provide defaultModel in plugin or model in config.runtime.agentRoles.${r.id}`);
1867
+ }
1868
+ map.set(r.id, { ...r, model });
1869
+ }
1870
+ return {
1871
+ resolve(id) {
1872
+ const r = map.get(id);
1873
+ if (!r)
1874
+ throw new Error(`agent role not registered: ${id}`);
1875
+ return r;
1876
+ },
1877
+ list: () => Array.from(map.values())
1878
+ };
1879
+ }
1880
+
1881
+ // packages/runtime/src/control-plane/task-fields.ts
1882
+ function createTaskFieldRegistry(extensions) {
1883
+ const byId = new Map;
1884
+ for (const e of extensions) {
1885
+ if (byId.has(e.id))
1886
+ throw new Error(`task field extension already registered: ${e.id}`);
1887
+ byId.set(e.id, e);
1888
+ }
1889
+ return {
1890
+ get: (id) => byId.get(id),
1891
+ list: () => Array.from(byId.values()),
1892
+ fieldNames: () => Array.from(byId.values()).map((e) => e.fieldName),
1893
+ validateTaskFields(task) {
1894
+ const errors = [];
1895
+ for (const ext of byId.values()) {
1896
+ let schema;
1897
+ try {
1898
+ schema = JSON.parse(ext.schemaJson);
1899
+ } catch {
1900
+ errors.push(`task field "${ext.id}": schemaJson is not valid JSON`);
1901
+ continue;
1902
+ }
1903
+ const isRequired = typeof schema === "object" && schema !== null && schema.required === true;
1904
+ if (!isRequired)
1905
+ continue;
1906
+ const value = task[ext.fieldName];
1907
+ if (value === undefined || value === null || value === "") {
1908
+ errors.push(`task field "${ext.fieldName}" (from extension "${ext.id}") is required but missing`);
1909
+ }
1910
+ }
1911
+ return errors.length === 0 ? { ok: true } : { ok: false, errors };
1912
+ }
1913
+ };
1914
+ }
1915
+
1916
+ // packages/runtime/src/control-plane/validators/runtime-registration.ts
1917
+ import { existsSync as existsSync10 } from "fs";
1918
+ import { join as join3 } from "path";
1919
+ function createValidatorRegistry() {
1920
+ const map = new Map;
1921
+ const order = [];
1922
+ const registry = {
1923
+ register(v) {
1924
+ if (map.has(v.id))
1925
+ throw new Error(`validator already registered: ${v.id}`);
1926
+ map.set(v.id, v);
1927
+ order.push(v);
1928
+ },
1929
+ resolve(id) {
1930
+ const v = map.get(id);
1931
+ if (!v)
1932
+ throw new Error(`validator not registered: ${id}`);
1933
+ return v;
1934
+ },
1935
+ list: () => order
1936
+ };
1937
+ registerBuiltInValidators(registry);
1938
+ return registry;
1939
+ }
1940
+ function registerBuiltInValidators(registry) {
1941
+ registry.register({
1942
+ id: "std:typecheck",
1943
+ category: "custom",
1944
+ description: "Runs the package typecheck script when present.",
1945
+ run: async (ctx) => runStdTypecheck(ctx)
1946
+ });
1947
+ }
1948
+ async function runStdTypecheck(ctx) {
1949
+ const packageJsonPath = join3(ctx.workspaceRoot, "package.json");
1950
+ if (!existsSync10(packageJsonPath)) {
1951
+ return {
1952
+ id: "std:typecheck",
1953
+ passed: false,
1954
+ summary: `package.json not found at ${packageJsonPath}`
1955
+ };
1956
+ }
1957
+ const proc = Bun.spawn(["bun", "run", "typecheck"], {
1958
+ cwd: ctx.workspaceRoot,
1959
+ env: process.env,
1960
+ stdout: "pipe",
1961
+ stderr: "pipe"
1962
+ });
1963
+ const [exitCode, stdout, stderr] = await Promise.all([
1964
+ proc.exited,
1965
+ new Response(proc.stdout).text(),
1966
+ new Response(proc.stderr).text()
1967
+ ]);
1968
+ const output = `${stdout}${stderr}`.trim();
1969
+ return {
1970
+ id: "std:typecheck",
1971
+ passed: exitCode === 0,
1972
+ summary: exitCode === 0 ? "typecheck passed" : "typecheck failed",
1973
+ ...output ? { details: output.slice(0, 4000) } : {}
1974
+ };
1975
+ }
1976
+
1977
+ // packages/runtime/src/control-plane/hook-materializer.ts
1978
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
1979
+ import { dirname as dirname9, resolve as resolve12 } from "path";
1980
+ var MARKER_PLUGIN = "_rigPlugin";
1981
+ var MARKER_HOOK_ID = "_rigHookId";
1982
+ function matcherToString(matcher) {
1983
+ if (matcher.kind === "all")
1984
+ return;
1985
+ if (matcher.kind === "tool")
1986
+ return matcher.name;
1987
+ return matcher.pattern;
1988
+ }
1989
+ function isPluginOwned(cmd) {
1990
+ return typeof cmd[MARKER_PLUGIN] === "string";
1991
+ }
1992
+ function materializeHooks(projectRoot, entries) {
1993
+ const settingsPath = resolve12(projectRoot, ".claude", "settings.json");
1994
+ const existing = existsSync11(settingsPath) ? safeReadJson(settingsPath) : {};
1995
+ const hooks = existing.hooks ?? {};
1996
+ for (const event of Object.keys(hooks)) {
1997
+ const groups = hooks[event] ?? [];
1998
+ const cleaned = [];
1999
+ for (const group of groups) {
2000
+ const operatorHooks = group.hooks.filter((h) => !isPluginOwned(h));
2001
+ if (operatorHooks.length > 0) {
2002
+ cleaned.push({ ...group, hooks: operatorHooks });
2003
+ }
2004
+ }
2005
+ if (cleaned.length > 0) {
2006
+ hooks[event] = cleaned;
2007
+ } else {
2008
+ delete hooks[event];
2009
+ }
2010
+ }
2011
+ for (const { pluginName, hook } of entries) {
2012
+ if (!hook.command) {
2013
+ continue;
2014
+ }
2015
+ const event = hook.event;
2016
+ const matcherString = matcherToString(hook.matcher);
2017
+ const groups = hooks[event] ??= [];
2018
+ let group = groups.find((g) => g.matcher === matcherString);
2019
+ if (!group) {
2020
+ group = matcherString === undefined ? { hooks: [] } : { matcher: matcherString, hooks: [] };
2021
+ groups.push(group);
2022
+ }
2023
+ group.hooks.push({
2024
+ type: "command",
2025
+ command: hook.command,
2026
+ [MARKER_PLUGIN]: pluginName,
2027
+ [MARKER_HOOK_ID]: hook.id
2028
+ });
2029
+ }
2030
+ const next = { ...existing };
2031
+ if (Object.keys(hooks).length > 0) {
2032
+ next.hooks = hooks;
2033
+ } else {
2034
+ delete next.hooks;
2035
+ }
2036
+ mkdirSync8(dirname9(settingsPath), { recursive: true });
2037
+ writeFileSync4(settingsPath, `${JSON.stringify(next, null, 2)}
2038
+ `, "utf-8");
2039
+ return settingsPath;
2040
+ }
2041
+ function safeReadJson(path) {
2042
+ try {
2043
+ return JSON.parse(readFileSync4(path, "utf-8"));
2044
+ } catch {
2045
+ return {};
2046
+ }
2047
+ }
2048
+
2049
+ // packages/runtime/src/control-plane/plugin-host-context.ts
2050
+ async function buildPluginHostContext(projectRoot) {
2051
+ let config;
2052
+ try {
2053
+ config = await loadConfig(projectRoot);
2054
+ } catch (err) {
2055
+ const msg = err instanceof Error ? err.message : String(err);
2056
+ if (msg.includes("no rig.config")) {
2057
+ return null;
2058
+ }
2059
+ throw err;
2060
+ }
2061
+ const pluginHost = createPluginHost(config.plugins);
2062
+ setScopeRules(config.workspace.scopeNormalization);
2063
+ const validatorRegistry = createValidatorRegistry();
2064
+ for (const impl of pluginHost.listExecutableValidators()) {
2065
+ validatorRegistry.register(impl);
2066
+ }
2067
+ const taskSourceRegistry = buildTaskSourceRegistry(config, pluginHost);
2068
+ const repoRegistry = createRepoRegistry(pluginHost.listRepoSources());
2069
+ const managedEntries = pluginHost.listRepoSources().map(repoRegistrationToManagedEntry).filter((e) => e !== null);
2070
+ setManagedRepos(managedEntries);
2071
+ const configRoleOverrides = config.runtime?.agentRoles;
2072
+ const agentRoleRegistry = createAgentRoleRegistry(pluginHost.listAgentRoles(), configRoleOverrides);
2073
+ const taskFieldRegistry = createTaskFieldRegistry(pluginHost.listTaskFieldExtensions());
2074
+ try {
2075
+ const hookEntries = config.plugins.flatMap((plugin) => (plugin.contributes?.hooks ?? []).map((hook) => ({
2076
+ pluginName: plugin.name,
2077
+ hook
2078
+ })));
2079
+ if (hookEntries.length > 0) {
2080
+ materializeHooks(projectRoot, hookEntries);
2081
+ }
2082
+ } catch (err) {
2083
+ console.warn(`[plugin-host] hook materialization failed: ${err instanceof Error ? err.message : err}`);
2084
+ }
2085
+ return {
2086
+ config,
2087
+ pluginHost,
2088
+ validatorRegistry,
2089
+ taskSourceRegistry,
2090
+ repoRegistry,
2091
+ agentRoleRegistry,
2092
+ taskFieldRegistry
2093
+ };
2094
+ }
2095
+
2096
+ // packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
2097
+ import { spawnSync } from "child_process";
2098
+ import { existsSync as existsSync13, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
2099
+ import { basename as basename5, join as join4, resolve as resolve14 } from "path";
2100
+
2101
+ // packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
2102
+ import { existsSync as existsSync12, readFileSync as readFileSync5 } from "fs";
2103
+ import { resolve as resolve13 } from "path";
2104
+
2105
+ // packages/runtime/src/control-plane/tasks/task-record-reader.ts
2106
+ async function findTaskById(reader, id) {
2107
+ const tasks = await reader.listTasks();
2108
+ return tasks.find((task) => task.id === id) ?? null;
2109
+ }
2110
+
2111
+ // packages/runtime/src/control-plane/tasks/legacy-task-config-source.ts
2112
+ class LegacyTaskConfigReadError extends Error {
2113
+ code = "LEGACY_TASK_CONFIG_READ_FAILED";
2114
+ projectRoot;
2115
+ configPath;
2116
+ cause;
2117
+ constructor(input) {
2118
+ super(input.message, { cause: input.cause });
2119
+ this.name = "LegacyTaskConfigReadError";
2120
+ this.projectRoot = input.projectRoot;
2121
+ this.configPath = input.configPath;
2122
+ this.cause = input.cause;
2123
+ }
2124
+ }
2125
+ function createLegacyTaskConfigRecordReader(projectRoot, options = {}) {
2126
+ const configPath = options.configPath ?? resolve13(projectRoot, ".rig", "task-config.json");
2127
+ const reader = {
2128
+ async listTasks() {
2129
+ return readLegacyTaskRecords(projectRoot, configPath);
2130
+ },
2131
+ async getTask(id) {
2132
+ return findTaskById(reader, id);
2133
+ }
2134
+ };
2135
+ return reader;
2136
+ }
2137
+ function readLegacyTaskRecords(projectRoot, configPath = resolve13(projectRoot, ".rig", "task-config.json")) {
2138
+ if (!existsSync12(configPath)) {
2139
+ return [];
2140
+ }
2141
+ const rawConfig = readLegacyTaskConfigJson(projectRoot, configPath);
2142
+ return Object.entries(stripLegacyTaskConfigMetadata(rawConfig)).map(([id, entry]) => legacyTaskConfigEntryToRecord(id, entry)).filter((record) => record !== null);
2143
+ }
2144
+ function readLegacyTaskConfigJson(projectRoot, configPath) {
2145
+ try {
2146
+ const parsed = JSON.parse(readFileSync5(configPath, "utf8"));
2147
+ if (isPlainRecord(parsed)) {
2148
+ return parsed;
2149
+ }
2150
+ throw new Error("task config root must be a JSON object");
2151
+ } catch (cause) {
2152
+ throw new LegacyTaskConfigReadError({
2153
+ projectRoot,
2154
+ configPath,
2155
+ message: `Could not read legacy task config at ${configPath} for project ${projectRoot}: ${cause instanceof Error ? cause.message : String(cause)}`,
2156
+ cause
2157
+ });
2158
+ }
2159
+ }
2160
+ function stripLegacyTaskConfigMetadata(raw) {
2161
+ const { validation_descriptions: _legacyDescriptions, _meta, ...tasks } = raw;
2162
+ return tasks;
2163
+ }
2164
+ function legacyTaskConfigEntryToRecord(id, entry) {
2165
+ if (!isPlainRecord(entry)) {
2166
+ return null;
2167
+ }
2168
+ const deps = firstStringList(entry.deps, entry.dependencies, entry.validation_deps, entry.validationDeps);
2169
+ const validation = readStringList(entry.validation);
2170
+ const validators = readStringList(entry.validators);
2171
+ const scope = readStringList(entry.scope);
2172
+ const status = typeof entry.status === "string" ? entry.status : "open";
2173
+ const title = typeof entry.title === "string" ? entry.title : undefined;
2174
+ const description = typeof entry.description === "string" ? entry.description : undefined;
2175
+ const acceptanceCriteria = typeof entry.acceptance_criteria === "string" ? entry.acceptance_criteria : typeof entry.acceptanceCriteria === "string" ? entry.acceptanceCriteria : undefined;
2176
+ return {
2177
+ id,
2178
+ deps,
2179
+ status,
2180
+ source: "legacy-task-config",
2181
+ ...title ? { title } : {},
2182
+ ...description ? { description } : {},
2183
+ ...acceptanceCriteria ? { acceptanceCriteria } : {},
2184
+ ...scope.length > 0 ? { scope } : {},
2185
+ ...validation.length > 0 ? { validation } : {},
2186
+ ...validators.length > 0 ? { validators } : {},
2187
+ ...preservedLegacyFields(entry)
2188
+ };
2189
+ }
2190
+ function preservedLegacyFields(entry) {
2191
+ const preserved = {};
2192
+ for (const key of [
2193
+ "role",
2194
+ "browser",
2195
+ "repo_pins",
2196
+ "criticality",
2197
+ "queue_weight",
2198
+ "creates_repo",
2199
+ "auto_synced"
2200
+ ]) {
2201
+ if (entry[key] !== undefined) {
2202
+ preserved[key] = entry[key];
2203
+ }
2204
+ }
2205
+ return preserved;
2206
+ }
2207
+ function firstStringList(...candidates) {
2208
+ for (const candidate of candidates) {
2209
+ const list = readStringList(candidate);
2210
+ if (list.length > 0) {
2211
+ return list;
2212
+ }
2213
+ }
2214
+ return [];
2215
+ }
2216
+ function readStringList(candidate) {
2217
+ if (!Array.isArray(candidate)) {
2218
+ return [];
2219
+ }
2220
+ return candidate.filter((value) => typeof value === "string");
2221
+ }
2222
+ function isPlainRecord(candidate) {
2223
+ return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
2224
+ }
2225
+
2226
+ // packages/runtime/src/control-plane/tasks/source-aware-task-config-source.ts
2227
+ var STATUS_LABELS = new Set(["ready", "blocked", "in-progress", "under-review", "failed", "cancelled"]);
2228
+ var FILE_TASK_PATTERN = /\.(task\.)?json$/;
2229
+ function createSourceAwareTaskConfigRecordReader(projectRoot, options = {}) {
2230
+ const configPath = options.configPath ?? resolve14(projectRoot, ".rig", "task-config.json");
2231
+ const legacy = createLegacyTaskConfigRecordReader(projectRoot, { configPath });
2232
+ const spawnFn = options.spawn ?? spawnSync;
2233
+ const ghBinary = options.ghBinary ?? "gh";
2234
+ const allowLocalFallback = options.allowLocalTaskConfigStatusFallback ?? true;
2235
+ return {
2236
+ async listTasks() {
2237
+ const rawConfig = readRawTaskConfig(configPath);
2238
+ if (!rawConfig) {
2239
+ const configuredFilesPath = readConfiguredFilesTaskSourcePath(projectRoot);
2240
+ return configuredFilesPath ? listFileBackedTasks(projectRoot, configuredFilesPath) : [];
2241
+ }
2242
+ const tasks = [];
2243
+ const legacyTasks = await legacy.listTasks();
2244
+ const legacyById = new Map(legacyTasks.map((task) => [task.id, task]));
2245
+ for (const [id, rawEntry] of Object.entries(stripLegacyTaskConfigMetadata2(rawConfig))) {
2246
+ if (!isPlainRecord2(rawEntry)) {
2247
+ continue;
2248
+ }
2249
+ const metadata = readMaterializedTaskMetadata(rawEntry);
2250
+ if (metadata.taskSource?.kind === "github-issues") {
2251
+ tasks.push(readGithubIssueTask(ghBinary, spawnFn, id, metadata, rawEntry));
2252
+ continue;
2253
+ }
2254
+ if (metadata.taskSource?.kind === "files" && metadata.taskSource.path) {
2255
+ const fileTask = readFileBackedTask(projectRoot, metadata.taskSource.path, id, rawEntry);
2256
+ if (fileTask)
2257
+ tasks.push(fileTask);
2258
+ continue;
2259
+ }
2260
+ if (!allowLocalFallback) {
2261
+ continue;
2262
+ }
2263
+ const legacyTask = legacyById.get(id);
2264
+ if (legacyTask) {
2265
+ tasks.push(legacyTask);
2266
+ }
2267
+ }
2268
+ return tasks;
2269
+ },
2270
+ async getTask(id) {
2271
+ const rawEntry = readRawTaskEntry(configPath, id);
2272
+ if (!rawEntry) {
2273
+ const configuredFilesPath = readConfiguredFilesTaskSourcePath(projectRoot);
2274
+ return configuredFilesPath ? readFileBackedTask(projectRoot, configuredFilesPath, id, {}) : null;
2275
+ }
2276
+ const metadata = readMaterializedTaskMetadata(rawEntry);
2277
+ if (metadata.taskSource?.kind === "github-issues") {
2278
+ return readGithubIssueTask(ghBinary, spawnFn, id, metadata, rawEntry);
2279
+ }
2280
+ if (metadata.taskSource?.kind === "files" && metadata.taskSource.path) {
2281
+ return readFileBackedTask(projectRoot, metadata.taskSource.path, id, rawEntry);
2282
+ }
2283
+ return allowLocalFallback ? legacy.getTask(id) : null;
2284
+ }
2285
+ };
2286
+ }
2287
+ function readMaterializedTaskMetadata(entry) {
2288
+ const rawRig = entry._rig;
2289
+ if (!isPlainRecord2(rawRig)) {
2290
+ return {};
2291
+ }
2292
+ const rawSource = rawRig.taskSource;
2293
+ const metadata = {};
2294
+ if (isPlainRecord2(rawSource)) {
2295
+ const kind = typeof rawSource.kind === "string" ? rawSource.kind : "";
2296
+ if (kind.length > 0) {
2297
+ metadata.taskSource = {
2298
+ kind,
2299
+ ...typeof rawSource.path === "string" ? { path: rawSource.path } : {},
2300
+ ...typeof rawSource.owner === "string" ? { owner: rawSource.owner } : {},
2301
+ ...typeof rawSource.repo === "string" ? { repo: rawSource.repo } : {},
2302
+ ...Array.isArray(rawSource.labels) ? { labels: rawSource.labels.filter((label) => typeof label === "string") } : {},
2303
+ ...rawSource.state === "open" || rawSource.state === "closed" || rawSource.state === "all" ? { state: rawSource.state } : {}
2304
+ };
2305
+ }
2306
+ }
2307
+ if (typeof rawRig.sourceIssueId === "string") {
2308
+ metadata.sourceIssueId = rawRig.sourceIssueId;
2309
+ }
2310
+ return metadata;
2311
+ }
2312
+ function readConfiguredFilesTaskSourcePath(projectRoot) {
2313
+ const jsonPath = resolve14(projectRoot, "rig.config.json");
2314
+ if (existsSync13(jsonPath)) {
2315
+ try {
2316
+ const parsed = JSON.parse(readFileSync6(jsonPath, "utf8"));
2317
+ if (isPlainRecord2(parsed) && isPlainRecord2(parsed.taskSource)) {
2318
+ const source = parsed.taskSource;
2319
+ return source.kind === "files" && typeof source.path === "string" ? source.path : null;
2320
+ }
2321
+ } catch {
2322
+ return null;
2323
+ }
2324
+ }
2325
+ const tsPath = resolve14(projectRoot, "rig.config.ts");
2326
+ if (!existsSync13(tsPath)) {
2327
+ return null;
2328
+ }
2329
+ try {
2330
+ const source = readFileSync6(tsPath, "utf8");
2331
+ const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
2332
+ const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
2333
+ if (kind !== "files") {
2334
+ return null;
2335
+ }
2336
+ return taskSourceBlock.match(/path\s*:\s*["']([^"']+)["']/)?.[1] ?? null;
2337
+ } catch {
2338
+ return null;
2339
+ }
2340
+ }
2341
+ function readRawTaskEntry(configPath, taskId) {
2342
+ const rawConfig = readRawTaskConfig(configPath);
2343
+ if (!rawConfig) {
2344
+ return null;
2345
+ }
2346
+ const entry = stripLegacyTaskConfigMetadata2(rawConfig)[taskId];
2347
+ return isPlainRecord2(entry) ? entry : null;
2348
+ }
2349
+ function readRawTaskConfig(configPath) {
2350
+ if (!existsSync13(configPath)) {
2351
+ return null;
2352
+ }
2353
+ const parsed = JSON.parse(readFileSync6(configPath, "utf8"));
2354
+ return isPlainRecord2(parsed) ? parsed : null;
2355
+ }
2356
+ function stripLegacyTaskConfigMetadata2(raw) {
2357
+ const { validation_descriptions: _legacyDescriptions, _meta, ...tasks } = raw;
2358
+ return tasks;
2359
+ }
2360
+ function listFileBackedTasks(projectRoot, sourcePath) {
2361
+ const directory = resolve14(projectRoot, sourcePath);
2362
+ if (!existsSync13(directory)) {
2363
+ return [];
2364
+ }
2365
+ const tasks = [];
2366
+ for (const name of readdirSync2(directory)) {
2367
+ if (!FILE_TASK_PATTERN.test(name))
2368
+ continue;
2369
+ const inferredId = basename5(name).replace(FILE_TASK_PATTERN, "");
2370
+ const task = readFileBackedTask(projectRoot, sourcePath, inferredId, {});
2371
+ if (task)
2372
+ tasks.push(task);
2373
+ }
2374
+ return tasks;
2375
+ }
2376
+ function readFileBackedTask(projectRoot, sourcePath, taskId, rawEntry) {
2377
+ const file = findFileBackedTaskFile(resolve14(projectRoot, sourcePath), taskId);
2378
+ if (!file) {
2379
+ return null;
2380
+ }
2381
+ const raw = JSON.parse(readFileSync6(file, "utf8"));
2382
+ if (!isPlainRecord2(raw)) {
2383
+ return null;
2384
+ }
2385
+ return {
2386
+ id: typeof raw.id === "string" ? raw.id : taskId,
2387
+ deps: Array.isArray(raw.deps) ? raw.deps : Array.isArray(raw.depends_on) ? raw.depends_on : [],
2388
+ status: typeof raw.status === "string" ? raw.status : "ready",
2389
+ title: typeof raw.title === "string" ? raw.title : typeof rawEntry.title === "string" ? rawEntry.title : taskId,
2390
+ ...raw
2391
+ };
2392
+ }
2393
+ function findFileBackedTaskFile(directory, taskId) {
2394
+ if (!existsSync13(directory)) {
2395
+ return null;
2396
+ }
2397
+ for (const name of readdirSync2(directory)) {
2398
+ if (!FILE_TASK_PATTERN.test(name))
2399
+ continue;
2400
+ const file = join4(directory, name);
2401
+ try {
2402
+ if (!statSync3(file).isFile())
2403
+ continue;
2404
+ const raw = JSON.parse(readFileSync6(file, "utf8"));
2405
+ const inferredId = basename5(file).replace(FILE_TASK_PATTERN, "");
2406
+ const id = isPlainRecord2(raw) && typeof raw.id === "string" ? raw.id : inferredId;
2407
+ if (id === taskId) {
2408
+ return file;
2409
+ }
2410
+ } catch {}
2411
+ }
2412
+ return null;
2413
+ }
2414
+ function readGithubIssueTask(bin, spawnFn, id, metadata, rawEntry) {
2415
+ const source = requireGithubIssueSource(metadata, id);
2416
+ const issue = runGh(bin, [
2417
+ "issue",
2418
+ "view",
2419
+ String(id),
2420
+ "--repo",
2421
+ `${source.owner}/${source.repo}`,
2422
+ "--json",
2423
+ "number,title,body,labels,state,url,assignees"
2424
+ ], spawnFn);
2425
+ return githubIssueToTask(issue, source, rawEntry);
2426
+ }
2427
+ function requireGithubIssueSource(metadata, id) {
2428
+ const source = metadata.taskSource;
2429
+ if (source?.kind === "github-issues" && source.owner && source.repo) {
2430
+ return { owner: source.owner, repo: source.repo };
2431
+ }
2432
+ const parsed = metadata.sourceIssueId?.match(/^([^/]+)\/([^#]+)#(\d+)$/);
2433
+ if (parsed && parsed[3] === id) {
2434
+ return { owner: parsed[1], repo: parsed[2] };
2435
+ }
2436
+ throw new Error(`Task ${id} is marked as github-issues but has no owner/repo source metadata`);
2437
+ }
2438
+ function githubIssueToTask(issue, source, rawEntry) {
2439
+ const labelNames = (issue.labels ?? []).map((label) => label.name);
2440
+ const scope = labelNames.filter((label) => label.startsWith("scope:")).map((label) => label.slice("scope:".length));
2441
+ const roleLabel = labelNames.find((label) => label.startsWith("role:"));
2442
+ const validators = labelNames.filter((label) => label.startsWith("validator:")).map((label) => label.slice("validator:".length));
2443
+ const body = issue.body ?? "";
2444
+ const repo = `${source.owner}/${source.repo}`;
2445
+ return {
2446
+ id: String(issue.number),
2447
+ deps: parseDeps(body),
2448
+ status: githubStatusFor(issue),
2449
+ title: issue.title,
2450
+ body,
2451
+ ...scope.length > 0 ? { scope } : {},
2452
+ ...roleLabel ? { role: roleLabel.slice("role:".length) } : typeof rawEntry.role === "string" ? { role: rawEntry.role } : {},
2453
+ ...validators.length > 0 ? { validators } : {},
2454
+ ...issue.url ? { url: issue.url } : {},
2455
+ issueType: issueTypeFor(labelNames),
2456
+ sourceIssueId: `${repo}#${issue.number}`,
2457
+ parentChildDeps: parseParents(body),
2458
+ labels: labelNames,
2459
+ raw: issue,
2460
+ source: "github-issues",
2461
+ _rig: {
2462
+ taskSource: { kind: "github-issues", owner: source.owner, repo: source.repo },
2463
+ sourceIssueId: `${repo}#${issue.number}`
2464
+ }
2465
+ };
2466
+ }
2467
+ function githubStatusFor(issue) {
2468
+ const state = (issue.state ?? "").toUpperCase();
2469
+ if (state === "CLOSED")
2470
+ return "closed";
2471
+ const labelNames = (issue.labels ?? []).map((label) => label.name);
2472
+ if (labelNames.includes("in-progress"))
2473
+ return "in_progress";
2474
+ if (labelNames.includes("blocked"))
2475
+ return "blocked";
2476
+ if (labelNames.includes("ready"))
2477
+ return "ready";
2478
+ if (labelNames.includes("under-review"))
2479
+ return "under_review";
2480
+ if (labelNames.includes("failed"))
2481
+ return "failed";
2482
+ if (labelNames.includes("cancelled"))
2483
+ return "cancelled";
2484
+ return "open";
2485
+ }
2486
+ function selectedGitHubEnv() {
2487
+ const token = process.env.RIG_GITHUB_SELECTED_TOKEN?.trim() ?? "";
2488
+ return { GH_TOKEN: token, GITHUB_TOKEN: token };
2489
+ }
2490
+ function ghSpawnOptions() {
2491
+ return { encoding: "utf-8", env: { ...process.env, ...selectedGitHubEnv() } };
2492
+ }
2493
+ function runGh(bin, args, spawnFn) {
2494
+ const res = spawnFn(bin, [...args], ghSpawnOptions());
2495
+ assertGhSuccess(args, res);
2496
+ if (!res.stdout || res.stdout.trim() === "") {
2497
+ throw new Error(`gh ${args.join(" ")} returned empty stdout`);
2498
+ }
2499
+ return JSON.parse(res.stdout);
2500
+ }
2501
+ function assertGhSuccess(args, res) {
2502
+ if (res.error) {
2503
+ const msg = res.error.message ?? String(res.error);
2504
+ throw new Error(`gh CLI not available \u2014 install gh (brew install gh / apt install gh): ${msg}`);
2505
+ }
2506
+ if (res.status !== 0) {
2507
+ throw new Error(`gh ${args.join(" ")} failed (exit ${res.status}): ${res.stderr}`);
2508
+ }
2509
+ }
2510
+ function parseDeps(body) {
2511
+ return parseIssueRefs(body, /^depends-on:\s*([^\n]+)/im);
2512
+ }
2513
+ function parseParents(body) {
2514
+ return parseIssueRefs(body, /^parents?:\s*([^\n]+)/im);
2515
+ }
2516
+ function parseIssueRefs(body, pattern) {
2517
+ const match = body.match(pattern);
2518
+ if (!match)
2519
+ return [];
2520
+ return match[1].split(",").map((value) => value.trim()).map((value) => value.replace(/^#/, "").match(/^(\d+)/)?.[1] ?? "").filter((value) => value.length > 0);
2521
+ }
2522
+ function issueTypeFor(labels) {
2523
+ const typed = labels.find((label) => label.startsWith("type:"));
2524
+ if (typed)
2525
+ return typed.slice("type:".length);
2526
+ if (labels.includes("epic"))
2527
+ return "epic";
2528
+ return "task";
2529
+ }
2530
+ function isPlainRecord2(candidate) {
2531
+ return typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
2532
+ }
2533
+
2534
+ // packages/runtime/src/control-plane/tasks/source-lifecycle.ts
2535
+ function hasRunnableTaskSource(source) {
2536
+ return Boolean(source && typeof source === "object" && !Array.isArray(source));
2537
+ }
2538
+ async function getPluginTask(projectRoot, taskId) {
2539
+ const ctx = await buildPluginHostContext(projectRoot);
2540
+ const [source] = ctx?.taskSourceRegistry.list() ?? [];
2541
+ if (!hasRunnableTaskSource(source)) {
2542
+ return ctx ? { configured: false, sourceKind: null, task: null } : null;
2543
+ }
2544
+ const task = source.get ? await source.get(taskId) ?? null : (await source.list()).find((entry) => entry.id === taskId) ?? null;
2545
+ return {
2546
+ configured: true,
2547
+ sourceKind: source.kind,
2548
+ task
2549
+ };
2550
+ }
2551
+ async function readConfiguredTaskSourceTask(projectRoot, taskId) {
2552
+ const pluginResult = await getPluginTask(projectRoot, taskId);
2553
+ if (pluginResult)
2554
+ return pluginResult;
2555
+ const task = await createSourceAwareTaskConfigRecordReader(projectRoot).getTask(taskId);
2556
+ return {
2557
+ configured: false,
2558
+ sourceKind: null,
2559
+ task
2560
+ };
2561
+ }
2562
+
2563
+ // packages/runtime/src/control-plane/native/task-state.ts
2564
+ import { existsSync as existsSync17, readFileSync as readFileSync9, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
2565
+ import { basename as basename6, resolve as resolve18 } from "path";
2566
+ // packages/runtime/src/control-plane/state-sync/read.ts
2567
+ import { existsSync as existsSync16, readFileSync as readFileSync8 } from "fs";
2568
+ import { resolve as resolve17 } from "path";
2569
+
2570
+ // packages/runtime/src/control-plane/native/git-native.ts
2571
+ import { chmodSync as chmodSync2, copyFileSync as copyFileSync3, existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
2572
+ import { tmpdir as tmpdir4 } from "os";
2573
+ import { dirname as dirname10, isAbsolute, resolve as resolve15 } from "path";
2574
+ import { createHash as createHash2 } from "crypto";
2575
+ var sharedGitNativeOutputDir = resolve15(tmpdir4(), "rig-native");
2576
+ var sharedGitNativeOutputPath = resolve15(sharedGitNativeOutputDir, `rig-git-${process.platform}-${process.arch}${process.platform === "win32" ? ".exe" : ""}`);
2577
+ var trackerCommandUsageProbe = "usage: rig-git fetch-ref <repo-path> <remote> <branch>";
2578
+ function temporaryGitBinaryOutputPath(outputPath) {
2579
+ const suffix2 = process.platform === "win32" ? ".exe" : "";
2580
+ return resolve15(dirname10(outputPath), `.rig-git-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}${suffix2}`);
2581
+ }
2582
+ function publishGitBinary(tempOutputPath, outputPath) {
2583
+ try {
2584
+ renameSync2(tempOutputPath, outputPath);
2585
+ } catch (error) {
2586
+ if (process.platform === "win32" && existsSync14(outputPath)) {
2587
+ rmSync3(outputPath, { force: true });
2588
+ renameSync2(tempOutputPath, outputPath);
2589
+ return;
2590
+ }
2591
+ throw error;
2592
+ }
2593
+ }
2594
+ function runtimeRigGitFileName() {
2595
+ return `rig-git${process.platform === "win32" ? ".exe" : ""}`;
2596
+ }
2597
+ function rigGitSourceCandidates() {
2598
+ const execDir = process.execPath?.trim() ? dirname10(process.execPath.trim()) : "";
2599
+ const cwd = process.cwd()?.trim() || "";
2600
+ const projectRoot = process.env.PROJECT_RIG_ROOT?.trim() || "";
2601
+ const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || "";
2602
+ const moduleRelativeSource = resolve15(import.meta.dir, "../../../native/rig-git.zig");
2603
+ return [...new Set([
2604
+ process.env.RIG_NATIVE_GIT_SOURCE?.trim() || "",
2605
+ moduleRelativeSource,
2606
+ projectRoot ? resolve15(projectRoot, "packages/runtime/native/rig-git.zig") : "",
2607
+ hostProjectRoot ? resolve15(hostProjectRoot, "packages/runtime/native/rig-git.zig") : "",
2608
+ cwd ? resolve15(cwd, "packages/runtime/native/rig-git.zig") : "",
2609
+ execDir ? resolve15(execDir, "..", "..", "packages/runtime/native/rig-git.zig") : "",
2610
+ execDir ? resolve15(execDir, "..", "native", "rig-git.zig") : ""
2611
+ ].filter(Boolean))];
2612
+ }
2613
+ function nativePackageBinaryCandidates(fromDir, fileName) {
2614
+ const candidates = [];
2615
+ let cursor = resolve15(fromDir);
2616
+ for (let index = 0;index < 8; index += 1) {
2617
+ candidates.push(resolve15(cursor, "native", `${process.platform}-${process.arch}`, fileName), resolve15(cursor, "native", `${process.platform}-${process.arch}`, "bin", fileName), resolve15(cursor, "native", fileName), resolve15(cursor, "native", "bin", fileName));
2618
+ const parent = dirname10(cursor);
2619
+ if (parent === cursor)
2620
+ break;
2621
+ cursor = parent;
2622
+ }
2623
+ return candidates;
2624
+ }
2625
+ function rigGitBinaryCandidates() {
2626
+ const execDir = process.execPath?.trim() ? dirname10(process.execPath.trim()) : "";
2627
+ const fileName = runtimeRigGitFileName();
2628
+ const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2629
+ return [...new Set([
2630
+ explicit,
2631
+ ...nativePackageBinaryCandidates(import.meta.dir, fileName),
2632
+ execDir ? resolve15(execDir, fileName) : "",
2633
+ execDir ? resolve15(execDir, "..", fileName) : "",
2634
+ execDir ? resolve15(execDir, "..", "bin", fileName) : "",
2635
+ sharedGitNativeOutputPath
2636
+ ].filter(Boolean))];
2637
+ }
2638
+ function resolveGitSourcePath() {
2639
+ for (const candidate of rigGitSourceCandidates()) {
2640
+ if (candidate && existsSync14(candidate)) {
2641
+ return candidate;
2642
+ }
2643
+ }
2644
+ return null;
2645
+ }
2646
+ function resolveGitBinaryPath() {
2647
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2648
+ return null;
2649
+ }
2650
+ for (const candidate of rigGitBinaryCandidates()) {
2651
+ if (candidate && existsSync14(candidate)) {
2652
+ return candidate;
2653
+ }
2654
+ }
2655
+ return null;
2656
+ }
2657
+ function preferredGitBinaryOutputPath() {
2658
+ const explicit = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2659
+ return explicit || sharedGitNativeOutputPath;
2660
+ }
2661
+ function binarySupportsTrackerCommandsSync(binaryPath) {
2662
+ try {
2663
+ const probe = Bun.spawnSync([binaryPath, "fetch-ref", "."], {
2664
+ stdout: "pipe",
2665
+ stderr: "pipe"
2666
+ });
2667
+ const stdout = probe.stdout.toString().trim();
2668
+ const stderr = probe.stderr.toString().trim();
2669
+ if (stdout.includes('"error":"unknown command"')) {
2670
+ return false;
2671
+ }
2672
+ return probe.exitCode === 2 && stderr.includes(trackerCommandUsageProbe);
2673
+ } catch {
2674
+ return false;
2675
+ }
2676
+ }
2677
+ function nativeBuildManifestPath(outputPath) {
2678
+ return `${outputPath}.build-manifest.json`;
2679
+ }
2680
+ function hasMatchingNativeBuildManifestSync(manifestPath, buildKey) {
2681
+ if (!existsSync14(manifestPath)) {
2682
+ return false;
2683
+ }
2684
+ try {
2685
+ const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
2686
+ return manifest.version === 1 && manifest.buildKey === buildKey;
2687
+ } catch {
2688
+ return false;
2689
+ }
2690
+ }
2691
+ function sha256FileSync(path) {
2692
+ return createHash2("sha256").update(readFileSync7(path)).digest("hex");
2693
+ }
2694
+ function ensureRigGitBinaryPathSync(outputPath = preferredGitBinaryOutputPath()) {
2695
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2696
+ throw new Error("Zig native git is disabled via RIG_DISABLE_ZIG_NATIVE=1");
2697
+ }
2698
+ const sourcePath = resolveGitSourcePath();
2699
+ if (!sourcePath) {
2700
+ const binaryPath = resolveGitBinaryPath();
2701
+ if (binaryPath) {
2702
+ return binaryPath;
2703
+ }
2704
+ throw new Error("rig-git.zig source file not found.");
2705
+ }
2706
+ const zigBinary = Bun.which("zig");
2707
+ if (!zigBinary) {
2708
+ throw new Error("zig is required to build native Rig git tools.");
2709
+ }
2710
+ mkdirSync9(dirname10(outputPath), { recursive: true });
2711
+ const sourceDigest = sha256FileSync(sourcePath);
2712
+ const buildKey = JSON.stringify({
2713
+ version: 1,
2714
+ zigBinary,
2715
+ platform: process.platform,
2716
+ arch: process.arch,
2717
+ sourcePath,
2718
+ sourceDigest
2719
+ });
2720
+ const manifestPath = nativeBuildManifestPath(outputPath);
2721
+ const needsBuild = !existsSync14(outputPath) || !hasMatchingNativeBuildManifestSync(manifestPath, buildKey) || !binarySupportsTrackerCommandsSync(outputPath);
2722
+ if (!needsBuild) {
2723
+ chmodSync2(outputPath, 493);
2724
+ return outputPath;
2725
+ }
2726
+ const tempOutputPath = temporaryGitBinaryOutputPath(outputPath);
2727
+ const build = Bun.spawnSync([
2728
+ zigBinary,
2729
+ "build-exe",
2730
+ sourcePath,
2731
+ "-O",
2732
+ "ReleaseFast",
2733
+ `-femit-bin=${tempOutputPath}`
2734
+ ], {
2735
+ cwd: dirname10(sourcePath),
2736
+ stdout: "pipe",
2737
+ stderr: "pipe"
2738
+ });
2739
+ if (build.exitCode !== 0 || !existsSync14(tempOutputPath)) {
2740
+ const stderr = build.stderr.toString().trim();
2741
+ const stdout = build.stdout.toString().trim();
2742
+ const details = [stderr, stdout].filter(Boolean).join(`
2743
+ `);
2744
+ throw new Error(`Failed to build native Rig git tools: ${details || `zig exited with code ${build.exitCode}`}`);
2745
+ }
2746
+ chmodSync2(tempOutputPath, 493);
2747
+ if (existsSync14(outputPath) && hasMatchingNativeBuildManifestSync(manifestPath, buildKey)) {
2748
+ rmSync3(tempOutputPath, { force: true });
2749
+ chmodSync2(outputPath, 493);
2750
+ return outputPath;
2751
+ }
2752
+ publishGitBinary(tempOutputPath, outputPath);
2753
+ if (!binarySupportsTrackerCommandsSync(outputPath)) {
2754
+ rmSync3(outputPath, { force: true });
2755
+ throw new Error("Failed to build native Rig git tools: tracker command probe failed");
2756
+ }
2757
+ writeFileSync6(manifestPath, `${JSON.stringify({ version: 1, buildKey }, null, 2)}
2758
+ `, "utf8");
2759
+ return outputPath;
2760
+ }
2761
+ function runGitNative(command, args) {
2762
+ if (process.env.RIG_DISABLE_ZIG_NATIVE === "1") {
2763
+ return { ok: false, error: "rig-git native disabled" };
2764
+ }
2765
+ const trackerCommand = command === "fetch-ref" || command === "read-blob-at-ref" || command === "write-tree-commit" || command === "push-ref-with-lease";
2766
+ let binaryPath = null;
2767
+ if (trackerCommand) {
2768
+ try {
2769
+ binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
2770
+ } catch (error) {
2771
+ const message = error instanceof Error ? error.message : String(error);
2772
+ if (message.includes("rig-git.zig source file not found")) {
2773
+ return { ok: false, error: "rig-git binary not found" };
2774
+ }
2775
+ return { ok: false, error: message };
2776
+ }
2777
+ } else {
2778
+ const explicitBinaryPath = process.env.RIG_NATIVE_GIT_BIN?.trim() || "";
2779
+ binaryPath = explicitBinaryPath && existsSync14(explicitBinaryPath) ? explicitBinaryPath : !explicitBinaryPath ? resolveGitBinaryPath() : null;
2780
+ if (!binaryPath) {
2781
+ try {
2782
+ binaryPath = ensureRigGitBinaryPathSync(preferredGitBinaryOutputPath());
2783
+ } catch (error) {
2784
+ const message = error instanceof Error ? error.message : String(error);
2785
+ if (message.includes("rig-git.zig source file not found")) {
2786
+ return { ok: false, error: "rig-git binary not found" };
2787
+ }
2788
+ return { ok: false, error: message };
2789
+ }
2790
+ }
2791
+ }
2792
+ try {
2793
+ const proc = Bun.spawnSync([binaryPath, command, ...args], {
2794
+ stdout: "pipe",
2795
+ stderr: "pipe",
2796
+ env: process.env
2797
+ });
2798
+ if (proc.exitCode !== 0) {
2799
+ const stdoutText = proc.stdout.toString().trim();
2800
+ if (stdoutText) {
2801
+ try {
2802
+ const parsed = JSON.parse(stdoutText);
2803
+ if (!parsed.ok) {
2804
+ return parsed;
2805
+ }
2806
+ } catch {}
2807
+ }
2808
+ const errText = proc.stderr.toString().trim() || `exit code ${proc.exitCode}`;
2809
+ return { ok: false, error: errText };
2810
+ }
2811
+ const output = proc.stdout.toString().trim();
2812
+ return JSON.parse(output);
2813
+ } catch (err) {
2814
+ return { ok: false, error: String(err) };
2815
+ }
2816
+ }
2817
+ function requireGitNative(command, args) {
2818
+ const result = runGitNative(command, args);
2819
+ if (!result.ok) {
2820
+ throw new Error(`rig-git ${command} failed: ${result.error}`);
2821
+ }
2822
+ return result;
2823
+ }
2824
+ function requireGitNativeString(command, args) {
2825
+ const result = requireGitNative(command, args);
2826
+ if ("value" in result && typeof result.value === "string") {
2827
+ return result.value;
2828
+ }
2829
+ throw new Error(`rig-git ${command} returned an unexpected result payload`);
2830
+ }
2831
+ function nativeFetchRef(repoPath, remote, branch) {
2832
+ return requireGitNativeString("fetch-ref", [repoPath, remote, branch]);
2833
+ }
2834
+ function nativeReadBlobAtRef(repoPath, ref, path) {
2835
+ const requestDir = resolve15(sharedGitNativeOutputDir, "reads", `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`);
2836
+ mkdirSync9(requestDir, { recursive: true });
2837
+ const outputPath = resolve15(requestDir, "blob.txt");
2838
+ try {
2839
+ requireGitNative("read-blob-at-ref", [repoPath, ref, path, outputPath]);
2840
+ return readFileSync7(outputPath, "utf8");
2841
+ } finally {
2842
+ rmSync3(requestDir, { recursive: true, force: true });
2843
+ }
2844
+ }
2845
+
2846
+ // packages/runtime/src/control-plane/state-sync/repo.ts
2847
+ import { existsSync as existsSync15 } from "fs";
2848
+ import { resolve as resolve16 } from "path";
2849
+ function resolveTrackerRepoPath(projectRoot) {
2850
+ const monorepoRoot = resolveMonorepoRoot2(projectRoot);
2851
+ try {
2852
+ const layout = resolveMonorepoRepoLayout(projectRoot);
2853
+ if (existsSync15(resolve16(layout.mirrorRoot, "HEAD"))) {
2854
+ return layout.mirrorRoot;
2855
+ }
2856
+ } catch {}
2857
+ return monorepoRoot;
2858
+ }
2859
+
2860
+ // packages/runtime/src/control-plane/state-sync/read.ts
2861
+ var DEFAULT_READ_DEPS = {
2862
+ fetchRef: nativeFetchRef,
2863
+ readBlobAtRef: nativeReadBlobAtRef,
2864
+ exists: existsSync16,
2865
+ readFile: (path) => readFileSync8(path, "utf8")
2866
+ };
2867
+ function parseIssueStatus(rawStatus) {
2868
+ const normalized = normalizeTaskLifecycleStatus(rawStatus);
2869
+ return normalized ?? "unknown";
2870
+ }
2871
+ function parseIssueDependencies(raw) {
2872
+ if (!Array.isArray(raw)) {
2873
+ return [];
2874
+ }
2875
+ return raw.filter((entry) => !!entry && typeof entry === "object" && !Array.isArray(entry)).map((entry) => ({
2876
+ issueId: typeof entry.issue_id === "string" && entry.issue_id.trim() ? entry.issue_id.trim() : null,
2877
+ dependsOnId: typeof entry.depends_on_id === "string" && entry.depends_on_id.trim() ? entry.depends_on_id.trim() : null,
2878
+ id: typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : null,
2879
+ type: typeof entry.type === "string" && entry.type.trim() ? entry.type.trim() : null
2880
+ }));
2881
+ }
2882
+ function parseIssuesJsonl(raw) {
2883
+ const issues = [];
2884
+ for (const line of raw.split(/\r?\n/)) {
2885
+ const trimmed = line.trim();
2886
+ if (!trimmed) {
2887
+ continue;
2888
+ }
2889
+ try {
2890
+ const record = JSON.parse(trimmed);
2891
+ const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : "";
2892
+ if (!id) {
2893
+ continue;
2894
+ }
2895
+ const rawStatus = typeof record.status === "string" && record.status.trim() ? record.status.trim() : null;
2896
+ issues.push({
2897
+ id,
2898
+ title: typeof record.title === "string" && record.title.trim() ? record.title.trim() : null,
2899
+ description: typeof record.description === "string" && record.description.trim() ? record.description.trim() : null,
2900
+ acceptanceCriteria: typeof record.acceptance_criteria === "string" && record.acceptance_criteria.trim() ? record.acceptance_criteria.trim() : typeof record.acceptanceCriteria === "string" && record.acceptanceCriteria.trim() ? record.acceptanceCriteria.trim() : null,
2901
+ issueType: typeof record.issue_type === "string" && record.issue_type.trim() ? record.issue_type.trim() : null,
2902
+ status: parseIssueStatus(rawStatus),
2903
+ rawStatus,
2904
+ priority: typeof record.priority === "number" ? record.priority : null,
2905
+ dependencies: parseIssueDependencies(record.dependencies)
2906
+ });
2907
+ } catch {}
2908
+ }
2909
+ return issues;
2910
+ }
2911
+ function parseTaskStateEnvelope(raw) {
2912
+ if (!raw || !raw.trim()) {
2913
+ return readTaskStateMetadataEnvelope(null);
2914
+ }
2915
+ try {
2916
+ return readTaskStateMetadataEnvelope(JSON.parse(raw));
2917
+ } catch {
2918
+ return readTaskStateMetadataEnvelope(null);
2919
+ }
2920
+ }
2921
+ function readRemoteBlobAtRef(deps, repoPath, ref, path, options) {
2922
+ try {
2923
+ return deps.readBlobAtRef(repoPath, ref, path);
2924
+ } catch (error) {
2925
+ if (options.required) {
2926
+ throw error;
2927
+ }
2928
+ return null;
2929
+ }
2930
+ }
2931
+ function shouldPreferLocalTrackerState(options) {
2932
+ if (!options.allowLocalFallback) {
2933
+ return false;
2934
+ }
2935
+ const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
2936
+ if (!runtimeWorkspace) {
2937
+ return false;
2938
+ }
2939
+ if (process.env.RIG_TASK_RUNTIME_ID?.trim()) {
2940
+ return true;
2941
+ }
2942
+ const runtimeContextPath = process.env[RUNTIME_CONTEXT_ENV]?.trim();
2943
+ if (runtimeContextPath) {
2944
+ return true;
2945
+ }
2946
+ return existsSync16(resolve17(runtimeWorkspace, ".rig", "runtime-context.json"));
2947
+ }
2948
+ function readLocalTrackerState(projectRoot, deps) {
2949
+ const monorepoRoot = resolveMonorepoRoot2(projectRoot);
2950
+ const issuesPath = resolve17(monorepoRoot, ".beads", "issues.jsonl");
2951
+ const taskStatePath = resolve17(monorepoRoot, ".beads", "task-state.json");
2952
+ return projectSyncedTrackerSnapshot({
2953
+ source: "local",
2954
+ issuesBaseOid: null,
2955
+ issuesText: deps.exists(issuesPath) ? deps.readFile(issuesPath) : "",
2956
+ taskStateBaseOid: null,
2957
+ taskStateText: deps.exists(taskStatePath) ? deps.readFile(taskStatePath) : null
2958
+ });
2959
+ }
2960
+ function projectSyncedTrackerSnapshot(input) {
2961
+ if (input.source === "remote" && input.issuesBaseOid && input.taskStateBaseOid && input.issuesBaseOid !== input.taskStateBaseOid) {
2962
+ throw new Error("Remote tracker files must be read from the same fetched base.");
2963
+ }
2964
+ return {
2965
+ source: input.source,
2966
+ baseOid: input.issuesBaseOid ?? input.taskStateBaseOid ?? null,
2967
+ issues: parseIssuesJsonl(input.issuesText),
2968
+ taskState: parseTaskStateEnvelope(input.taskStateText)
2969
+ };
2970
+ }
2971
+ function readSyncedTrackerState(projectRoot, deps = {}, options = {}) {
2972
+ const readDeps = { ...DEFAULT_READ_DEPS, ...deps };
2973
+ const trackerRepoPath = resolveTrackerRepoPath(projectRoot);
2974
+ if (shouldPreferLocalTrackerState(options)) {
2975
+ return readLocalTrackerState(projectRoot, readDeps);
2976
+ }
2977
+ try {
2978
+ const baseOid = readDeps.fetchRef(trackerRepoPath, "origin", "main");
2979
+ return projectSyncedTrackerSnapshot({
2980
+ source: "remote",
2981
+ issuesBaseOid: baseOid,
2982
+ issuesText: readRemoteBlobAtRef(readDeps, trackerRepoPath, baseOid, ".beads/issues.jsonl", {
2983
+ required: true
2984
+ }) ?? "",
2985
+ taskStateBaseOid: baseOid,
2986
+ taskStateText: readRemoteBlobAtRef(readDeps, trackerRepoPath, baseOid, ".beads/task-state.json", {
2987
+ required: false
2988
+ })
2989
+ });
2990
+ } catch (error) {
2991
+ if (!options.allowLocalFallback) {
2992
+ throw error;
2993
+ }
2994
+ return readLocalTrackerState(projectRoot, readDeps);
2995
+ }
2996
+ }
2997
+ // packages/runtime/src/control-plane/state-sync/reconcile.ts
2998
+ var STALE_CLAIM_MS = 24 * 60 * 60 * 1000;
2999
+ // packages/runtime/src/control-plane/native/task-state.ts
3000
+ function readTaskConfig(projectRoot) {
3001
+ const raw = readJsonFile(resolveTaskConfigPath(projectRoot), {});
3002
+ return stripTaskConfigMetadata(raw);
3003
+ }
3004
+ function readSourceTaskConfig(projectRoot) {
3005
+ const raw = readAndSyncSourceTaskConfig(projectRoot);
3006
+ return stripTaskConfigMetadata(raw);
3007
+ }
3008
+ function readValidationDescriptions(projectRoot) {
3009
+ const raw = readJsonFile(resolveTaskConfigPath(projectRoot), {});
3010
+ return readValidationDescriptionMap(raw);
3011
+ }
3012
+ function readSourceValidationDescriptions(projectRoot) {
3013
+ const rootRaw = readJsonFile(resolve18(projectRoot, "rig", "task-config.json"), {});
3014
+ const sourcePath = findSourceTaskConfigPath(projectRoot);
3015
+ const sourceRaw = sourcePath ? readJsonFile(sourcePath, {}) : {};
3016
+ const rootDescriptions = readValidationDescriptionMap(rootRaw);
3017
+ const sourceDescriptions = readValidationDescriptionMap(sourceRaw);
3018
+ return {
3019
+ ...rootDescriptions,
3020
+ ...sourceDescriptions
3021
+ };
3022
+ }
3023
+ function currentTaskId(projectRoot) {
3024
+ const fromEnv = (process.env.RIG_TASK_ID || "").trim();
3025
+ if (fromEnv) {
3026
+ return fromEnv;
3027
+ }
3028
+ const runtimeId = (process.env.RIG_TASK_RUNTIME_ID || "").trim();
3029
+ if (runtimeId.startsWith("task-") && runtimeId.length > "task-".length) {
3030
+ return runtimeId.slice("task-".length);
3031
+ }
3032
+ const workspace = (process.env.RIG_TASK_WORKSPACE || "").trim();
3033
+ const inferredFromWorkspace = inferTaskIdFromRuntimePath(workspace);
3034
+ if (inferredFromWorkspace) {
3035
+ return inferredFromWorkspace;
3036
+ }
3037
+ const inferredFromCwd = inferTaskIdFromRuntimePath(process.cwd());
3038
+ if (inferredFromCwd) {
3039
+ return inferredFromCwd;
3040
+ }
3041
+ try {
3042
+ const paths = resolveHarnessPaths(projectRoot);
3043
+ const session = readJsonFile(paths.sessionPath, {});
3044
+ return session.activeTaskIds?.[0] || "";
3045
+ } catch {
3046
+ return "";
3047
+ }
3048
+ }
3049
+ function readSourceTaskStateMetadata(projectRoot, taskId, lifecycleStatus, snapshot) {
3050
+ const syncedSnapshot = snapshot ?? readSyncedTrackerState(projectRoot);
3051
+ const syncedIssue = syncedSnapshot.issues.find((issue) => issue.id === taskId) ?? null;
3052
+ const syncedLifecycleStatus = syncedIssue && syncedIssue.status !== "unknown" ? syncedIssue.status : readLocalSourceTaskLifecycleStatus(projectRoot, taskId);
3053
+ const metadata = syncedSnapshot.taskState.tasks[taskId] ?? readLocalSourceTaskStateEnvelope(projectRoot).tasks[taskId] ?? null;
3054
+ return discardMismatchedTaskStateMetadata({
3055
+ taskId,
3056
+ lifecycleStatus: syncedLifecycleStatus ?? normalizeTaskLifecycleStatus(lifecycleStatus),
3057
+ metadata
3058
+ });
3059
+ }
3060
+ function readValidationDescriptionMap(raw) {
3061
+ return {
3062
+ ...coerceValidationDescriptions(raw.validation_descriptions),
3063
+ ...coerceValidationDescriptions(readValidationDescriptionsFromMeta(raw._meta))
3064
+ };
3065
+ }
3066
+ function stripTaskConfigMetadata(raw) {
3067
+ const { validation_descriptions: _legacyDescriptions, _meta, ...tasks } = raw;
3068
+ return tasks;
3069
+ }
3070
+ function coerceValidationDescriptions(candidate) {
3071
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
3072
+ return {};
3073
+ }
3074
+ const descriptions = {};
3075
+ for (const [key, value] of Object.entries(candidate)) {
3076
+ if (typeof value === "string" && value.length > 0) {
3077
+ descriptions[key] = value;
3078
+ }
3079
+ }
3080
+ return descriptions;
3081
+ }
3082
+ function readValidationDescriptionsFromMeta(meta) {
3083
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
3084
+ return;
3085
+ }
3086
+ return meta.validation_descriptions;
3087
+ }
3088
+ function readLocalSourceTaskStateEnvelope(projectRoot) {
3089
+ const taskStatePath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "task-state.json");
3090
+ return readTaskStateMetadataEnvelope(readJsonFile(taskStatePath, {}));
3091
+ }
3092
+ function readLocalSourceTaskLifecycleStatus(projectRoot, taskId) {
3093
+ const issuesPath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
3094
+ if (!existsSync17(issuesPath)) {
3095
+ return null;
3096
+ }
3097
+ for (const line of readFileSync9(issuesPath, "utf8").split(/\r?\n/)) {
3098
+ const trimmed = line.trim();
3099
+ if (!trimmed) {
3100
+ continue;
3101
+ }
3102
+ try {
3103
+ const parsed = JSON.parse(trimmed);
3104
+ if (parsed.id === taskId && parsed.issue_type === "task") {
3105
+ return normalizeTaskLifecycleStatus(parsed.status);
3106
+ }
3107
+ } catch {}
3108
+ }
3109
+ return null;
3110
+ }
3111
+ function inferTaskIdFromRuntimePath(path) {
3112
+ if (!path) {
3113
+ return "";
3114
+ }
3115
+ const match = path.match(/\/\.rig\/runtime\/agents\/task-([^/]+)\/worktree(?:\/|$)/) || path.match(/\/\.worktrees\/([^/]+)(?:\/|$)/);
3116
+ const candidate = match?.[1] || "";
3117
+ return candidate.startsWith("bd-") ? candidate : "";
3118
+ }
3119
+ function artifactDirForId(projectRoot, id) {
3120
+ const workspaceDir = process.env.RIG_TASK_WORKSPACE?.trim();
3121
+ if (workspaceDir) {
3122
+ const worktreeArtifacts = resolve18(workspaceDir, "artifacts", id);
3123
+ if (existsSync17(worktreeArtifacts) || existsSync17(resolve18(workspaceDir, "artifacts"))) {
3124
+ return worktreeArtifacts;
3125
+ }
3126
+ }
3127
+ try {
3128
+ const paths = resolveHarnessPaths(projectRoot);
3129
+ return resolve18(paths.artifactsDir, id);
3130
+ } catch {
3131
+ return resolve18(resolveMonorepoRoot2(projectRoot), "artifacts", id);
3132
+ }
3133
+ }
3134
+ function resolveTaskConfigPath(projectRoot) {
3135
+ const paths = resolveHarnessPaths(projectRoot);
3136
+ if (existsSync17(paths.taskConfigPath)) {
3137
+ return paths.taskConfigPath;
3138
+ }
3139
+ for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
3140
+ if (existsSync17(candidate)) {
3141
+ return candidate;
3142
+ }
3143
+ }
3144
+ throw new Error(`Task config missing at ${paths.taskConfigPath}.`);
3145
+ }
3146
+ function findSourceTaskConfigPath(projectRoot) {
3147
+ for (const candidate of sourceTaskConfigCandidates(projectRoot)) {
3148
+ if (existsSync17(candidate)) {
3149
+ return candidate;
3150
+ }
3151
+ }
3152
+ return null;
3153
+ }
3154
+ var FILE_TASK_PATTERN2 = /\.(task\.)?json$/;
3155
+ function readAndSyncSourceTaskConfig(projectRoot) {
3156
+ const sourcePath = findSourceTaskConfigPath(projectRoot);
3157
+ const raw = sourcePath ? readJsonFile(sourcePath, {}) : readConfiguredFileTaskConfig(projectRoot);
3158
+ const synced = synchronizeTaskConfigWithTracker(projectRoot, raw);
3159
+ if (sourcePath && synced.updated) {
3160
+ try {
3161
+ writeFileSync7(sourcePath, `${JSON.stringify(synced.config, null, 2)}
3162
+ `, "utf-8");
3163
+ } catch {}
3164
+ }
3165
+ return synced.config;
3166
+ }
3167
+ function synchronizeTaskConfigWithTracker(projectRoot, rawConfig) {
3168
+ const issues = readSourceIssueRecords(projectRoot);
3169
+ if (issues.length === 0) {
3170
+ return { config: rawConfig, updated: false };
3171
+ }
3172
+ const taskConfig = stripTaskConfigMetadata(rawConfig);
3173
+ const mergedConfig = { ...taskConfig };
3174
+ const validationDescriptions = coerceValidationDescriptions(rawConfig.validation_descriptions);
3175
+ const metaValidationDescriptions = coerceValidationDescriptions(readValidationDescriptionsFromMeta(rawConfig._meta));
3176
+ let updated = false;
3177
+ for (const issue of issues) {
3178
+ if (issue.issueType !== "task") {
3179
+ continue;
3180
+ }
3181
+ if (mergedConfig[issue.id] && !shouldRefreshAutoSyncedTaskConfigEntry(mergedConfig[issue.id])) {
3182
+ continue;
3183
+ }
3184
+ mergedConfig[issue.id] = buildAutoSyncedTaskConfigEntry(issue);
3185
+ updated = true;
3186
+ }
3187
+ return {
3188
+ config: {
3189
+ ...mergedConfig,
3190
+ ...Object.keys(validationDescriptions).length > 0 ? { validation_descriptions: validationDescriptions } : {},
3191
+ ...Object.keys(metaValidationDescriptions).length > 0 ? { _meta: { validation_descriptions: metaValidationDescriptions } } : {}
3192
+ },
3193
+ updated
3194
+ };
3195
+ }
3196
+ function shouldRefreshAutoSyncedTaskConfigEntry(entry) {
3197
+ if (!entry || typeof entry !== "object") {
3198
+ return false;
3199
+ }
3200
+ const candidate = entry;
3201
+ if (!candidate.auto_synced) {
3202
+ return false;
3203
+ }
3204
+ if (!Array.isArray(candidate.scope) || candidate.scope.length === 0) {
3205
+ return true;
3206
+ }
3207
+ if (candidate.scope.some((glob) => typeof glob !== "string" || glob.trim().length === 0)) {
3208
+ return true;
3209
+ }
3210
+ return !candidate.role;
3211
+ }
3212
+ function readSourceIssueRecords(projectRoot) {
3213
+ const issuesPath = resolve18(resolveMonorepoRoot2(projectRoot), ".beads", "issues.jsonl");
3214
+ if (!existsSync17(issuesPath)) {
3215
+ return [];
3216
+ }
3217
+ const records = [];
3218
+ for (const line of readFileSync9(issuesPath, "utf-8").split(/\r?\n/)) {
3219
+ const trimmed = line.trim();
3220
+ if (!trimmed) {
3221
+ continue;
3222
+ }
3223
+ try {
3224
+ const parsed = JSON.parse(trimmed);
3225
+ const id = typeof parsed.id === "string" ? parsed.id.trim() : "";
3226
+ if (!id) {
3227
+ continue;
3228
+ }
3229
+ records.push({
3230
+ id,
3231
+ title: typeof parsed.title === "string" ? parsed.title.trim() : "",
3232
+ issueType: typeof parsed.issue_type === "string" ? parsed.issue_type.trim() : null,
3233
+ labels: Array.isArray(parsed.labels) ? parsed.labels.filter((label) => typeof label === "string") : []
3234
+ });
3235
+ } catch {}
3236
+ }
3237
+ return records;
3238
+ }
3239
+ function buildAutoSyncedTaskConfigEntry(issue) {
3240
+ return {
3241
+ auto_synced: true,
3242
+ role: inferAutoSyncedTaskRole(issue),
3243
+ scope: inferAutoSyncedTaskScope(issue),
3244
+ validation: []
3245
+ };
3246
+ }
3247
+ function inferAutoSyncedTaskRole(issue) {
3248
+ for (const label of issue.labels) {
3249
+ if (label === "role:architect")
3250
+ return "architect";
3251
+ if (label === "role:extractor")
3252
+ return "extractor";
3253
+ if (label === "role:mechanic")
3254
+ return "mechanic";
3255
+ if (label === "role:verifier")
3256
+ return "verifier";
3257
+ }
3258
+ if (/\bDESIGN\b/i.test(issue.title)) {
3259
+ return "architect";
3260
+ }
3261
+ if (/\bInitialize\b/i.test(issue.title)) {
3262
+ return "mechanic";
3263
+ }
3264
+ return "extractor";
3265
+ }
3266
+ function inferAutoSyncedTaskScope(issue) {
3267
+ return [`artifacts/${issue.id}/**`];
3268
+ }
3269
+ function readConfiguredFileTaskConfig(projectRoot) {
3270
+ const sourcePath = readConfiguredFilesTaskSourcePath2(projectRoot);
3271
+ if (!sourcePath) {
3272
+ return {};
3273
+ }
3274
+ const directory = resolve18(projectRoot, sourcePath);
3275
+ if (!existsSync17(directory)) {
3276
+ return {};
3277
+ }
3278
+ const config = {};
3279
+ for (const name of readdirSync3(directory)) {
3280
+ if (!FILE_TASK_PATTERN2.test(name))
3281
+ continue;
3282
+ const file = resolve18(directory, name);
3283
+ try {
3284
+ if (!statSync4(file).isFile())
3285
+ continue;
3286
+ const raw = JSON.parse(readFileSync9(file, "utf8"));
3287
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
3288
+ continue;
3289
+ const record = raw;
3290
+ const inferredId = basename6(name).replace(FILE_TASK_PATTERN2, "");
3291
+ const id = typeof record.id === "string" && record.id.trim().length > 0 ? record.id.trim() : inferredId;
3292
+ config[id] = fileTaskToConfigEntry(record, { kind: "files", path: sourcePath });
3293
+ } catch {}
3294
+ }
3295
+ return config;
3296
+ }
3297
+ function fileTaskToConfigEntry(task, source) {
3298
+ const labels = Array.isArray(task.labels) ? task.labels.filter((label) => typeof label === "string") : [];
3299
+ const scope = firstStringList2(task.scope, labels.filter((label) => label.startsWith("scope:")).map((label) => label.slice("scope:".length)));
3300
+ const validation = firstStringList2(task.validation, task.validators, labels.filter((label) => label.startsWith("validator:")).map((label) => label.slice("validator:".length)));
3301
+ const roleLabel = labels.find((label) => label.startsWith("role:"));
3302
+ const role = typeof task.role === "string" && task.role.trim().length > 0 ? task.role.trim() : roleLabel ? roleLabel.slice("role:".length) : undefined;
3303
+ return {
3304
+ auto_synced: true,
3305
+ ...typeof task.title === "string" ? { title: task.title } : {},
3306
+ ...typeof task.status === "string" ? { status: task.status } : {},
3307
+ ...typeof task.description === "string" ? { description: task.description } : {},
3308
+ ...typeof task.acceptance_criteria === "string" ? { acceptance_criteria: task.acceptance_criteria } : typeof task.acceptanceCriteria === "string" ? { acceptance_criteria: task.acceptanceCriteria } : {},
3309
+ ...role ? { role } : {},
3310
+ ...scope.length > 0 ? { scope } : {},
3311
+ ...validation.length > 0 ? { validation } : {},
3312
+ _rig: { taskSource: source }
3313
+ };
3314
+ }
3315
+ function firstStringList2(...candidates) {
3316
+ for (const candidate of candidates) {
3317
+ if (!Array.isArray(candidate)) {
3318
+ continue;
3319
+ }
3320
+ const list = candidate.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
3321
+ if (list.length > 0) {
3322
+ return list;
3323
+ }
3324
+ }
3325
+ return [];
3326
+ }
3327
+ function readConfiguredFilesTaskSourcePath2(projectRoot) {
3328
+ const jsonPath = resolve18(projectRoot, "rig.config.json");
3329
+ if (existsSync17(jsonPath)) {
3330
+ try {
3331
+ const parsed = JSON.parse(readFileSync9(jsonPath, "utf8"));
3332
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
3333
+ const taskSource = parsed.taskSource;
3334
+ if (taskSource && typeof taskSource === "object" && !Array.isArray(taskSource)) {
3335
+ const record = taskSource;
3336
+ return record.kind === "files" && typeof record.path === "string" ? record.path : null;
3337
+ }
3338
+ }
3339
+ } catch {
3340
+ return null;
3341
+ }
3342
+ }
3343
+ const tsPath = resolve18(projectRoot, "rig.config.ts");
3344
+ if (!existsSync17(tsPath)) {
3345
+ return null;
3346
+ }
3347
+ try {
3348
+ const source = readFileSync9(tsPath, "utf8");
3349
+ const taskSourceBlock = source.match(/taskSource\s*:\s*\{[\s\S]*?\}/m)?.[0] ?? "";
3350
+ const kind = taskSourceBlock.match(/kind\s*:\s*["']([^"']+)["']/)?.[1];
3351
+ if (kind !== "files") {
3352
+ return null;
3353
+ }
3354
+ return taskSourceBlock.match(/path\s*:\s*["']([^"']+)["']/)?.[1] ?? null;
3355
+ } catch {
3356
+ return null;
3357
+ }
3358
+ }
3359
+ function sourceTaskConfigCandidates(projectRoot) {
3360
+ const runtimeContext = loadRuntimeContextFromEnv();
3361
+ return [
3362
+ runtimeContext?.monorepoMainRoot ? resolve18(runtimeContext.monorepoMainRoot, ".rig", "task-config.json") : "",
3363
+ process.env.MONOREPO_MAIN_ROOT?.trim() ? resolve18(process.env.MONOREPO_MAIN_ROOT.trim(), ".rig", "task-config.json") : "",
3364
+ resolve18(resolveMonorepoRoot2(projectRoot), ".rig", "task-config.json")
3365
+ ].filter(Boolean);
3366
+ }
3367
+
3368
+ // packages/runtime/src/binary-run.ts
3369
+ var runtimeBinaryBuildQueue = Promise.resolve();
3370
+ // packages/runtime/src/control-plane/native/git-ops.ts
3371
+ var TASK_ARTIFACT_STAGE_FALLBACK = new Set([
3372
+ "changed-files.txt",
3373
+ "contract-changes.md",
3374
+ "decision-log.md",
3375
+ "git-state.txt",
3376
+ "next-actions.md",
3377
+ "pr-state.json",
3378
+ "task-result.json",
3379
+ "validation-summary.json"
3380
+ ]);
3381
+
3382
+ // packages/runtime/src/control-plane/provider/runtime-instructions.ts
3383
+ var CLAUDE_ROUTER_TOOL_NAMES = [
3384
+ "`mcp__rig_runtime_tools__read`",
3385
+ "`mcp__rig_runtime_tools__write`",
3386
+ "`mcp__rig_runtime_tools__edit`",
3387
+ "`mcp__rig_runtime_tools__glob`",
3388
+ "`mcp__rig_runtime_tools__grep`"
3389
+ ].join(", ");
3390
+ var CODEX_DYNAMIC_TOOL_NAMES = [
3391
+ "`shell`",
3392
+ "`read`",
3393
+ "`write`",
3394
+ "`edit`",
3395
+ "`glob`",
3396
+ "`grep`"
3397
+ ].join(", ");
3398
+ function normalizeRuntimeInstructionProvider(value) {
3399
+ const normalized = value?.trim().toLowerCase();
3400
+ if (normalized === "codex" || normalized === "codex-cli" || normalized === "codex-app-server" || normalized === "gpt-codex") {
3401
+ return "codex";
3402
+ }
3403
+ if (normalized === "pi" || normalized === "rig-pi" || normalized === "@rig/pi") {
3404
+ return "pi";
3405
+ }
3406
+ return "claude-code";
3407
+ }
3408
+ function buildProviderRuntimeContextLines(provider) {
3409
+ if (provider === "codex") {
3410
+ return [
3411
+ "Codex file and shell work must go through Rig-owned dynamic tools.",
3412
+ `Use the dynamic tools instead: ${CODEX_DYNAMIC_TOOL_NAMES}.`,
3413
+ "`read`/`glob`/`grep` may inspect the task worktree, `$PROJECT_RIG_ROOT`, and `$MONOREPO_MAIN_ROOT`.",
3414
+ "`write` and `edit` remain restricted to the scoped task workspace.",
3415
+ "Codex shell work must stay inside Rig's gatewayed binaries via the `shell` tool.",
3416
+ "Do not invoke absolute host shell paths like `/bin/bash`, `/bin/zsh`, `/usr/bin/env`, or `/usr/bin/git`.",
3417
+ "Parallel Rig tool calls are supported when each call stays scoped and bounded.",
3418
+ "Never run repo-wide `rg` or `grep` against `.` or without an explicit narrow path; start with a scoped router `glob`/`grep`, `rg --files <dir>`, or pipe through `head` before widening."
3419
+ ];
3420
+ }
3421
+ if (provider === "pi") {
3422
+ return [
3423
+ "Pi runs from the isolated Rig task worktree with its built-in `read`, `write`, `edit`, and `bash` tools enabled.",
3424
+ "Keep writes scoped to the task workspace unless Rig-provided instructions name an explicit metadata path.",
3425
+ "Prefer Pi file tools over shell one-liners for file reads and edits.",
3426
+ "Keep shell commands narrow and bounded; cap broad searches before widening."
3427
+ ];
3428
+ }
3429
+ return [
3430
+ "Claude's built-in Read/Edit/Write/Glob/Grep/NotebookEdit tools are disabled in this runtime.",
3431
+ "Use the Rig-owned router tools instead:",
3432
+ "`read`/`glob`/`grep` may inspect the task worktree, `$PROJECT_RIG_ROOT`, and `$MONOREPO_MAIN_ROOT`.",
3433
+ "`write` and `edit` remain restricted to the scoped task workspace.",
3434
+ "mcp__rig_runtime_tools__read",
3435
+ "mcp__rig_runtime_tools__write",
3436
+ "mcp__rig_runtime_tools__edit",
3437
+ "mcp__rig_runtime_tools__glob",
3438
+ "mcp__rig_runtime_tools__grep",
3439
+ "Use `.rig/bin/bash` or `.rig/bin/sh` only for shell-oriented work.",
3440
+ "Run shell commands serially; parallel Bash calls can lose cwd state inside the isolated worktree."
3441
+ ];
3442
+ }
3443
+
3444
+ // packages/runtime/src/control-plane/native/task-ops.ts
3445
+ var BUILD_CONFIG = readBuildConfig();
3446
+ var BAKED_INFO_OUTPUT = BUILD_CONFIG.AGENT_INFO_OUTPUT ?? "";
3447
+ var BAKED_DEPS_OUTPUT = BUILD_CONFIG.AGENT_DEPS_OUTPUT ?? "";
3448
+ var BAKED_STATUS_OUTPUT = BUILD_CONFIG.AGENT_STATUS_OUTPUT ?? "";
3449
+ var REOPENABLE_TASK_STATUSES = new Set(["completed", "cancelled", "blocked"]);
3450
+ function hasRuntimeWorkspace() {
3451
+ return Boolean(process.env.RIG_TASK_WORKSPACE?.trim());
3452
+ }
3453
+ function readTaskConfigForInvocation(projectRoot) {
3454
+ return hasRuntimeWorkspace() ? readTaskConfig(projectRoot) : readSourceTaskConfig(projectRoot);
3455
+ }
3456
+ function readValidationDescriptionsForInvocation(projectRoot) {
3457
+ return hasRuntimeWorkspace() ? readValidationDescriptions(projectRoot) : readSourceValidationDescriptions(projectRoot);
3458
+ }
3459
+ function readStringList2(candidate) {
3460
+ return Array.isArray(candidate) ? candidate.filter((value) => typeof value === "string") : [];
3461
+ }
3462
+ function taskConfigEntryFromSourceTask(task) {
3463
+ if (!task)
3464
+ return {};
3465
+ const record = task;
3466
+ const description = firstNonEmpty(typeof record.description === "string" ? record.description : undefined, typeof record.body === "string" ? record.body : undefined);
3467
+ const acceptance = firstNonEmpty(typeof record.acceptanceCriteria === "string" ? record.acceptanceCriteria : undefined, typeof record.acceptance_criteria === "string" ? record.acceptance_criteria : undefined);
3468
+ const validation = readStringList2(record.validation).length > 0 ? readStringList2(record.validation) : readStringList2(record.validators);
3469
+ const scope = readStringList2(record.scope);
3470
+ const browser = record.browser && typeof record.browser === "object" && !Array.isArray(record.browser) ? record.browser : undefined;
3471
+ return {
3472
+ ...typeof record.role === "string" ? { role: record.role } : {},
3473
+ ...description ? { description } : {},
3474
+ ...acceptance ? { acceptance_criteria: acceptance } : {},
3475
+ ...scope.length > 0 ? { scope } : {},
3476
+ ...validation.length > 0 ? { validation } : {},
3477
+ ...browser ? { browser } : {}
3478
+ };
3479
+ }
3480
+ function taskMetadataFromSourceTask(task) {
3481
+ if (!task)
3482
+ return null;
3483
+ const record = task;
3484
+ const description = firstNonEmpty(typeof record.description === "string" ? record.description : undefined, typeof record.body === "string" ? record.body : undefined);
3485
+ const acceptanceCriteria = firstNonEmpty(typeof record.acceptanceCriteria === "string" ? record.acceptanceCriteria : undefined, typeof record.acceptance_criteria === "string" ? record.acceptance_criteria : undefined);
3486
+ return {
3487
+ title: typeof record.title === "string" && record.title.trim() ? record.title : task.id,
3488
+ ...typeof record.status === "string" ? { status: record.status } : {},
3489
+ ...typeof record.priority === "number" ? { priority: record.priority } : {},
3490
+ ...description ? { description } : {},
3491
+ ...acceptanceCriteria ? { acceptanceCriteria } : {}
3492
+ };
3493
+ }
3494
+ function sourceTaskDependencyIds(task) {
3495
+ if (!task)
3496
+ return null;
3497
+ const record = task;
3498
+ return unique([
3499
+ ...readStringList2(record.deps),
3500
+ ...readStringList2(record.dependencies)
3501
+ ]).filter((id) => id !== task.id);
3502
+ }
3503
+ async function readTaskSourceRecordForInvocation(projectRoot, taskId) {
3504
+ if (hasRuntimeWorkspace()) {
3505
+ return null;
3506
+ }
3507
+ try {
3508
+ const result = await readConfiguredTaskSourceTask(projectRoot, taskId);
3509
+ return result.task;
3510
+ } catch {
3511
+ return null;
3512
+ }
3513
+ }
3514
+ function runtimeToolSummary() {
3515
+ const available = new Set(runtimeGatewayToolNames());
3516
+ const shell = ["bash", "sh", "python3", "tee", "cp", "mv", "rm", "mkdir", "touch"].filter((tool) => available.has(tool));
3517
+ return { shell, file: runtimeFileToolNames() };
3518
+ }
3519
+ function providerToolReferenceLine(provider) {
3520
+ if (provider === "codex") {
3521
+ return "Dynamic tool names: `shell`, `read`, `write`, `edit`, `glob`, `grep`.";
3522
+ }
3523
+ if (provider === "pi") {
3524
+ return "Pi tool names: `read`, `write`, `edit`, `bash`.";
3525
+ }
3526
+ return "Router tool names: `mcp__rig_runtime_tools__read`, `mcp__rig_runtime_tools__write`, `mcp__rig_runtime_tools__edit`, `mcp__rig_runtime_tools__glob`, `mcp__rig_runtime_tools__grep`.";
3527
+ }
3528
+ async function taskInfo(projectRoot, taskId, runtimeProviderOverride) {
3529
+ if (BAKED_INFO_OUTPUT) {
3530
+ process.stdout.write(BAKED_INFO_OUTPUT);
3531
+ return;
3532
+ }
3533
+ const activeTask = taskId || currentTaskId(projectRoot);
3534
+ if (!activeTask) {
3535
+ throw new Error("No active task. Start one with: rig run start-serial");
3536
+ }
3537
+ const runtimeContext = loadRuntimeContextFromEnv();
3538
+ const sourceTask = runtimeContext?.sourceTask ?? await readTaskSourceRecordForInvocation(projectRoot, activeTask);
3539
+ const tracker = loadReadonlyTaskTrackerContext(projectRoot);
3540
+ const taskConfig = (() => {
3541
+ try {
3542
+ return readTaskConfigForInvocation(projectRoot);
3543
+ } catch {
3544
+ return {};
3545
+ }
3546
+ })();
3547
+ const entry = { ...taskConfig[activeTask] || {}, ...taskConfigEntryFromSourceTask(sourceTask) };
3548
+ const taskMeta = taskMetadataFromSourceTask(sourceTask) ?? await readTaskMetadata(projectRoot, activeTask, tracker);
3549
+ const runtimeProvider = runtimeProviderOverride ?? normalizeRuntimeInstructionProvider(process.env.RIG_RUNTIME_ADAPTER);
3550
+ const description = firstNonEmpty(taskMeta?.description, entry.description);
3551
+ const acceptanceCriteria = firstNonEmpty(taskMeta?.acceptanceCriteria, entry.acceptance_criteria);
3552
+ const browserContext = runtimeContext?.browser ?? resolveTaskBrowserContext(entry.browser, {
3553
+ hostProjectRoot: projectRoot
3554
+ });
3555
+ console.log(`=== Task: ${activeTask} ===`);
3556
+ console.log("");
3557
+ console.log(`Role: ${entry.role || "unassigned"}`);
3558
+ if (taskMeta) {
3559
+ console.log(`Title: ${taskMeta.title}`);
3560
+ if (taskMeta.status) {
3561
+ console.log(`Status: ${taskMeta.status.toUpperCase()}`);
3562
+ }
3563
+ if (taskMeta.claimId) {
3564
+ console.log(`Claim: ${taskMeta.claimId}`);
3565
+ }
3566
+ if (typeof taskMeta.priority === "number") {
3567
+ console.log(`Priority: P${taskMeta.priority}`);
3568
+ }
3569
+ } else {
3570
+ console.log("Title: (task metadata unavailable)");
3571
+ }
3572
+ if (description) {
3573
+ console.log(`
3574
+ Description:`);
3575
+ printIndented(description);
3576
+ } else {
3577
+ console.log('\nDescription: (not set; add one with `br update <task-id> --description "..."`)');
3578
+ }
3579
+ if (acceptanceCriteria) {
3580
+ console.log(`
3581
+ Acceptance Criteria:`);
3582
+ printIndented(acceptanceCriteria);
3583
+ }
3584
+ console.log(`
3585
+ Scope globs:`);
3586
+ for (const scope of entry.scope || []) {
3587
+ console.log(` - ${scope}`);
3588
+ }
3589
+ if (entry.creates_repo) {
3590
+ console.log(`
3591
+ NOTE: This task creates a new repository. The scope directory does not exist yet \u2014 you are scaffolding it from scratch.`);
3592
+ }
3593
+ const valDescriptions = (() => {
3594
+ try {
3595
+ return readValidationDescriptionsForInvocation(projectRoot);
3596
+ } catch {
3597
+ return {};
3598
+ }
3599
+ })();
3600
+ console.log(`
3601
+ Validation:`);
3602
+ for (const cmd of entry.validation || []) {
3603
+ const desc = valDescriptions[cmd];
3604
+ console.log(desc ? ` $ ${cmd} \u2014 ${desc}` : ` $ ${cmd}`);
3605
+ }
3606
+ if (browserContext?.required) {
3607
+ console.log(`
3608
+ Browser:`);
3609
+ for (const line of buildBrowserGuidanceLines(browserContext)) {
3610
+ console.log(` - ${line}`);
3611
+ }
3612
+ }
3613
+ const runtimeTools = runtimeToolSummary();
3614
+ console.log(`
3615
+ Runtime Tools:`);
3616
+ console.log(" - This task runtime provides audited shell tooling and Rig-owned file tools.");
3617
+ console.log(" - Use the Rig-owned router tools for all read, search, edit, and write work inside the task workspace.");
3618
+ for (const line of buildProviderRuntimeContextLines(runtimeProvider)) {
3619
+ console.log(` - ${line}`);
3620
+ }
3621
+ console.log(" - `write` and `edit` remain restricted to the scoped task workspace, plus the task artifact subtree at `artifacts/<taskId>/` for closeout files.");
3622
+ console.log(` - ${providerToolReferenceLine(runtimeProvider)}`);
3623
+ console.log(" - Runtime tool location: `$RIG_TASK_WORKSPACE/.rig/bin`.");
3624
+ if (runtimeTools.file.length > 0) {
3625
+ console.log(` - Runtime file binaries: ${runtimeTools.file.join(", ")}`);
3626
+ }
3627
+ if (runtimeTools.shell.length > 0) {
3628
+ console.log(` - Shell helpers: ${runtimeTools.shell.join(", ")}`);
3629
+ }
3630
+ console.log(" - Shell commands route through `rig-shell`; file-tool binaries enforce runtime workspace and scope boundaries directly.");
3631
+ console.log(`
3632
+ Dependencies:`);
3633
+ const deps = sourceTaskDependencyIds(sourceTask) ?? taskDependencies(projectRoot, activeTask, tracker);
3634
+ if (deps.length === 0) {
3635
+ console.log(" (none - root task)");
3636
+ } else {
3637
+ for (const dep of deps) {
3638
+ const depMeta = readTaskMetadataFromTracker(projectRoot, dep, tracker);
3639
+ const depStatus = depMeta?.status ? `status=${depMeta.status}` : "";
3640
+ console.log(` - ${dep} ${depStatus}`.trim());
3641
+ }
3642
+ }
3643
+ }
3644
+ async function taskDeps(projectRoot, taskId) {
3645
+ if (BAKED_DEPS_OUTPUT) {
3646
+ process.stdout.write(BAKED_DEPS_OUTPUT);
3647
+ return;
3648
+ }
3649
+ const activeTask = taskId || currentTaskId(projectRoot);
3650
+ if (!activeTask) {
3651
+ throw new Error("No active task.");
3652
+ }
3653
+ const sourceTask = loadRuntimeContextFromEnv()?.sourceTask ?? await readTaskSourceRecordForInvocation(projectRoot, activeTask);
3654
+ const tracker = loadReadonlyTaskTrackerContext(projectRoot);
3655
+ const deps = sourceTaskDependencyIds(sourceTask) ?? taskDependencies(projectRoot, activeTask, tracker);
3656
+ if (deps.length === 0) {
3657
+ console.log(`No dependencies for ${activeTask}.`);
3658
+ return;
3659
+ }
3660
+ for (const dep of deps) {
3661
+ const artifactDir = artifactDirForId(projectRoot, dep);
3662
+ console.log(`=== ${dep} ===`);
3663
+ if (!existsSync18(artifactDir)) {
3664
+ console.log(` (no artifacts yet)
3665
+ `);
3666
+ continue;
3667
+ }
3668
+ printArtifactSection(resolve19(artifactDir, "decision-log.md"), "--- Decisions ---");
3669
+ printArtifactSection(resolve19(artifactDir, "next-actions.md"), "--- Next Actions (for you) ---");
3670
+ const changedFiles = resolve19(artifactDir, "changed-files.txt");
3671
+ if (existsSync18(changedFiles)) {
3672
+ const lines = readFileSync10(changedFiles, "utf-8").split(/\r?\n/).filter(Boolean);
3673
+ console.log(`--- Changed Files (${lines.length}) ---`);
3674
+ for (const line of lines) {
3675
+ console.log(line);
3676
+ }
3677
+ console.log("");
3678
+ }
3679
+ }
3680
+ }
3681
+ var GENERATED_TASK_ARTIFACT_FILES = new Set([
3682
+ "changed-files.txt",
3683
+ "decision-log.md",
3684
+ "next-actions.md",
3685
+ "task-result.json",
3686
+ "validation-summary.json",
3687
+ "review-feedback.md",
3688
+ "review-state.json",
3689
+ "review-status.txt",
3690
+ "review-greptile-raw.json",
3691
+ "pr-state.json",
3692
+ "git-state.txt"
3693
+ ]);
3694
+ async function readTaskMetadata(projectRoot, taskId, tracker) {
3695
+ const trackerMetadata = readTaskMetadataFromTracker(projectRoot, taskId, tracker);
3696
+ if (trackerMetadata) {
3697
+ return trackerMetadata;
3698
+ }
3699
+ try {
3700
+ const task = await createSourceAwareTaskConfigRecordReader(projectRoot).getTask(taskId);
3701
+ if (!task) {
3702
+ return null;
3703
+ }
3704
+ const record = task;
3705
+ const title = asNonEmptyString(record.title) || taskId;
3706
+ const metadata = { title };
3707
+ const status = asNonEmptyString(record.status);
3708
+ if (status) {
3709
+ metadata.status = normalizeTaskLifecycleStatus(status) ?? status;
3710
+ }
3711
+ const description = firstNonEmpty(asNonEmptyString(record.description), asNonEmptyString(record.body));
3712
+ if (description)
3713
+ metadata.description = description;
3714
+ const acceptance = firstNonEmpty(asNonEmptyString(record.acceptance_criteria), asNonEmptyString(record.acceptanceCriteria));
3715
+ if (acceptance)
3716
+ metadata.acceptanceCriteria = acceptance;
3717
+ const priority = record.priority;
3718
+ if (typeof priority === "number")
3719
+ metadata.priority = priority;
3720
+ return metadata;
3721
+ } catch {
3722
+ return null;
3723
+ }
3724
+ }
3725
+ function readTaskMetadataFromTracker(projectRoot, taskId, tracker) {
3726
+ const context = tracker ?? loadTaskTrackerContext(projectRoot);
3727
+ const record = context.tasksById.get(taskId);
3728
+ if (!record?.title) {
3729
+ return null;
3730
+ }
3731
+ const metadata = { title: record.title };
3732
+ const status = asNonEmptyString(record.status);
3733
+ if (status) {
3734
+ const normalizedStatus = normalizeTaskLifecycleStatus(status);
3735
+ metadata.status = normalizedStatus ?? status;
3736
+ if (normalizedStatus) {
3737
+ const taskState = readSourceTaskStateMetadata(projectRoot, taskId, normalizedStatus, context.snapshot);
3738
+ if (taskState?.claimId) {
3739
+ metadata.claimId = taskState.claimId;
3740
+ }
3741
+ }
3742
+ }
3743
+ if (typeof record.priority === "number")
3744
+ metadata.priority = record.priority;
3745
+ const description = asNonEmptyString(record.description);
3746
+ if (description)
3747
+ metadata.description = description;
3748
+ const acceptance = firstNonEmpty(asNonEmptyString(record.acceptance_criteria), asNonEmptyString(record.acceptanceCriteria));
3749
+ if (acceptance)
3750
+ metadata.acceptanceCriteria = acceptance;
3751
+ return metadata;
3752
+ }
3753
+ function asNonEmptyString(value) {
3754
+ if (typeof value !== "string") {
3755
+ return "";
3756
+ }
3757
+ return value.trim();
3758
+ }
3759
+ function firstNonEmpty(...values) {
3760
+ for (const value of values) {
3761
+ if (typeof value === "string" && value.trim()) {
3762
+ return value.trim();
3763
+ }
3764
+ }
3765
+ return "";
3766
+ }
3767
+ function printIndented(text) {
3768
+ for (const line of text.split(/\r?\n/)) {
3769
+ console.log(` ${line}`);
3770
+ }
3771
+ }
3772
+ function readLocalBeadsTasks(projectRoot) {
3773
+ const issuesPath = resolve19(resolveMonorepoRoot2(projectRoot), ".beads/issues.jsonl");
3774
+ if (!existsSync18(issuesPath)) {
3775
+ return [];
3776
+ }
3777
+ const tasks = [];
3778
+ for (const line of readFileSync10(issuesPath, "utf-8").split(/\r?\n/)) {
3779
+ const trimmed = line.trim();
3780
+ if (!trimmed) {
3781
+ continue;
3782
+ }
3783
+ try {
3784
+ const parsed = JSON.parse(trimmed);
3785
+ if (parsed.issue_type === "task" && parsed.id) {
3786
+ tasks.push({
3787
+ id: parsed.id,
3788
+ status: parsed.status || "open",
3789
+ issue_type: parsed.issue_type,
3790
+ title: parsed.title,
3791
+ priority: parsed.priority,
3792
+ description: parsed.description,
3793
+ acceptance_criteria: parsed.acceptance_criteria,
3794
+ acceptanceCriteria: parsed.acceptanceCriteria,
3795
+ dependencies: Array.isArray(parsed.dependencies) ? parsed.dependencies : []
3796
+ });
3797
+ }
3798
+ } catch {}
3799
+ }
3800
+ return tasks;
3801
+ }
3802
+ function readBeadsTasks(projectRoot, tracker, allowLocalFallback = false) {
3803
+ return (tracker ?? loadTaskTrackerContext(projectRoot, undefined, allowLocalFallback)).tasks;
3804
+ }
3805
+ function loadTaskTrackerContext(projectRoot, snapshot, allowLocalFallback = false) {
3806
+ const resolvedSnapshot = snapshot ?? readSyncedTrackerState(projectRoot, {}, allowLocalFallback ? { allowLocalFallback: true } : {});
3807
+ const tasks = resolvedSnapshot.issues.filter((issue) => issue.issueType === "task").map(projectTaskRecordFromSyncedIssue);
3808
+ const seenTaskIds = new Set(tasks.map((task) => task.id));
3809
+ if (allowLocalFallback) {
3810
+ for (const localTask of readLocalBeadsTasks(projectRoot)) {
3811
+ if (seenTaskIds.has(localTask.id)) {
3812
+ continue;
3813
+ }
3814
+ tasks.push(localTask);
3815
+ seenTaskIds.add(localTask.id);
3816
+ }
3817
+ }
3818
+ return {
3819
+ snapshot: resolvedSnapshot,
3820
+ tasks,
3821
+ tasksById: new Map(tasks.map((task) => [task.id, task]))
3822
+ };
3823
+ }
3824
+ function loadReadonlyTaskTrackerContext(projectRoot) {
3825
+ return loadTaskTrackerContext(projectRoot, undefined, true);
3826
+ }
3827
+ function projectTaskRecordFromSyncedIssue(issue) {
3828
+ const dependencies = issue.dependencies ?? [];
3829
+ return {
3830
+ id: issue.id,
3831
+ status: issue.status === "unknown" ? issue.rawStatus || "open" : issue.status,
3832
+ issue_type: "task",
3833
+ title: issue.title ?? undefined,
3834
+ priority: issue.priority ?? undefined,
3835
+ description: issue.description ?? undefined,
3836
+ acceptance_criteria: issue.acceptanceCriteria ?? undefined,
3837
+ acceptanceCriteria: issue.acceptanceCriteria ?? undefined,
3838
+ dependencies: dependencies.map((dependency) => ({
3839
+ type: dependency.type ?? undefined,
3840
+ issue_id: dependency.issueId ?? undefined,
3841
+ depends_on_id: dependency.dependsOnId ?? undefined,
3842
+ id: dependency.id ?? dependency.issueId ?? dependency.dependsOnId ?? undefined
3843
+ }))
3844
+ };
3845
+ }
3846
+ function taskDependencyIds(projectRoot, taskId) {
3847
+ const tracker = loadReadonlyTaskTrackerContext(projectRoot);
3848
+ const canonicalTaskIds = new Set(tracker.tasks.map((task) => task.id));
3849
+ const record = readBeadsTasks(projectRoot, tracker).find((entry) => entry.id === taskId);
3850
+ if (!record?.dependencies?.length) {
3851
+ return [];
3852
+ }
3853
+ const ids = new Set;
3854
+ for (const edge of record.dependencies) {
3855
+ if (!edge || edge.type === "parent-child") {
3856
+ continue;
3857
+ }
3858
+ for (const candidate of [edge.depends_on_id, edge.id, edge.issue_id]) {
3859
+ if (typeof candidate !== "string" || candidate === taskId || !canonicalTaskIds.has(candidate)) {
3860
+ continue;
3861
+ }
3862
+ ids.add(candidate);
3863
+ }
3864
+ }
3865
+ return [...ids].sort();
3866
+ }
3867
+ function taskDependencies(projectRoot, taskId, tracker) {
3868
+ if (!tracker) {
3869
+ return taskDependencyIds(projectRoot, taskId);
3870
+ }
3871
+ const canonicalTaskIds = new Set(tracker.tasks.map((task) => task.id));
3872
+ const record = readBeadsTasks(projectRoot, tracker).find((entry) => entry.id === taskId);
3873
+ if (!record?.dependencies?.length) {
3874
+ return [];
3875
+ }
3876
+ const ids = new Set;
3877
+ for (const edge of record.dependencies) {
3878
+ if (!edge || edge.type === "parent-child") {
3879
+ continue;
3880
+ }
3881
+ for (const candidate of [edge.depends_on_id, edge.id, edge.issue_id]) {
3882
+ if (typeof candidate !== "string" || candidate === taskId || !canonicalTaskIds.has(candidate)) {
3883
+ continue;
3884
+ }
3885
+ ids.add(candidate);
3886
+ }
3887
+ }
3888
+ return [...ids].sort();
3889
+ }
3890
+ function printArtifactSection(path, header) {
3891
+ if (!existsSync18(path)) {
3892
+ return;
3893
+ }
3894
+ console.log(header);
3895
+ process.stdout.write(readFileSync10(path, "utf-8"));
3896
+ console.log("");
3897
+ }
3898
+
3899
+ // packages/runtime/src/control-plane/native/repo-ops.ts
3900
+ function primaryManagedRepoId() {
3901
+ const entries = listManagedRepoEntries();
3902
+ return entries.length > 0 ? entries[0].id : null;
3903
+ }
3904
+ function primaryManagedRepoAlias() {
3905
+ const entries = listManagedRepoEntries();
3906
+ return entries.length > 0 ? entries[0].alias : null;
3907
+ }
3908
+ function repoDiscover(projectRoot, taskId) {
3909
+ const resolvedTask = taskId || currentTaskId(projectRoot);
3910
+ if (!resolvedTask) {
3911
+ return {};
3912
+ }
3913
+ const explicit = explicitPins(projectRoot, resolvedTask);
3914
+ if (Object.keys(explicit).length > 0) {
3915
+ return explicit;
3916
+ }
3917
+ return discoverPins(projectRoot, resolvedTask);
3918
+ }
3919
+ function repoBaseline(projectRoot, refresh = false) {
3920
+ const paths = resolveRepoDiscoveryPaths(projectRoot);
3921
+ if (!refresh && existsSync19(paths.baseRepoPinsPath)) {
3922
+ const baseline = readJsonFile(paths.baseRepoPinsPath, { recorded_at: nowIso(), repos: {} });
3923
+ return baseline.repos || {};
3924
+ }
3925
+ const id = primaryManagedRepoId();
3926
+ if (!id) {
3927
+ return persistBaselinePins(projectRoot, {});
3928
+ }
3929
+ const synced = syncManagedRepo(projectRoot, id);
3930
+ const alias = primaryManagedRepoAlias() ?? id;
3931
+ return persistBaselinePins(projectRoot, { [alias]: synced.headCommit });
3932
+ }
3933
+ function persistBaselinePins(projectRoot, repos) {
3934
+ const paths = resolveRepoDiscoveryPaths(projectRoot);
3935
+ mkdirSync11(resolve20(paths.baseRepoPinsPath, ".."), { recursive: true });
3936
+ writeFileSync9(paths.baseRepoPinsPath, `${JSON.stringify({ recorded_at: nowIso(), repos }, null, 2)}
3937
+ `, "utf-8");
3938
+ return repos;
3939
+ }
3940
+ function explicitPins(projectRoot, taskId) {
3941
+ const repoPins = readRepoDiscoveryTaskConfig(projectRoot)[taskId]?.repo_pins || {};
3942
+ const normalized = {};
3943
+ const validAliases = new Set(listManagedRepoEntries().map((e) => e.alias));
3944
+ for (const [key, value] of Object.entries(repoPins)) {
3945
+ if (!value) {
3946
+ continue;
3947
+ }
3948
+ if (validAliases.size > 0 && !validAliases.has(key)) {
3949
+ throw new Error(`Unsupported repo pin key for ${taskId}: ${key}. Known aliases: ${[...validAliases].join(", ") || "(none registered)"}`);
3950
+ }
3951
+ const existing = normalized[key];
3952
+ if (existing && existing !== value) {
3953
+ throw new Error(`Conflicting explicit repo pins for ${key}: ${existing} vs ${value}`);
3954
+ }
3955
+ normalized[key] = value;
3956
+ }
3957
+ return normalized;
3958
+ }
3959
+ function taskDependencies2(projectRoot, taskId) {
3960
+ return taskDependencyIds(projectRoot, taskId);
3961
+ }
3962
+ function discoverPins(projectRoot, taskId) {
3963
+ const deps = taskDependencies2(projectRoot, taskId);
3964
+ if (deps.length === 0) {
3965
+ return repoBaseline(projectRoot, true);
3966
+ }
3967
+ const paths = resolveRepoDiscoveryPaths(projectRoot);
3968
+ const state = readJsonFile(paths.taskRepoCommitsPath, {});
3969
+ const pinKeys = listManagedRepoEntries().map((e) => e.alias);
3970
+ if (pinKeys.length === 0) {
3971
+ return {};
3972
+ }
3973
+ const discovered = {};
3974
+ for (const key of pinKeys) {
3975
+ const commits = new Set;
3976
+ for (const dep of deps) {
3977
+ const fromState = state[dep]?.repos?.[key];
3978
+ if (fromState) {
3979
+ commits.add(fromState);
3980
+ }
3981
+ const fromArtifact = readPinFromArtifact(projectRoot, dep, key);
3982
+ if (fromArtifact) {
3983
+ commits.add(fromArtifact);
3984
+ }
3985
+ }
3986
+ if (commits.size > 1) {
3987
+ const values = [...commits].join(`
3988
+ - `);
3989
+ throw new Error(`Conflicting discovered pins for ${key} from dependency graph of ${taskId}:
3990
+ - ${values}`);
3991
+ }
3992
+ if (commits.size === 1) {
3993
+ discovered[key] = [...commits][0];
3994
+ }
3995
+ }
3996
+ return discovered;
3997
+ }
3998
+ function readRepoDiscoveryTaskConfig(projectRoot) {
3999
+ try {
4000
+ return readSourceTaskConfig(projectRoot);
4001
+ } catch {
4002
+ return readTaskConfig(projectRoot);
4003
+ }
4004
+ }
4005
+ function resolveRepoDiscoveryPaths(projectRoot) {
4006
+ const monorepoRoot = resolveMonorepoRepoLayout(projectRoot).checkoutRoot;
4007
+ const explicitHostProjectRoot = (process.env.RIG_HOST_PROJECT_ROOT || "").trim();
4008
+ const normalizedProjectRoot = resolve20(projectRoot);
4009
+ const hostProjectRoot = explicitHostProjectRoot && shouldUseHostProjectStateRoot(normalizedProjectRoot) ? explicitHostProjectRoot : normalizedProjectRoot;
4010
+ const stateDir = resolve20(hostProjectRoot, ".rig", "state");
4011
+ return {
4012
+ monorepoRoot,
4013
+ taskRepoCommitsPath: resolve20(stateDir, "task-repo-commits.json"),
4014
+ baseRepoPinsPath: resolve20(stateDir, "base-repo-pins.json")
4015
+ };
4016
+ }
4017
+ function shouldUseHostProjectStateRoot(projectRoot) {
4018
+ const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
4019
+ if (runtimeWorkspace && resolve20(runtimeWorkspace) === projectRoot) {
4020
+ return true;
4021
+ }
4022
+ return basename7(dirname11(projectRoot)) === ".worktrees";
4023
+ }
4024
+ function readPinFromArtifact(projectRoot, depTask, repoKey) {
4025
+ const snapshot = resolve20(artifactDirForId(projectRoot, depTask), "git-state.txt");
4026
+ if (!existsSync19(snapshot)) {
4027
+ return "";
4028
+ }
4029
+ const content = readFileSync11(snapshot, "utf-8");
4030
+ const chunk = content.split(/\r?\n/);
4031
+ let inSection = false;
4032
+ for (const line of chunk) {
4033
+ if (line.startsWith("## ")) {
4034
+ inSection = line.startsWith(`## ${repoKey}`);
4035
+ continue;
4036
+ }
4037
+ if (!inSection) {
4038
+ continue;
4039
+ }
4040
+ if (line.startsWith("head:")) {
4041
+ return line.replace(/^head:\s*/, "").trim();
4042
+ }
4043
+ }
4044
+ return "";
4045
+ }
4046
+
4047
+ // packages/runtime/src/control-plane/hooks/inject-context.ts
4048
+ function defaultMemoryQuery(taskId, runtimeContext) {
4049
+ const role = runtimeContext?.role?.trim();
4050
+ const scopeTerms = (runtimeContext?.scopes ?? []).flatMap((scope) => scope.split("/").slice(-3)).map((term) => term.trim()).filter(Boolean);
4051
+ const validationTerms = (runtimeContext?.validation ?? []).map((command) => command.split(/\s+/)[0]?.trim() ?? "").filter(Boolean);
4052
+ const parts = [...new Set([role, ...scopeTerms, ...validationTerms].filter(Boolean))];
4053
+ return parts.join(" ").trim() || taskId;
4054
+ }
4055
+ async function printSharedMemorySection(taskId) {
4056
+ const runtimeContext = loadRuntimeContextFromEnv();
4057
+ const memory = runtimeContext?.memory;
4058
+ if (!memory?.hydratedPath || !existsSync20(memory.hydratedPath)) {
4059
+ return;
4060
+ }
4061
+ const db = await openMemoryDb(memory.hydratedPath);
4062
+ try {
4063
+ const results = await queryRelevantMemory(db, {
4064
+ query: defaultMemoryQuery(taskId, runtimeContext),
4065
+ limit: memory.retrieval.topK,
4066
+ retrieval: memory.retrieval
4067
+ });
4068
+ if (results.length === 0) {
4069
+ return;
4070
+ }
4071
+ console.log(`
4072
+ === Shared Memory ===
4073
+ `);
4074
+ console.log(formatMemoryQueryResults(results));
4075
+ } finally {
4076
+ await db.close();
4077
+ }
4078
+ }
4079
+ async function main() {
4080
+ const projectRoot = resolveProjectRoot();
4081
+ const taskId = currentTaskId(projectRoot);
4082
+ if (!taskId) {
4083
+ console.log("No active task in Rig session. Start a run with: rig run start-serial");
4084
+ return;
4085
+ }
4086
+ console.log(`=== Task Assignment: ${taskId} ===
4087
+ `);
4088
+ await taskInfo(projectRoot, taskId);
4089
+ await printSharedMemorySection(taskId);
4090
+ const depIds = taskDependencyIds(projectRoot, taskId);
4091
+ if (depIds.length > 0) {
4092
+ console.log(`
4093
+ === Completed Dependency Artifacts ===
4094
+ `);
4095
+ await taskDeps(projectRoot, taskId);
4096
+ }
4097
+ console.log(`
4098
+ === Repo Pin Verification ===
4099
+ `);
4100
+ const pins = repoDiscover(projectRoot, taskId);
4101
+ if (Object.keys(pins).length === 0) {
4102
+ console.log("(no explicit/discovered task pins)");
4103
+ } else {
4104
+ for (const [key, expected] of Object.entries(pins)) {
4105
+ const repoPath = key.startsWith("/") ? key : resolve21(projectRoot, key);
4106
+ let current = "missing";
4107
+ if (existsSync20(resolve21(repoPath, ".git"))) {
4108
+ current = runCapture(["git", "-C", repoPath, "rev-parse", "HEAD"], projectRoot).stdout.trim() || "unknown";
4109
+ }
4110
+ console.log(`${current === expected ? "OK " : "WARN"} ${key} expected ${expected} got ${current}`);
4111
+ }
4112
+ }
4113
+ const failedPath = resolve21(resolveHarnessPaths(projectRoot).stateDir, "failed_approaches.md");
4114
+ if (existsSync20(failedPath)) {
4115
+ const lines = readFileSync12(failedPath, "utf-8").split(/\r?\n/);
4116
+ let printing = false;
4117
+ const failureLines = [];
4118
+ for (const line of lines) {
4119
+ if (line.startsWith("## ")) {
4120
+ if (printing) {
4121
+ break;
4122
+ }
4123
+ printing = line.includes(taskId);
4124
+ }
4125
+ if (printing) {
4126
+ failureLines.push(line);
4127
+ }
4128
+ }
4129
+ if (failureLines.length > 0) {
4130
+ console.log(`
4131
+ === Failed Approaches ===
4132
+ `);
4133
+ for (const line of failureLines) {
4134
+ console.log(line);
4135
+ }
4136
+ }
4137
+ }
4138
+ console.log(`
4139
+ === Agent Runtime ===
4140
+ `);
4141
+ await showProfile(projectRoot, false);
4142
+ console.log(`
4143
+ === AI Review Profile ===
4144
+ `);
4145
+ await showReviewProfile(projectRoot);
4146
+ console.log(`
4147
+ === Agent CLI ===
4148
+ `);
4149
+ console.log("Run: rig-agent help");
4150
+ console.log("");
4151
+ console.log("Quick reference:");
4152
+ console.log(" rig-agent info \u2014 Your task, role, scope, deps");
4153
+ console.log(" rig-agent scope \u2014 Scope globs (--files to expand)");
4154
+ console.log(" rig-agent deps \u2014 Dependency decisions & next-actions");
4155
+ console.log(" rig-agent project-root \u2014 Absolute task worktree root");
4156
+ console.log(" rig-agent monorepo-root \u2014 Absolute monorepo root");
4157
+ console.log(" rig-agent validate \u2014 Run validation before completing");
4158
+ console.log(" rig-agent verify \u2014 Run verifier (local + optional AI review)");
4159
+ console.log("");
4160
+ console.log("File tool contract:");
4161
+ for (const line of buildProviderRuntimeContextLines(normalizeRuntimeInstructionProvider(process.env.RIG_RUNTIME_ADAPTER))) {
4162
+ console.log(` ${line}`);
4163
+ }
4164
+ console.log(`
4165
+ === Working Directory ===
4166
+ `);
4167
+ const taskWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
4168
+ if (!taskWorkspace) {
4169
+ throw new Error("inject-context requires RIG_TASK_WORKSPACE to point at the active runtime workspace.");
4170
+ }
4171
+ console.log("IMPORTANT: Your working directory is the task worktree:");
4172
+ console.log(` ${taskWorkspace}`);
4173
+ console.log("");
4174
+ console.log(`Do not use absolute root paths under ${projectRoot}/repos.`);
4175
+ console.log("");
4176
+ console.log("Use the agent CLI from any directory:");
4177
+ console.log(" rig-agent <command>");
4178
+ console.log("Scope globs are monorepo-relative inside the monorepo worktree.");
4179
+ console.log("");
4180
+ console.log("Path discipline:");
4181
+ console.log(" Use absolute paths for Read/Grep/Bash after any `cd` command.");
4182
+ console.log(" The shell session preserves cwd across commands, so relative repos/... paths can drift.");
4183
+ console.log(` Project root: ${taskWorkspace}`);
4184
+ console.log(` Monorepo root: ${taskWorkspace}`);
4185
+ }
4186
+ main().catch((error) => {
4187
+ console.error(error);
4188
+ process.exit(1);
4189
+ });