substrate-ai 0.9.0 → 0.11.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 (35) hide show
  1. package/dist/adapter-registry-DXLMTmfD.js +0 -0
  2. package/dist/adapter-registry-neBZrkr3.js +4 -0
  3. package/dist/cli/index.js +5594 -5951
  4. package/dist/decisions-C0pz9Clx.js +0 -0
  5. package/dist/{decisions-BDLp3tJB.js → decisions-DQZW0h9X.js} +2 -1
  6. package/dist/dist-eNB_v7Iy.js +10205 -0
  7. package/dist/errors-BvyMlvCX.js +74 -0
  8. package/dist/experimenter-Dos3NsCg.js +3 -0
  9. package/dist/health-BvYILeQQ.js +6 -0
  10. package/dist/{health-C-VRJruD.js → health-CiDi90gC.js} +57 -1850
  11. package/dist/{helpers-CpMs8VZX.js → helpers-DTp3VJ2-.js} +31 -121
  12. package/dist/index.d.ts +709 -266
  13. package/dist/index.js +5 -3
  14. package/dist/{logger-D2fS2ccL.js → logger-KeHncl-f.js} +2 -42
  15. package/dist/routing-CcBOCuC9.js +0 -0
  16. package/dist/{routing-CD8bIci_.js → routing-HaYsjEIS.js} +2 -2
  17. package/dist/{run-ClxNDHbr.js → run-CAUhTR7Y.js} +594 -4249
  18. package/dist/run-DPZOQOvB.js +9 -0
  19. package/dist/{upgrade-B1S61VXJ.js → upgrade-DFGrqjGI.js} +3 -3
  20. package/dist/{upgrade-BK0HrKA6.js → upgrade-DYdYuuJK.js} +3 -3
  21. package/dist/version-manager-impl-BmOWu8ml.js +0 -0
  22. package/dist/version-manager-impl-CKv6I1S0.js +4 -0
  23. package/package.json +5 -2
  24. package/dist/adapter-registry-D2zdMwVu.js +0 -840
  25. package/dist/adapter-registry-WAyFydN5.js +0 -4
  26. package/dist/config-migrator-CtGelIsG.js +0 -250
  27. package/dist/decisions-DhAA2HG2.js +0 -397
  28. package/dist/experimenter-D_N_7ZF3.js +0 -503
  29. package/dist/git-utils-DxPx6erV.js +0 -365
  30. package/dist/health-DMbNP9bw.js +0 -5
  31. package/dist/operational-BdcdmDqS.js +0 -374
  32. package/dist/routing-BVrxrM6v.js +0 -832
  33. package/dist/run-MAQ3Wuju.js +0 -10
  34. package/dist/version-manager-impl-BIxOe7gZ.js +0 -372
  35. package/dist/version-manager-impl-RrWs-CI6.js +0 -4
@@ -1,13 +1,13 @@
1
- import { createLogger } from "./logger-D2fS2ccL.js";
2
- import { getLatestRun, getPipelineRunById } from "./decisions-DhAA2HG2.js";
1
+ import { createLogger } from "./logger-KeHncl-f.js";
2
+ import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-eNB_v7Iy.js";
3
3
  import { createRequire } from "module";
4
4
  import { dirname, join } from "path";
5
- import { existsSync } from "fs";
6
- import { createRequire as createRequire$1 } from "node:module";
7
- import { execFile, spawn, spawnSync } from "node:child_process";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { spawn, spawnSync } from "node:child_process";
8
7
  import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
9
- import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "node:fs";
10
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
8
+ import { readFile, writeFile } from "node:fs/promises";
9
+ import { existsSync as existsSync$1 } from "fs";
10
+ import { createRequire as createRequire$1 } from "node:module";
11
11
  import { fileURLToPath } from "node:url";
12
12
 
13
13
  //#region rolldown:runtime
@@ -80,1844 +80,17 @@ async function resolveMainRepoRoot(cwd = process.cwd()) {
80
80
  });
81
81
  }
82
82
 
83
- //#endregion
84
- //#region src/persistence/dolt-adapter.ts
85
- var DoltDatabaseAdapter = class {
86
- _client;
87
- /**
88
- * Create a DoltDatabaseAdapter wrapping the supplied DoltClient.
89
- * The caller should construct the client with the correct `repoPath`
90
- * before passing it here; `connect()` is called lazily by DoltClient.
91
- */
92
- constructor(client) {
93
- this._client = client;
94
- }
95
- /**
96
- * Execute a SQL query and return all result rows as typed objects.
97
- *
98
- * Delegates to `DoltClient.query<T>()` which supports both mysql2
99
- * pool mode and CLI fallback.
100
- */
101
- async query(sql, params) {
102
- return this._client.query(sql, params);
103
- }
104
- /**
105
- * Execute a SQL statement with no return value (DDL or DML).
106
- *
107
- * Delegates to `DoltClient.query()` and discards the result rows.
108
- */
109
- async exec(sql) {
110
- await this._client.query(sql, void 0);
111
- }
112
- /**
113
- * Execute a function within an explicit SQL transaction.
114
- *
115
- * Issues `BEGIN` before the function and `COMMIT` on success or
116
- * `ROLLBACK` on error. Works in both mysql2 pool mode (where
117
- * transactions are natively supported) and CLI mode (where Dolt
118
- * supports multi-statement sessions via CALL DOLT_CHECKOUT).
119
- */
120
- async transaction(fn) {
121
- await this._client.query("BEGIN", void 0);
122
- try {
123
- const result = await fn(this);
124
- await this._client.query("COMMIT", void 0);
125
- return result;
126
- } catch (err) {
127
- await this._client.query("ROLLBACK", void 0);
128
- throw err;
129
- }
130
- }
131
- /**
132
- * Close the underlying DoltClient connection pool.
133
- */
134
- async close() {
135
- await this._client.close();
136
- }
137
- /**
138
- * Query story keys from the `ready_stories` SQL view.
139
- *
140
- * Returns story keys whose status is `planned` or `ready` and whose
141
- * hard dependencies are all `complete` in the work graph.
142
- *
143
- * On any SQL error (e.g., view not yet created by story 31-1 schema,
144
- * or empty stories table), returns `[]` so the caller falls through to
145
- * the legacy discovery chain.
146
- */
147
- async queryReadyStories() {
148
- try {
149
- const rows = await this._client.query("SELECT `key` FROM ready_stories ORDER BY `key` ASC", void 0);
150
- return rows.map((r) => r.key);
151
- } catch {
152
- return [];
153
- }
154
- }
155
- };
156
-
157
- //#endregion
158
- //#region src/persistence/memory-adapter.ts
159
- var InMemoryDatabaseAdapter = class {
160
- _tables = new Map();
161
- _indexes = [];
162
- /** Maps table name → auto-increment column name */
163
- _autoIncrementCols = new Map();
164
- /** Maps table name → last assigned auto-increment value */
165
- _autoIncrementCounters = new Map();
166
- /** Maps "tableName.colName" → default value expression ('CURRENT_TIMESTAMP' → ISO string, else literal) */
167
- _columnDefaults = new Map();
168
- /** Maps table name → array of primary key column names */
169
- _primaryKeys = new Map();
170
- /** Maps table name → ordered list of column names (from CREATE TABLE) */
171
- _tableColumns = new Map();
172
- async query(sql, params) {
173
- const rows = this._execute(sql.trim(), params);
174
- return rows;
175
- }
176
- async exec(sql) {
177
- this._execute(sql.trim(), void 0);
178
- }
179
- querySync(sql, params) {
180
- const rows = this._execute(sql.trim(), params);
181
- return rows;
182
- }
183
- execSync(sql) {
184
- this._execute(sql.trim(), void 0);
185
- }
186
- async transaction(fn) {
187
- const snapshot = new Map();
188
- for (const [name, rows] of this._tables) snapshot.set(name, rows.map((r) => ({ ...r })));
189
- const counterSnapshot = new Map(this._autoIncrementCounters);
190
- try {
191
- const result = await fn(this);
192
- return result;
193
- } catch (err) {
194
- this._tables = snapshot;
195
- this._autoIncrementCounters = counterSnapshot;
196
- throw err;
197
- }
198
- }
199
- async close() {
200
- this._tables.clear();
201
- }
202
- /**
203
- * Work graph not supported in InMemoryDatabaseAdapter.
204
- * Returns `[]` to signal the caller to use the legacy discovery path.
205
- */
206
- async queryReadyStories() {
207
- return [];
208
- }
209
- _execute(sql, params) {
210
- const resolved = this._substituteParams(sql, params);
211
- const upper = resolved.trimStart().toUpperCase();
212
- if (/^CREATE\s+TABLE/i.test(upper)) return this._createTable(resolved);
213
- if (/^DROP\s+TABLE/i.test(upper)) return this._dropTable(resolved);
214
- if (/^CREATE\s+(?:UNIQUE\s+)?INDEX/i.test(upper)) return this._createIndex(resolved);
215
- if (/^DROP\s+INDEX/i.test(upper)) return this._dropIndex(resolved);
216
- if (/^CREATE\s+(?:OR\s+REPLACE\s+)?VIEW/i.test(upper)) return [];
217
- if (/^INSERT\s+(?:IGNORE\s+)?INTO/i.test(upper)) return this._insert(resolved, /^INSERT\s+IGNORE\s+INTO/i.test(upper));
218
- if (/^SELECT/i.test(upper)) return this._select(resolved);
219
- if (/^UPDATE/i.test(upper)) return this._update(resolved);
220
- if (/^DELETE\s+FROM/i.test(upper)) return this._delete(resolved);
221
- if (/^PRAGMA\s+table_info\s*\(/i.test(upper)) return this._pragmaTableInfo(resolved);
222
- return [];
223
- }
224
- /**
225
- * Replace each `?` placeholder with an escaped literal value.
226
- */
227
- _substituteParams(sql, params) {
228
- if (!params || params.length === 0) return sql;
229
- let idx = 0;
230
- return sql.replace(/\?/g, () => {
231
- const val = params[idx++];
232
- if (val === null || val === void 0) return "NULL";
233
- if (typeof val === "number") return String(val);
234
- if (typeof val === "boolean") return val ? "1" : "0";
235
- return `'${String(val).replace(/'/g, "''")}'`;
236
- });
237
- }
238
- _createTable(sql) {
239
- const m = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)/i.exec(sql);
240
- if (m) {
241
- const name = m[1];
242
- if (!this._tables.has(name)) this._tables.set(name, []);
243
- const colDefsM = /\((.+)\)\s*$/is.exec(sql);
244
- if (colDefsM) {
245
- const colDefs = colDefsM[1];
246
- const aiMatch = /^\s*(\w+)\s+\w+.*?(?:AUTO_INCREMENT|AUTOINCREMENT)/im.exec(colDefs);
247
- if (aiMatch) {
248
- this._autoIncrementCols.set(name, aiMatch[1]);
249
- if (!this._autoIncrementCounters.has(name)) this._autoIncrementCounters.set(name, 0);
250
- }
251
- const colLines = this._splitTopLevelCommas(colDefs);
252
- const pkCols = [];
253
- for (const colLine of colLines) {
254
- const trimmedLine = colLine.trim();
255
- const tablePkM = /^PRIMARY\s+KEY\s*\(([^)]+)\)/i.exec(trimmedLine);
256
- if (tablePkM) {
257
- const cols = tablePkM[1].split(",").map((c) => c.trim().replace(/^[`"](.+)[`"]$/, "$1"));
258
- pkCols.push(...cols);
259
- continue;
260
- }
261
- const defaultM = /^\s*(\w+)\s+\S+.*?\bDEFAULT\s+(.+?)(?:\s*,?\s*$)/i.exec(trimmedLine);
262
- if (defaultM) {
263
- const colName = defaultM[1];
264
- const defaultVal = defaultM[2].trim();
265
- this._columnDefaults.set(`${name}.${colName}`, defaultVal);
266
- }
267
- const colPkM = /^(\w+)\s+\w+.*?\bPRIMARY\s+KEY\b/i.exec(trimmedLine);
268
- if (colPkM && !/^PRIMARY\s+KEY\b/i.test(trimmedLine)) pkCols.push(colPkM[1]);
269
- }
270
- if (pkCols.length > 0 && !this._primaryKeys.has(name)) this._primaryKeys.set(name, pkCols);
271
- if (!this._tableColumns.has(name)) {
272
- const colNames = [];
273
- for (const colLine of colLines) {
274
- const trimmedLine = colLine.trim();
275
- if (/^(?:PRIMARY\s+KEY|UNIQUE|FOREIGN\s+KEY|CHECK)\s*[\s(]/i.test(trimmedLine)) continue;
276
- const quotedM = /^[`"](\w+)[`"]\s+\S/i.exec(trimmedLine);
277
- if (quotedM) {
278
- colNames.push(quotedM[1]);
279
- continue;
280
- }
281
- const bareM = /^(\w+)\s+\S/i.exec(trimmedLine);
282
- if (bareM) colNames.push(bareM[1]);
283
- }
284
- if (colNames.length > 0) this._tableColumns.set(name, colNames);
285
- }
286
- }
287
- }
288
- return [];
289
- }
290
- _dropTable(sql) {
291
- const m = /DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(\w+)/i.exec(sql);
292
- if (m) this._tables.delete(m[1]);
293
- return [];
294
- }
295
- _createIndex(sql) {
296
- const m = /CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s+ON\s+(\w+)/i.exec(sql);
297
- if (m) {
298
- const name = m[1];
299
- const tbl_name = m[2];
300
- if (!this._indexes.some((idx) => idx.name === name)) this._indexes.push({
301
- name,
302
- tbl_name,
303
- type: "index"
304
- });
305
- }
306
- return [];
307
- }
308
- _dropIndex(sql) {
309
- const m = /DROP\s+INDEX\s+(?:IF\s+EXISTS\s+)?(\w+)/i.exec(sql);
310
- if (m) {
311
- const name = m[1];
312
- this._indexes = this._indexes.filter((idx) => idx.name !== name);
313
- }
314
- return [];
315
- }
316
- /**
317
- * Emulate PRAGMA table_info(tableName) — returns one row per column with
318
- * {cid, name, type, notnull, dflt_value, pk} shape.
319
- * Column names are derived from the CREATE TABLE definition order.
320
- * This is sufficient for schema compatibility checks.
321
- */
322
- _pragmaTableInfo(sql) {
323
- const m = /PRAGMA\s+table_info\s*\(\s*[`'"]?(\w+)[`'"]?\s*\)/i.exec(sql);
324
- if (!m) return [];
325
- const tableName = m[1];
326
- const colNames = this._tableColumns.get(tableName);
327
- if (!colNames || colNames.length === 0) return [];
328
- const pkCols = this._primaryKeys.get(tableName) ?? [];
329
- return colNames.map((name, cid) => ({
330
- cid,
331
- name,
332
- type: "TEXT",
333
- notnull: 0,
334
- dflt_value: null,
335
- pk: pkCols.includes(name) ? 1 : 0
336
- }));
337
- }
338
- _selectFromSqliteMaster(sql) {
339
- const masterRows = [];
340
- for (const name of this._tables.keys()) masterRows.push({
341
- type: "table",
342
- name,
343
- tbl_name: name,
344
- rootpage: 0,
345
- sql: null
346
- });
347
- for (const idx of this._indexes) masterRows.push({
348
- type: "index",
349
- name: idx.name,
350
- tbl_name: idx.tbl_name,
351
- rootpage: 0,
352
- sql: null
353
- });
354
- const whereM = /WHERE\s+(.+?)(?:\s+ORDER\s+BY|\s+LIMIT|\s*$)/is.exec(sql);
355
- let rows = masterRows;
356
- if (whereM) rows = rows.filter((row) => this._matchWhere(whereM[1].trim(), row));
357
- const colsM = /SELECT\s+(.+?)\s+FROM\s+sqlite_master/is.exec(sql);
358
- if (!colsM) return rows;
359
- const colsStr = colsM[1].trim();
360
- if (colsStr === "*") return rows;
361
- return rows.map((row) => this._projectCols(colsStr, row));
362
- }
363
- _insert(sql, _ignoreConflicts = false) {
364
- const m = /INSERT\s+(?:IGNORE\s+)?INTO\s+(\w+)\s*\(([^)]+)\)\s*VALUES\s*\((.+)\)\s*$/is.exec(sql);
365
- if (!m) return [];
366
- const tableName = m[1];
367
- const cols = m[2].split(",").map((c) => c.trim().replace(/^[`"](.+)[`"]$/, "$1"));
368
- const valStr = m[3];
369
- const vals = this._parseValueList(valStr);
370
- const row = {};
371
- for (let i = 0; i < cols.length; i++) row[cols[i]] = vals[i] ?? null;
372
- const aiCol = this._autoIncrementCols.get(tableName);
373
- if (aiCol && !(aiCol in row)) {
374
- const next = (this._autoIncrementCounters.get(tableName) ?? 0) + 1;
375
- this._autoIncrementCounters.set(tableName, next);
376
- row[aiCol] = next;
377
- }
378
- for (const [key, defaultExpr] of this._columnDefaults) {
379
- const [tbl, col] = key.split(".");
380
- if (tbl === tableName && !(col in row)) if (/^CURRENT_TIMESTAMP$/i.test(defaultExpr)) row[col] = new Date().toISOString();
381
- else row[col] = this._evalLiteral(defaultExpr);
382
- }
383
- if (!this._tables.has(tableName)) this._tables.set(tableName, []);
384
- const pkCols = this._primaryKeys.get(tableName);
385
- if (pkCols && pkCols.length > 0 && !_ignoreConflicts) {
386
- const table = this._tables.get(tableName);
387
- const isDuplicate = table.some((existingRow) => pkCols.every((col) => existingRow[col] !== void 0 && String(existingRow[col]) === String(row[col])));
388
- if (isDuplicate) throw new Error(`UNIQUE constraint failed: ${tableName} (${pkCols.join(", ")})`);
389
- }
390
- this._tables.get(tableName).push(row);
391
- return [];
392
- }
393
- _select(sql) {
394
- if (!/FROM/i.test(sql)) {
395
- const m$1 = /SELECT\s+(.+)$/is.exec(sql);
396
- if (!m$1) return [];
397
- return [this._evalSelectExprs(m$1[1].trim())];
398
- }
399
- if (/FROM\s+sqlite_master/i.test(sql)) return this._selectFromSqliteMaster(sql);
400
- let limitValue;
401
- const limitMatch = /\s+LIMIT\s+(\d+)\s*$/is.exec(sql);
402
- if (limitMatch) limitValue = parseInt(limitMatch[1], 10);
403
- let orderByExprs;
404
- const orderByMatch = /\s+ORDER\s+BY\s+(.+?)(?:\s+LIMIT\s+\d+\s*)?$/is.exec(sql);
405
- if (orderByMatch) orderByExprs = this._parseOrderBy(orderByMatch[1].trim());
406
- let stripped = sql.replace(/\s+ORDER\s+BY\s+.+?(?=\s+LIMIT\s|\s*$)/is, "").replace(/\s+LIMIT\s+\d+\s*$/is, "");
407
- let groupByCols = null;
408
- const groupByMatch = /\s+GROUP\s+BY\s+(.+?)(?:\s+HAVING\s+.+?)?$/is.exec(stripped);
409
- if (groupByMatch) {
410
- groupByCols = groupByMatch[1].split(",").map((c) => c.trim());
411
- stripped = stripped.replace(/\s+GROUP\s+BY\s+.+$/is, "");
412
- }
413
- const m = /SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(stripped);
414
- if (!m) return [];
415
- const colsStr = m[1].trim();
416
- const tableName = m[2];
417
- const whereStr = m[3];
418
- const table = this._tables.get(tableName) ?? [];
419
- let rows = table.map((r) => ({ ...r }));
420
- if (whereStr) rows = rows.filter((row) => this._matchWhere(whereStr.trim(), row));
421
- if (groupByCols !== null) {
422
- const grouped = this._applyGroupBy(colsStr, rows, groupByCols);
423
- const sorted = orderByExprs ? this._applyOrderBy(grouped, orderByExprs) : grouped;
424
- return limitValue !== void 0 ? sorted.slice(0, limitValue) : sorted;
425
- }
426
- if (orderByExprs) rows = this._applyOrderBy(rows, orderByExprs);
427
- if (limitValue !== void 0) rows = rows.slice(0, limitValue);
428
- if (colsStr === "*") return rows;
429
- if (/\b(?:SUM|COALESCE|COUNT|AVG|MIN|MAX)\s*\(/i.test(colsStr)) return [this._evalAggregate(colsStr, rows)];
430
- return rows.map((row) => this._projectCols(colsStr, row));
431
- }
432
- /**
433
- * Apply GROUP BY: bucket rows by the group-by columns, then evaluate
434
- * aggregate expressions for each bucket. Plain column references in the
435
- * SELECT list that are also GROUP BY columns return the group value from
436
- * the first row in the bucket (all rows in a group share the same value).
437
- */
438
- _applyGroupBy(colsStr, rows, groupByCols) {
439
- const groups = new Map();
440
- for (const row of rows) {
441
- const key = groupByCols.map((col) => String(row[col] ?? "")).join("\0");
442
- if (!groups.has(key)) groups.set(key, []);
443
- groups.get(key).push(row);
444
- }
445
- const result = [];
446
- for (const [, groupRows] of groups) result.push(this._evalAggregateGroup(colsStr, groupRows));
447
- return result;
448
- }
449
- /**
450
- * Parse ORDER BY expression list into sort keys.
451
- * Handles: simple columns, COALESCE(col, default) DESC, CASE...END expressions.
452
- */
453
- _parseOrderBy(orderByStr) {
454
- const parts = this._splitTopLevelCommas(orderByStr);
455
- return parts.map((part) => {
456
- const trimmed = part.trim();
457
- const dirMatch = /^(.*?)\s+(ASC|DESC)\s*$/i.exec(trimmed);
458
- if (dirMatch) return {
459
- expr: dirMatch[1].trim(),
460
- dir: dirMatch[2].toUpperCase()
461
- };
462
- return {
463
- expr: trimmed,
464
- dir: "ASC"
465
- };
466
- });
467
- }
468
- /**
469
- * Sort rows according to ORDER BY expression list.
470
- */
471
- _applyOrderBy(rows, orderBy) {
472
- return [...rows].sort((a, b) => {
473
- for (const { expr, dir } of orderBy) {
474
- const aVal = this._evalOrderByExpr(expr, a);
475
- const bVal = this._evalOrderByExpr(expr, b);
476
- const cmp = this._compareValues(aVal, bVal);
477
- if (cmp !== 0) return dir === "DESC" ? -cmp : cmp;
478
- }
479
- return 0;
480
- });
481
- }
482
- /**
483
- * Evaluate an ORDER BY expression for a single row.
484
- */
485
- _evalOrderByExpr(expr, row) {
486
- const trimmed = expr.trim();
487
- const simpleCaseM = /^CASE\s+(\w+)\s+(.+?)\s+END$/is.exec(trimmed);
488
- if (simpleCaseM) {
489
- const col = simpleCaseM[1];
490
- const colVal = String(row[col] ?? "");
491
- const body = simpleCaseM[2].trim();
492
- const whenMatches = [...body.matchAll(/WHEN\s+'([^']*)'\s+THEN\s+(\S+)/gi)];
493
- for (const wm of whenMatches) if (colVal === wm[1]) return this._evalLiteral(wm[2]);
494
- const elseM = /ELSE\s+(\S+)\s*$/i.exec(body);
495
- if (elseM) return this._evalLiteral(elseM[1]);
496
- return null;
497
- }
498
- return this._evalRowExpr(trimmed, row);
499
- }
500
- /**
501
- * Compare two values for sorting (handles null, numbers, strings).
502
- * Returns negative if a < b, 0 if equal, positive if a > b.
503
- */
504
- _compareValues(a, b) {
505
- if (a === null || a === void 0) return b === null || b === void 0 ? 0 : 1;
506
- if (b === null || b === void 0) return -1;
507
- if (typeof a === "number" && typeof b === "number") return a - b;
508
- return String(a).localeCompare(String(b));
509
- }
510
- /**
511
- * Evaluate SELECT expressions against a single GROUP BY bucket.
512
- * Handles both aggregate expressions (SUM, COUNT, COALESCE) and plain
513
- * column references (for GROUP BY projected columns).
514
- */
515
- _evalAggregateGroup(colsStr, rows) {
516
- const result = {};
517
- const cols = this._splitTopLevelCommas(colsStr);
518
- for (const col of cols) {
519
- const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
520
- const expr = aliasM ? aliasM[1].trim() : col.trim();
521
- const alias = aliasM ? aliasM[2] : col.trim();
522
- result[alias] = this._evalAggregateExprGrouped(expr, rows);
523
- }
524
- return result;
525
- }
526
- /**
527
- * Evaluate a single aggregate expression against a GROUP BY bucket.
528
- * Extends _evalAggregateExpr with:
529
- * - SUM(expr) where expr may be CASE WHEN ... END
530
- * - Plain column references (returns first-row value, for GROUP BY cols)
531
- */
532
- _evalAggregateExprGrouped(expr, rows) {
533
- const trimmed = expr.trim();
534
- const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
535
- if (coalesceM) {
536
- const args = this._splitTopLevelCommas(coalesceM[1]);
537
- for (const arg of args) {
538
- const val = this._evalAggregateExprGrouped(arg.trim(), rows);
539
- if (val !== null && val !== void 0) return val;
540
- }
541
- return null;
542
- }
543
- const sumM = /^SUM\((.+)\)$/i.exec(trimmed);
544
- if (sumM) {
545
- if (rows.length === 0) return null;
546
- let total = 0;
547
- for (const row of rows) total += Number(this._evalRowExpr(sumM[1].trim(), row) ?? 0);
548
- return total;
549
- }
550
- if (/^COUNT\(\*\)$/i.test(trimmed)) return rows.length;
551
- const countColM = /^COUNT\((\w+)\)$/i.exec(trimmed);
552
- if (countColM) {
553
- const col = countColM[1];
554
- return rows.filter((r) => r[col] !== null && r[col] !== void 0).length;
555
- }
556
- const maxColM = /^MAX\((\w+)\)$/i.exec(trimmed);
557
- if (maxColM) {
558
- const col = maxColM[1];
559
- const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== void 0);
560
- if (values.length === 0) return null;
561
- return values.reduce((a, b) => String(a) >= String(b) ? a : b);
562
- }
563
- const minColM = /^MIN\((\w+)\)$/i.exec(trimmed);
564
- if (minColM) {
565
- const col = minColM[1];
566
- const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== void 0);
567
- if (values.length === 0) return null;
568
- return values.reduce((a, b) => String(a) <= String(b) ? a : b);
569
- }
570
- if (/^\w+$/.test(trimmed) && rows.length > 0 && trimmed in rows[0]) return rows[0][trimmed];
571
- return this._evalLiteral(trimmed);
572
- }
573
- /**
574
- * Evaluate an expression for a single row.
575
- * Supports CASE WHEN conditions, COALESCE, column references, and literals.
576
- * Used by _evalAggregateExprGrouped to evaluate the argument of SUM().
577
- */
578
- _evalRowExpr(expr, row) {
579
- const trimmed = expr.trim();
580
- const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
581
- if (coalesceM) {
582
- const args = this._splitTopLevelCommas(coalesceM[1]);
583
- for (const arg of args) {
584
- const val = this._evalRowExpr(arg.trim(), row);
585
- if (val !== null && val !== void 0) return val;
586
- }
587
- return null;
588
- }
589
- const caseM = /^CASE\s+WHEN\s+(.+?)\s+THEN\s+(.+?)\s+ELSE\s+(.+?)\s+END$/is.exec(trimmed);
590
- if (caseM) {
591
- const matches = this._evalRowCondition(caseM[1].trim(), row);
592
- return this._evalRowExpr(matches ? caseM[2].trim() : caseM[3].trim(), row);
593
- }
594
- if (/^\w+$/.test(trimmed) && /^[a-zA-Z_]/.test(trimmed)) {
595
- if (trimmed in row) return row[trimmed];
596
- return null;
597
- }
598
- return this._evalLiteral(trimmed);
599
- }
600
- /**
601
- * Evaluate a simple condition (col = 'str', col = num) for a single row.
602
- * Returns true if the condition holds.
603
- */
604
- _evalRowCondition(condition, row) {
605
- const trimmed = condition.trim();
606
- const strM = /^(\w+)\s*=\s*'(.*)'$/is.exec(trimmed);
607
- if (strM) {
608
- const colVal = String(row[strM[1]] ?? "");
609
- const literal = strM[2].replace(/''/g, "'");
610
- return colVal === literal;
611
- }
612
- const numM = /^(\w+)\s*=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
613
- if (numM) return Number(row[numM[1]]) === parseFloat(numM[2]);
614
- return false;
615
- }
616
- _update(sql) {
617
- const m = /UPDATE\s+(\w+)\s+SET\s+(.+?)(?:\s+WHERE\s+(.+))?$/is.exec(sql);
618
- if (!m) return [];
619
- const tableName = m[1];
620
- const setStr = m[2];
621
- const whereStr = m[3];
622
- const table = this._tables.get(tableName);
623
- if (!table) return [];
624
- for (const row of table) if (!whereStr || this._matchWhere(whereStr.trim(), row)) {
625
- const assignments = this._parseAssignmentsForRow(setStr, row);
626
- for (const [col, val] of assignments) row[col] = val;
627
- }
628
- return [];
629
- }
630
- _delete(sql) {
631
- const m = /DELETE\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(sql);
632
- if (!m) return [];
633
- const tableName = m[1];
634
- const whereStr = m[2];
635
- const table = this._tables.get(tableName);
636
- if (!table) return [];
637
- if (!whereStr) {
638
- this._tables.set(tableName, []);
639
- return [];
640
- }
641
- const kept = table.filter((row) => !this._matchWhere(whereStr.trim(), row));
642
- this._tables.set(tableName, kept);
643
- return [];
644
- }
645
- /**
646
- * Evaluate a simple WHERE clause against a row.
647
- * Supports: `col = val` conditions joined by AND.
648
- */
649
- _matchWhere(whereClause, row) {
650
- const conditions = whereClause.split(/\s+AND\s+/i);
651
- for (const condition of conditions) {
652
- const trimmed = condition.trim();
653
- const strM = /^[`"]?(\w+)[`"]?\s*=\s*'(.*)'$/is.exec(trimmed);
654
- if (strM) {
655
- const colVal = String(row[strM[1]] ?? "");
656
- const literal = strM[2].replace(/''/g, "'");
657
- if (colVal !== literal) return false;
658
- continue;
659
- }
660
- const strNeqM = /^[`"]?(\w+)[`"]?\s*!=\s*'(.*)'$/is.exec(trimmed);
661
- if (strNeqM) {
662
- const colVal = String(row[strNeqM[1]] ?? "");
663
- const literal = strNeqM[2].replace(/''/g, "'");
664
- if (colVal === literal) return false;
665
- continue;
666
- }
667
- const numM = /^[`"]?(\w+)[`"]?\s*=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
668
- if (numM) {
669
- if (Number(row[numM[1]]) !== parseFloat(numM[2])) return false;
670
- continue;
671
- }
672
- const numNeqM = /^[`"]?(\w+)[`"]?\s*!=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
673
- if (numNeqM) {
674
- if (Number(row[numNeqM[1]]) === parseFloat(numNeqM[2])) return false;
675
- continue;
676
- }
677
- const strCmpM = /^[`"]?(\w+)[`"]?\s*(>=|<=|>|<)\s*'(.*)'$/s.exec(trimmed);
678
- if (strCmpM) {
679
- const colVal = row[strCmpM[1]];
680
- if (colVal === null || colVal === void 0) return false;
681
- const lhs = String(colVal);
682
- const rhs = strCmpM[3].replace(/''/g, "'");
683
- const op = strCmpM[2];
684
- if (op === "<" && !(lhs < rhs)) return false;
685
- if (op === "<=" && !(lhs <= rhs)) return false;
686
- if (op === ">" && !(lhs > rhs)) return false;
687
- if (op === ">=" && !(lhs >= rhs)) return false;
688
- continue;
689
- }
690
- const numCmpM = /^[`"]?(\w+)[`"]?\s*(>=|<=|>|<)\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
691
- if (numCmpM) {
692
- const colVal = Number(row[numCmpM[1]] ?? 0);
693
- const rhs = parseFloat(numCmpM[3]);
694
- const op = numCmpM[2];
695
- if (op === "<" && !(colVal < rhs)) return false;
696
- if (op === "<=" && !(colVal <= rhs)) return false;
697
- if (op === ">" && !(colVal > rhs)) return false;
698
- if (op === ">=" && !(colVal >= rhs)) return false;
699
- continue;
700
- }
701
- const nullM = /^[`"]?(\w+)[`"]?\s+IS\s+NULL$/i.exec(trimmed);
702
- if (nullM) {
703
- if (row[nullM[1]] !== null && row[nullM[1]] !== void 0) return false;
704
- continue;
705
- }
706
- const notNullM = /^[`"]?(\w+)[`"]?\s+IS\s+NOT\s+NULL$/i.exec(trimmed);
707
- if (notNullM) {
708
- if (row[notNullM[1]] === null || row[notNullM[1]] === void 0) return false;
709
- continue;
710
- }
711
- const likeM = /^[`"]?(\w+)[`"]?\s+LIKE\s+'(.*)'$/is.exec(trimmed);
712
- if (likeM) {
713
- const colVal = row[likeM[1]];
714
- if (colVal === null || colVal === void 0) return false;
715
- const pattern = likeM[2].replace(/''/g, "'");
716
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, (ch) => ch === "%" || ch === "_" ? ch : "\\" + ch);
717
- const regex = new RegExp("^" + escaped.replace(/%/g, ".*").replace(/_/g, ".") + "$", "s");
718
- if (!regex.test(String(colVal))) return false;
719
- continue;
720
- }
721
- const inM = /^[`"]?(\w+)[`"]?\s+IN\s*\((.+)\)$/is.exec(trimmed);
722
- if (inM) {
723
- const colVal = row[inM[1]];
724
- const inValues = this._parseValueList(inM[2]);
725
- const colStr = colVal === null || colVal === void 0 ? null : typeof colVal === "number" ? colVal : String(colVal);
726
- if (!inValues.some((v) => v === colStr || String(v) === String(colStr))) return false;
727
- continue;
728
- }
729
- const notInM = /^[`"]?(\w+)[`"]?\s+NOT\s+IN\s*\((.+)\)$/is.exec(trimmed);
730
- if (notInM) {
731
- const colVal = row[notInM[1]];
732
- const notInValues = this._parseValueList(notInM[2]);
733
- const colStr = colVal === null || colVal === void 0 ? null : typeof colVal === "number" ? colVal : String(colVal);
734
- if (notInValues.some((v) => v === colStr || String(v) === String(colStr))) return false;
735
- continue;
736
- }
737
- }
738
- return true;
739
- }
740
- _projectCols(colsStr, row) {
741
- const result = {};
742
- const cols = this._splitTopLevelCommas(colsStr);
743
- for (const col of cols) {
744
- const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
745
- if (aliasM) result[aliasM[2]] = this._evalExprAgainstRow(aliasM[1].trim(), row);
746
- else result[col] = col in row ? row[col] : null;
747
- }
748
- return result;
749
- }
750
- _evalSelectExprs(exprs) {
751
- const result = {};
752
- const parts = this._splitTopLevelCommas(exprs);
753
- for (const part of parts) {
754
- const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(part);
755
- if (aliasM) result[aliasM[2]] = this._evalLiteral(aliasM[1].trim());
756
- else result[part] = this._evalLiteral(part);
757
- }
758
- return result;
759
- }
760
- _evalLiteral(expr) {
761
- const trimmed = expr.trim();
762
- if (trimmed.toUpperCase() === "NULL") return null;
763
- if (/^'.*'$/.test(trimmed)) return trimmed.slice(1, -1).replace(/''/g, "'");
764
- if (/^-?\d+$/.test(trimmed)) return parseInt(trimmed, 10);
765
- if (/^-?\d+\.\d+$/.test(trimmed)) return parseFloat(trimmed);
766
- return trimmed;
767
- }
768
- _evalExprAgainstRow(expr, row) {
769
- const literal = this._evalLiteral(expr);
770
- if (typeof literal !== "string") return literal;
771
- if (/^\w+$/.test(expr) && expr in row) return row[expr];
772
- return literal;
773
- }
774
- /**
775
- * Split a string by commas that are NOT inside parentheses.
776
- * E.g. "COALESCE(SUM(x), 0) as a, y" → ["COALESCE(SUM(x), 0) as a", "y"]
777
- */
778
- _splitTopLevelCommas(str) {
779
- const parts = [];
780
- let current = "";
781
- let depth = 0;
782
- let inStr = false;
783
- for (let i = 0; i < str.length; i++) {
784
- const ch = str[i];
785
- if (ch === "'" && !inStr) {
786
- inStr = true;
787
- current += ch;
788
- } else if (ch === "'" && inStr) if (str[i + 1] === "'") {
789
- current += "''";
790
- i++;
791
- } else {
792
- inStr = false;
793
- current += ch;
794
- }
795
- else if (!inStr && ch === "(") {
796
- depth++;
797
- current += ch;
798
- } else if (!inStr && ch === ")") {
799
- depth--;
800
- current += ch;
801
- } else if (!inStr && ch === "," && depth === 0) {
802
- parts.push(current.trim());
803
- current = "";
804
- } else current += ch;
805
- }
806
- if (current.trim() !== "") parts.push(current.trim());
807
- return parts;
808
- }
809
- /**
810
- * Evaluate aggregate SELECT expressions (SUM, COALESCE, COUNT) across
811
- * a set of filtered rows, returning a single result row.
812
- */
813
- _evalAggregate(colsStr, rows) {
814
- const result = {};
815
- const cols = this._splitTopLevelCommas(colsStr);
816
- for (const col of cols) {
817
- const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
818
- const expr = aliasM ? aliasM[1].trim() : col.trim();
819
- const alias = aliasM ? aliasM[2] : col.trim();
820
- result[alias] = this._evalAggregateExpr(expr, rows);
821
- }
822
- return result;
823
- }
824
- /**
825
- * Evaluate a single aggregate expression against a set of rows.
826
- * Supports: SUM(expr), COALESCE(expr, default), COUNT(*), MAX(col), MIN(col).
827
- */
828
- _evalAggregateExpr(expr, rows) {
829
- const trimmed = expr.trim();
830
- const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
831
- if (coalesceM) {
832
- const args = this._splitTopLevelCommas(coalesceM[1]);
833
- for (const arg of args) {
834
- const val = this._evalAggregateExpr(arg.trim(), rows);
835
- if (val !== null && val !== void 0) return val;
836
- }
837
- return null;
838
- }
839
- const sumM = /^SUM\((.+)\)$/i.exec(trimmed);
840
- if (sumM) {
841
- if (rows.length === 0) return null;
842
- let total = 0;
843
- for (const row of rows) total += Number(this._evalRowExpr(sumM[1].trim(), row) ?? 0);
844
- return total;
845
- }
846
- if (/^COUNT\(\*\)$/i.test(trimmed)) return rows.length;
847
- const countDistinctM = /^COUNT\(\s*DISTINCT\s+(\w+)\s*\)$/i.exec(trimmed);
848
- if (countDistinctM) {
849
- const col = countDistinctM[1];
850
- const distinct = new Set(rows.map((r) => r[col]).filter((v) => v !== null && v !== void 0));
851
- return distinct.size;
852
- }
853
- const countM = /^COUNT\((\w+)\)$/i.exec(trimmed);
854
- if (countM) {
855
- const col = countM[1];
856
- return rows.filter((r) => r[col] !== null && r[col] !== void 0).length;
857
- }
858
- const maxM = /^MAX\((\w+)\)$/i.exec(trimmed);
859
- if (maxM) {
860
- const col = maxM[1];
861
- const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== void 0);
862
- if (values.length === 0) return null;
863
- return values.reduce((a, b) => String(a) >= String(b) ? a : b);
864
- }
865
- const minM = /^MIN\((\w+)\)$/i.exec(trimmed);
866
- if (minM) {
867
- const col = minM[1];
868
- const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== void 0);
869
- if (values.length === 0) return null;
870
- return values.reduce((a, b) => String(a) <= String(b) ? a : b);
871
- }
872
- return this._evalLiteral(trimmed);
873
- }
874
- /**
875
- * Parse a comma-separated list of SQL literal values.
876
- * Handles: NULL, numbers, single-quoted strings.
877
- * Simple split by comma (assumes no commas inside string values).
878
- */
879
- _parseValueList(valStr) {
880
- const tokens = [];
881
- let current = "";
882
- let inStr = false;
883
- for (let i = 0; i < valStr.length; i++) {
884
- const ch = valStr[i];
885
- if (ch === "'" && !inStr) {
886
- inStr = true;
887
- current += ch;
888
- } else if (ch === "'" && inStr) if (valStr[i + 1] === "'") {
889
- current += "''";
890
- i++;
891
- } else {
892
- inStr = false;
893
- current += ch;
894
- }
895
- else if (ch === "," && !inStr) {
896
- tokens.push(current.trim());
897
- current = "";
898
- } else current += ch;
899
- }
900
- if (current.trim() !== "") tokens.push(current.trim());
901
- return tokens.map((t) => this._evalLiteral(t));
902
- }
903
- /**
904
- * Parse `col1 = val1, col2 = val2` assignments into an array of [col, val] pairs.
905
- * Evaluates each RHS expression against the provided row for arithmetic support.
906
- */
907
- _parseAssignmentsForRow(setStr, row) {
908
- const assignments = [];
909
- const parts = [];
910
- let current = "";
911
- let inStr = false;
912
- for (let i = 0; i < setStr.length; i++) {
913
- const ch = setStr[i];
914
- if (ch === "'" && !inStr) {
915
- inStr = true;
916
- current += ch;
917
- } else if (ch === "'" && inStr) if (setStr[i + 1] === "'") {
918
- current += "''";
919
- i++;
920
- } else {
921
- inStr = false;
922
- current += ch;
923
- }
924
- else if (ch === "," && !inStr) {
925
- parts.push(current.trim());
926
- current = "";
927
- } else current += ch;
928
- }
929
- if (current.trim() !== "") parts.push(current.trim());
930
- for (const part of parts) {
931
- const eqIdx = part.indexOf("=");
932
- if (eqIdx === -1) continue;
933
- const col = part.slice(0, eqIdx).trim();
934
- const valStr = part.slice(eqIdx + 1).trim();
935
- assignments.push([col, this._evalAssignmentExpr(valStr, row)]);
936
- }
937
- return assignments;
938
- }
939
- /**
940
- * Evaluate a SET assignment RHS expression against the current row.
941
- * Handles: simple literals, column arithmetic (col +/- val), COALESCE, CURRENT_TIMESTAMP.
942
- */
943
- _evalAssignmentExpr(expr, row) {
944
- const trimmed = expr.trim();
945
- if (/^CURRENT_TIMESTAMP$/i.test(trimmed)) return new Date().toISOString();
946
- const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
947
- if (coalesceM) {
948
- const args = this._splitTopLevelCommas(coalesceM[1]);
949
- for (const arg of args) {
950
- const val = this._evalAssignmentExpr(arg.trim(), row);
951
- if (val !== null && val !== void 0) return val;
952
- }
953
- return null;
954
- }
955
- const arithM = /^(\w+)\s*([+\-*\/])\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
956
- if (arithM) {
957
- const colName = arithM[1];
958
- const op = arithM[2];
959
- const num = parseFloat(arithM[3]);
960
- const colVal = Number(row[colName] ?? 0);
961
- switch (op) {
962
- case "+": return colVal + num;
963
- case "-": return colVal - num;
964
- case "*": return colVal * num;
965
- case "/": return num !== 0 ? colVal / num : null;
966
- }
967
- }
968
- return this._evalLiteral(trimmed);
969
- }
970
- /**
971
- * @deprecated Use _parseAssignmentsForRow instead.
972
- * Kept for internal compatibility — evaluates without row context.
973
- */
974
- _parseAssignments(setStr) {
975
- return this._parseAssignmentsForRow(setStr, {});
976
- }
977
- };
978
-
979
- //#endregion
980
- //#region src/modules/state/errors.ts
981
- /**
982
- * Typed error classes for the Dolt state store.
983
- */
984
- var StateStoreError = class extends Error {
985
- code;
986
- constructor(code, message) {
987
- super(message);
988
- this.name = "StateStoreError";
989
- this.code = code;
990
- }
991
- };
992
- var DoltQueryError = class extends StateStoreError {
993
- sql;
994
- detail;
995
- constructor(sql, detail) {
996
- super("DOLT_QUERY_ERROR", `Dolt query failed: ${detail}`);
997
- this.name = "DoltQueryError";
998
- this.sql = sql;
999
- this.detail = detail;
1000
- }
1001
- };
1002
- var DoltMergeConflictError = class extends StateStoreError {
1003
- table;
1004
- conflictingKeys;
1005
- rowKey;
1006
- ourValue;
1007
- theirValue;
1008
- constructor(table, conflictingKeys, options) {
1009
- super("DOLT_MERGE_CONFLICT", `Merge conflict in table '${table}' on keys: ${conflictingKeys.join(", ")}`);
1010
- this.name = "DoltMergeConflictError";
1011
- this.table = table;
1012
- this.conflictingKeys = conflictingKeys;
1013
- if (options) {
1014
- this.rowKey = options.rowKey;
1015
- this.ourValue = options.ourValue;
1016
- this.theirValue = options.theirValue;
1017
- }
1018
- }
1019
- };
1020
- /** Alias for DoltMergeConflictError — used by orchestrator branch lifecycle. */
1021
- const DoltMergeConflict = DoltMergeConflictError;
1022
-
1023
- //#endregion
1024
- //#region src/modules/state/dolt-client.ts
1025
- /**
1026
- * Promise-wrapper around execFile that always resolves to { stdout, stderr }.
1027
- * Using an explicit wrapper rather than promisify() avoids the util.promisify.custom
1028
- * symbol complexity when mocking in tests.
1029
- */
1030
- function runExecFile(cmd, args, opts) {
1031
- return new Promise((resolve$2, reject) => {
1032
- execFile(cmd, args, opts, (err, stdout, stderr) => {
1033
- if (err) reject(err);
1034
- else resolve$2({
1035
- stdout,
1036
- stderr
1037
- });
1038
- });
1039
- });
1040
- }
1041
- const log$1 = createLogger("modules:state:dolt");
1042
- var DoltClient = class {
1043
- repoPath;
1044
- socketPath;
1045
- _pool = null;
1046
- _useCliMode = false;
1047
- _connected = false;
1048
- /** Promise-chain mutex that serializes all CLI operations to prevent concurrent noms manifest access */
1049
- _cliMutex = Promise.resolve();
1050
- constructor(options) {
1051
- this.repoPath = options.repoPath;
1052
- this.socketPath = options.socketPath ?? `${options.repoPath}/.dolt/dolt.sock`;
1053
- }
1054
- async connect() {
1055
- try {
1056
- await access(this.socketPath);
1057
- const mysql = await import("mysql2/promise");
1058
- this._pool = mysql.createPool({
1059
- socketPath: this.socketPath,
1060
- user: "root",
1061
- database: "doltdb",
1062
- waitForConnections: true,
1063
- connectionLimit: 5
1064
- });
1065
- this._useCliMode = false;
1066
- log$1.debug("Connected via unix socket: %s", this.socketPath);
1067
- } catch {
1068
- this._useCliMode = true;
1069
- log$1.debug("Unix socket not available, using CLI fallback for %s", this.repoPath);
1070
- }
1071
- this._connected = true;
1072
- }
1073
- async query(sql, params, branch) {
1074
- if (!this._connected) await this.connect();
1075
- if (this._useCliMode) return this._queryCli(sql, params, branch);
1076
- return this._queryPool(sql, params, branch);
1077
- }
1078
- async _queryPool(sql, params, branch) {
1079
- try {
1080
- if (branch !== void 0 && branch !== "main") {
1081
- const conn = await this._pool.getConnection();
1082
- try {
1083
- await conn.execute(`USE \`substrate/${branch}\``);
1084
- const [rows$1] = await conn.execute(sql, params);
1085
- return rows$1;
1086
- } finally {
1087
- conn.release();
1088
- }
1089
- }
1090
- const [rows] = await this._pool.execute(sql, params);
1091
- return rows;
1092
- } catch (err) {
1093
- const detail = err instanceof Error ? err.message : String(err);
1094
- throw new DoltQueryError(sql, detail);
1095
- }
1096
- }
1097
- /**
1098
- * Acquire an exclusive CLI lock. Dolt CLI takes an exclusive lock on the noms
1099
- * manifest, so concurrent `dolt sql -q` / `dolt <subcommand>` processes
1100
- * produce "cannot update manifest: database is read only" errors.
1101
- * Serialize all CLI operations through a single promise chain.
1102
- */
1103
- _withCliLock(fn) {
1104
- const prev = this._cliMutex;
1105
- let release;
1106
- this._cliMutex = new Promise((resolve$2) => {
1107
- release = resolve$2;
1108
- });
1109
- return prev.then(fn).finally(() => release());
1110
- }
1111
- async _queryCli(sql, params, branch) {
1112
- let resolvedSql = sql;
1113
- if (params && params.length > 0) {
1114
- let i = 0;
1115
- resolvedSql = sql.replace(/\?/g, () => {
1116
- const val = params[i++];
1117
- if (val === null || val === void 0) return "NULL";
1118
- if (typeof val === "number") return String(val);
1119
- return `'${String(val).replace(/'/g, "''")}'`;
1120
- });
1121
- }
1122
- const finalSql = resolvedSql;
1123
- return this._withCliLock(async () => {
1124
- try {
1125
- const branchPrefix = branch ? `CALL DOLT_CHECKOUT('${branch.replace(/'/g, "''")}'); ` : "";
1126
- const args = [
1127
- "sql",
1128
- "-q",
1129
- branchPrefix + finalSql,
1130
- "--result-format",
1131
- "json"
1132
- ];
1133
- const { stdout } = await runExecFile("dolt", args, { cwd: this.repoPath });
1134
- const lines = (stdout || "").trim().split("\n").filter(Boolean);
1135
- const lastLine = lines.length > 0 ? lines[lines.length - 1] : "{\"rows\":[]}";
1136
- const parsed = JSON.parse(lastLine);
1137
- return parsed.rows ?? [];
1138
- } catch (err) {
1139
- const detail = err instanceof Error ? err.message : String(err);
1140
- throw new DoltQueryError(finalSql, detail);
1141
- }
1142
- });
1143
- }
1144
- /**
1145
- * Execute a raw Dolt CLI command (e.g. `dolt diff main...story/26-1 --stat`)
1146
- * and return the stdout as a string.
1147
- *
1148
- * This is distinct from `query()` which runs SQL. Use `exec()` for Dolt
1149
- * sub-commands like `diff`, `log`, `branch`, etc.
1150
- */
1151
- async exec(command) {
1152
- const parts = command.trim().split(/\s+/);
1153
- const cmdArgs = parts[0] === "dolt" ? parts.slice(1) : parts;
1154
- return this.execArgs(cmdArgs);
1155
- }
1156
- /**
1157
- * Execute a Dolt CLI command with pre-split arguments.
1158
- *
1159
- * Use this instead of `exec()` when arguments contain spaces (e.g. commit
1160
- * messages) to avoid whitespace-splitting issues.
1161
- */
1162
- async execArgs(args) {
1163
- return this._withCliLock(async () => {
1164
- try {
1165
- const { stdout } = await runExecFile("dolt", args, { cwd: this.repoPath });
1166
- return stdout;
1167
- } catch (err) {
1168
- const detail = err instanceof Error ? err.message : String(err);
1169
- throw new DoltQueryError(args.join(" "), detail);
1170
- }
1171
- });
1172
- }
1173
- async close() {
1174
- if (this._pool) {
1175
- await this._pool.end();
1176
- this._pool = null;
1177
- }
1178
- this._connected = false;
1179
- }
1180
- };
1181
- function createDoltClient(options) {
1182
- return new DoltClient(options);
1183
- }
1184
-
1185
83
  //#endregion
1186
84
  //#region src/persistence/adapter.ts
1187
- const logger$2 = createLogger("persistence:adapter");
1188
- /** Type guard: check if a DatabaseAdapter also implements SyncAdapter. */
1189
- function isSyncAdapter(adapter) {
1190
- return typeof adapter.querySync === "function";
1191
- }
1192
85
  /**
1193
- * Synchronously check whether Dolt is installed on PATH and a Dolt repo
1194
- * exists at the canonical state path under `basePath`.
1195
- */
1196
- function isDoltAvailable(basePath) {
1197
- const result = spawnSync("dolt", ["version"], { stdio: "ignore" });
1198
- if (result.error != null || result.status !== 0) return false;
1199
- const stateDoltDir = join$1(basePath, ".substrate", "state", ".dolt");
1200
- return existsSync$1(stateDoltDir);
1201
- }
1202
- /**
1203
- * Create a `DatabaseAdapter` for the specified (or auto-detected) backend.
86
+ * Create a DatabaseAdapter for the specified (or auto-detected) backend.
1204
87
  *
1205
- * @param config - Optional configuration. Defaults to `{ backend: 'auto' }`.
1206
- * @returns A `DatabaseAdapter` instance ready for use.
1207
- */
1208
- function createDatabaseAdapter(config = { backend: "auto" }) {
1209
- const backend = config.backend ?? "auto";
1210
- const basePath = config.basePath ?? process.cwd();
1211
- const doltRepoPath = join$1(basePath, ".substrate", "state");
1212
- if (backend === "dolt") {
1213
- logger$2.debug("Using DoltDatabaseAdapter (explicit config)");
1214
- const client = new DoltClient({ repoPath: doltRepoPath });
1215
- return new DoltDatabaseAdapter(client);
1216
- }
1217
- if (backend === "memory") {
1218
- logger$2.debug("Using InMemoryDatabaseAdapter (explicit config)");
1219
- return new InMemoryDatabaseAdapter();
1220
- }
1221
- if (isDoltAvailable(basePath)) {
1222
- logger$2.debug("Dolt detected, using DoltDatabaseAdapter");
1223
- const client = new DoltClient({ repoPath: doltRepoPath });
1224
- return new DoltDatabaseAdapter(client);
1225
- }
1226
- logger$2.debug("Dolt not available, using InMemoryDatabaseAdapter");
1227
- return new InMemoryDatabaseAdapter();
1228
- }
1229
-
1230
- //#endregion
1231
- //#region src/persistence/schema.ts
1232
- /**
1233
- * Initialize all persistence tables on the given adapter.
1234
- * Idempotent — safe to call multiple times.
88
+ * This shim wraps the core factory and injects the concrete DoltClient
89
+ * constructor as the doltClientFactory parameter, so monolith callers
90
+ * get Dolt support transparently.
1235
91
  */
1236
- async function initSchema(adapter) {
1237
- await adapter.exec(`
1238
- CREATE TABLE IF NOT EXISTS sessions (
1239
- id VARCHAR(255) PRIMARY KEY,
1240
- name TEXT,
1241
- graph_file TEXT NOT NULL,
1242
- status VARCHAR(32) NOT NULL DEFAULT 'active',
1243
- budget_usd DOUBLE,
1244
- total_cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1245
- planning_cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1246
- config_snapshot TEXT,
1247
- base_branch TEXT NOT NULL DEFAULT 'main',
1248
- plan_source TEXT,
1249
- planning_agent TEXT,
1250
- planning_costs_count_against_budget INTEGER NOT NULL DEFAULT 0,
1251
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1252
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1253
- )
1254
- `);
1255
- await adapter.exec(`
1256
- CREATE TABLE IF NOT EXISTS tasks (
1257
- id VARCHAR(255) PRIMARY KEY,
1258
- session_id VARCHAR(255) NOT NULL,
1259
- name TEXT NOT NULL,
1260
- description TEXT,
1261
- prompt TEXT NOT NULL,
1262
- status VARCHAR(32) NOT NULL DEFAULT 'pending',
1263
- agent VARCHAR(128),
1264
- model TEXT,
1265
- billing_mode VARCHAR(32),
1266
- worktree_path TEXT,
1267
- worktree_branch TEXT,
1268
- worktree_cleaned_at TEXT,
1269
- worker_id TEXT,
1270
- budget_usd DOUBLE,
1271
- cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1272
- input_tokens INTEGER NOT NULL DEFAULT 0,
1273
- output_tokens INTEGER NOT NULL DEFAULT 0,
1274
- result TEXT,
1275
- error TEXT,
1276
- exit_code INTEGER,
1277
- retry_count INTEGER NOT NULL DEFAULT 0,
1278
- max_retries INTEGER NOT NULL DEFAULT 2,
1279
- timeout_ms INTEGER,
1280
- task_type TEXT,
1281
- metadata TEXT,
1282
- merge_status TEXT,
1283
- merged_files TEXT,
1284
- conflict_files TEXT,
1285
- budget_exceeded INTEGER NOT NULL DEFAULT 0,
1286
- started_at TEXT,
1287
- completed_at TEXT,
1288
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1289
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1290
- )
1291
- `);
1292
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id)");
1293
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)");
1294
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(agent)");
1295
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tasks_session_status ON tasks(session_id, status)");
1296
- await adapter.exec(`
1297
- CREATE TABLE IF NOT EXISTS task_dependencies (
1298
- task_id VARCHAR(255) NOT NULL,
1299
- depends_on VARCHAR(255) NOT NULL,
1300
- PRIMARY KEY (task_id, depends_on),
1301
- CHECK (task_id != depends_on)
1302
- )
1303
- `);
1304
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_deps_depends_on ON task_dependencies(depends_on)");
1305
- await adapter.exec(`
1306
- CREATE TABLE IF NOT EXISTS execution_log (
1307
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1308
- session_id VARCHAR(255) NOT NULL,
1309
- task_id VARCHAR(255),
1310
- event VARCHAR(128) NOT NULL,
1311
- old_status VARCHAR(32),
1312
- new_status VARCHAR(32),
1313
- agent VARCHAR(128),
1314
- cost_usd DOUBLE,
1315
- data TEXT,
1316
- timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1317
- )
1318
- `);
1319
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_log_session ON execution_log(session_id)");
1320
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_log_task ON execution_log(task_id)");
1321
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_log_event ON execution_log(event)");
1322
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_log_timestamp ON execution_log(timestamp)");
1323
- await adapter.exec(`
1324
- CREATE TABLE IF NOT EXISTS cost_entries (
1325
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1326
- session_id VARCHAR(255) NOT NULL,
1327
- task_id VARCHAR(255),
1328
- agent VARCHAR(128) NOT NULL,
1329
- billing_mode VARCHAR(32) NOT NULL,
1330
- category VARCHAR(64) NOT NULL DEFAULT 'execution',
1331
- provider VARCHAR(64) NOT NULL DEFAULT 'unknown',
1332
- input_tokens INTEGER NOT NULL DEFAULT 0,
1333
- output_tokens INTEGER NOT NULL DEFAULT 0,
1334
- estimated_cost DOUBLE NOT NULL DEFAULT 0.0,
1335
- actual_cost DOUBLE,
1336
- savings_usd DOUBLE NOT NULL DEFAULT 0.0,
1337
- model TEXT,
1338
- timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1339
- )
1340
- `);
1341
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_session ON cost_entries(session_id)");
1342
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_task ON cost_entries(task_id)");
1343
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_category ON cost_entries(category)");
1344
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_entries_session_task ON cost_entries(session_id, task_id)");
1345
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_entries_provider ON cost_entries(provider)");
1346
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_session_agent ON cost_entries(session_id, agent)");
1347
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_cost_agent ON cost_entries(agent)");
1348
- await adapter.exec(`
1349
- CREATE TABLE IF NOT EXISTS session_signals (
1350
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1351
- session_id VARCHAR(255) NOT NULL,
1352
- \`signal\` VARCHAR(16) NOT NULL CHECK(\`signal\` IN ('pause', 'resume', 'cancel')),
1353
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1354
- processed_at TEXT
1355
- )
1356
- `);
1357
- await adapter.exec(`
1358
- CREATE TABLE IF NOT EXISTS plans (
1359
- id VARCHAR(255) PRIMARY KEY,
1360
- description TEXT NOT NULL,
1361
- task_count INTEGER NOT NULL DEFAULT 0,
1362
- estimated_cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1363
- planning_agent VARCHAR(128) NOT NULL,
1364
- plan_yaml TEXT NOT NULL,
1365
- status VARCHAR(32) NOT NULL DEFAULT 'draft',
1366
- current_version INTEGER NOT NULL DEFAULT 1,
1367
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1368
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1369
- )
1370
- `);
1371
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
1372
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_plans_created ON plans(created_at)");
1373
- await adapter.exec(`
1374
- CREATE TABLE IF NOT EXISTS plan_versions (
1375
- plan_id VARCHAR(255) NOT NULL,
1376
- version INTEGER NOT NULL,
1377
- task_graph_yaml TEXT NOT NULL,
1378
- feedback_used TEXT,
1379
- planning_cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1380
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1381
- PRIMARY KEY (plan_id, version)
1382
- )
1383
- `);
1384
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_plan_versions_plan_id ON plan_versions(plan_id)");
1385
- await adapter.exec(`
1386
- CREATE TABLE IF NOT EXISTS pipeline_runs (
1387
- id VARCHAR(255) PRIMARY KEY,
1388
- methodology VARCHAR(128) NOT NULL,
1389
- current_phase VARCHAR(64),
1390
- status VARCHAR(32) NOT NULL DEFAULT 'running'
1391
- CHECK(status IN ('running','paused','completed','failed','stopped')),
1392
- config_json TEXT,
1393
- token_usage_json TEXT,
1394
- parent_run_id VARCHAR(255),
1395
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1396
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1397
- )
1398
- `);
1399
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_pipeline_runs_status ON pipeline_runs(status)");
1400
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_pipeline_runs_parent_run_id ON pipeline_runs(parent_run_id)");
1401
- await adapter.exec(`
1402
- CREATE TABLE IF NOT EXISTS decisions (
1403
- id VARCHAR(255) PRIMARY KEY,
1404
- pipeline_run_id VARCHAR(255),
1405
- phase VARCHAR(64) NOT NULL,
1406
- category VARCHAR(64) NOT NULL,
1407
- \`key\` VARCHAR(255) NOT NULL,
1408
- value TEXT NOT NULL,
1409
- rationale TEXT,
1410
- superseded_by VARCHAR(255),
1411
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1412
- updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1413
- )
1414
- `);
1415
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_decisions_phase ON decisions(phase)");
1416
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_decisions_key ON decisions(phase, `key`)");
1417
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_decisions_superseded_by ON decisions(superseded_by)");
1418
- await adapter.exec(`
1419
- CREATE TABLE IF NOT EXISTS requirements (
1420
- id VARCHAR(255) PRIMARY KEY,
1421
- pipeline_run_id VARCHAR(255),
1422
- source VARCHAR(128) NOT NULL,
1423
- type VARCHAR(32) NOT NULL CHECK(type IN ('functional','non_functional','constraint')),
1424
- description TEXT NOT NULL,
1425
- priority VARCHAR(16) NOT NULL CHECK(priority IN ('must','should','could','wont')),
1426
- status VARCHAR(32) NOT NULL DEFAULT 'active',
1427
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1428
- )
1429
- `);
1430
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_requirements_type ON requirements(type)");
1431
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_requirements_status ON requirements(status)");
1432
- await adapter.exec(`
1433
- CREATE TABLE IF NOT EXISTS constraints (
1434
- id VARCHAR(255) PRIMARY KEY,
1435
- pipeline_run_id VARCHAR(255),
1436
- category VARCHAR(64) NOT NULL,
1437
- description TEXT NOT NULL,
1438
- source VARCHAR(128) NOT NULL,
1439
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1440
- )
1441
- `);
1442
- await adapter.exec(`
1443
- CREATE TABLE IF NOT EXISTS artifacts (
1444
- id VARCHAR(255) PRIMARY KEY,
1445
- pipeline_run_id VARCHAR(255),
1446
- phase VARCHAR(64) NOT NULL,
1447
- type VARCHAR(128) NOT NULL,
1448
- path TEXT NOT NULL,
1449
- content_hash TEXT,
1450
- summary TEXT,
1451
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1452
- )
1453
- `);
1454
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_artifacts_phase ON artifacts(phase)");
1455
- await adapter.exec(`
1456
- CREATE TABLE IF NOT EXISTS token_usage (
1457
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1458
- pipeline_run_id VARCHAR(255),
1459
- phase VARCHAR(64) NOT NULL,
1460
- agent VARCHAR(128) NOT NULL,
1461
- input_tokens INTEGER NOT NULL DEFAULT 0,
1462
- output_tokens INTEGER NOT NULL DEFAULT 0,
1463
- cost_usd DOUBLE NOT NULL DEFAULT 0.0,
1464
- metadata TEXT,
1465
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1466
- )
1467
- `);
1468
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_token_usage_run ON token_usage(pipeline_run_id)");
1469
- await adapter.exec(`
1470
- CREATE TABLE IF NOT EXISTS run_metrics (
1471
- run_id VARCHAR(255) PRIMARY KEY,
1472
- methodology VARCHAR(128) NOT NULL,
1473
- status VARCHAR(32) NOT NULL DEFAULT 'running',
1474
- started_at TEXT NOT NULL,
1475
- completed_at TEXT,
1476
- wall_clock_seconds DOUBLE DEFAULT 0,
1477
- total_input_tokens INTEGER DEFAULT 0,
1478
- total_output_tokens INTEGER DEFAULT 0,
1479
- total_cost_usd DOUBLE DEFAULT 0,
1480
- stories_attempted INTEGER DEFAULT 0,
1481
- stories_succeeded INTEGER DEFAULT 0,
1482
- stories_failed INTEGER DEFAULT 0,
1483
- stories_escalated INTEGER DEFAULT 0,
1484
- total_review_cycles INTEGER DEFAULT 0,
1485
- total_dispatches INTEGER DEFAULT 0,
1486
- concurrency_setting INTEGER DEFAULT 1,
1487
- max_concurrent_actual INTEGER DEFAULT 1,
1488
- restarts INTEGER DEFAULT 0,
1489
- is_baseline INTEGER DEFAULT 0,
1490
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1491
- )
1492
- `);
1493
- await adapter.exec(`
1494
- CREATE TABLE IF NOT EXISTS story_metrics (
1495
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1496
- run_id VARCHAR(255) NOT NULL,
1497
- story_key VARCHAR(255) NOT NULL,
1498
- result VARCHAR(32) NOT NULL DEFAULT 'pending',
1499
- phase_durations_json TEXT,
1500
- started_at TEXT,
1501
- completed_at TEXT,
1502
- wall_clock_seconds DOUBLE DEFAULT 0,
1503
- input_tokens INTEGER DEFAULT 0,
1504
- output_tokens INTEGER DEFAULT 0,
1505
- cost_usd DOUBLE DEFAULT 0,
1506
- review_cycles INTEGER DEFAULT 0,
1507
- dispatches INTEGER DEFAULT 0,
1508
- created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1509
- UNIQUE(run_id, story_key)
1510
- )
1511
- `);
1512
- await adapter.exec(`
1513
- CREATE TABLE IF NOT EXISTS task_metrics (
1514
- task_id VARCHAR(255) NOT NULL,
1515
- agent VARCHAR(128) NOT NULL,
1516
- task_type VARCHAR(128) NOT NULL,
1517
- outcome VARCHAR(16) NOT NULL CHECK(outcome IN ('success', 'failure')),
1518
- failure_reason TEXT,
1519
- input_tokens INTEGER NOT NULL DEFAULT 0,
1520
- output_tokens INTEGER NOT NULL DEFAULT 0,
1521
- duration_ms INTEGER NOT NULL DEFAULT 0,
1522
- cost DOUBLE NOT NULL DEFAULT 0.0,
1523
- estimated_cost DOUBLE NOT NULL DEFAULT 0.0,
1524
- billing_mode VARCHAR(32) NOT NULL DEFAULT 'api',
1525
- retries INTEGER NOT NULL DEFAULT 0,
1526
- recorded_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1527
- PRIMARY KEY (task_id, recorded_at)
1528
- )
1529
- `);
1530
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tm_agent ON task_metrics(agent)");
1531
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tm_task_type ON task_metrics(task_type)");
1532
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tm_recorded_at ON task_metrics(recorded_at)");
1533
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_tm_agent_type ON task_metrics(agent, task_type)");
1534
- await adapter.exec(`
1535
- CREATE TABLE IF NOT EXISTS performance_aggregates (
1536
- agent VARCHAR(255) NOT NULL,
1537
- task_type VARCHAR(255) NOT NULL,
1538
- total_tasks INTEGER NOT NULL DEFAULT 0,
1539
- successful_tasks INTEGER NOT NULL DEFAULT 0,
1540
- failed_tasks INTEGER NOT NULL DEFAULT 0,
1541
- total_input_tokens INTEGER NOT NULL DEFAULT 0,
1542
- total_output_tokens INTEGER NOT NULL DEFAULT 0,
1543
- total_duration_ms INTEGER NOT NULL DEFAULT 0,
1544
- total_cost DOUBLE NOT NULL DEFAULT 0.0,
1545
- total_retries INTEGER NOT NULL DEFAULT 0,
1546
- last_updated DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1547
- PRIMARY KEY (agent, task_type)
1548
- )
1549
- `);
1550
- await adapter.exec(`
1551
- CREATE TABLE IF NOT EXISTS routing_recommendations (
1552
- id INTEGER PRIMARY KEY AUTO_INCREMENT,
1553
- task_type VARCHAR(128) NOT NULL,
1554
- current_agent VARCHAR(128) NOT NULL,
1555
- recommended_agent VARCHAR(128) NOT NULL,
1556
- reason TEXT,
1557
- confidence DOUBLE NOT NULL DEFAULT 0.0,
1558
- supporting_data TEXT,
1559
- generated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
1560
- expires_at TEXT
1561
- )
1562
- `);
1563
- await adapter.exec(`
1564
- CREATE TABLE IF NOT EXISTS turn_analysis (
1565
- story_key VARCHAR(64) NOT NULL,
1566
- span_id VARCHAR(128) NOT NULL,
1567
- turn_number INTEGER NOT NULL,
1568
- name VARCHAR(255) NOT NULL DEFAULT '',
1569
- timestamp BIGINT NOT NULL DEFAULT 0,
1570
- source VARCHAR(32) NOT NULL DEFAULT '',
1571
- model VARCHAR(64),
1572
- input_tokens INTEGER NOT NULL DEFAULT 0,
1573
- output_tokens INTEGER NOT NULL DEFAULT 0,
1574
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
1575
- fresh_tokens INTEGER NOT NULL DEFAULT 0,
1576
- cache_hit_rate DOUBLE NOT NULL DEFAULT 0,
1577
- cost_usd DOUBLE NOT NULL DEFAULT 0,
1578
- duration_ms INTEGER NOT NULL DEFAULT 0,
1579
- context_size INTEGER NOT NULL DEFAULT 0,
1580
- context_delta INTEGER NOT NULL DEFAULT 0,
1581
- tool_name VARCHAR(128),
1582
- is_context_spike BOOLEAN NOT NULL DEFAULT 0,
1583
- child_spans_json TEXT NOT NULL DEFAULT '[]',
1584
- task_type VARCHAR(64),
1585
- phase VARCHAR(64),
1586
- dispatch_id VARCHAR(64),
1587
- PRIMARY KEY (story_key, span_id)
1588
- )
1589
- `);
1590
- await adapter.exec("CREATE INDEX IF NOT EXISTS idx_turn_analysis_story ON turn_analysis (story_key, turn_number)");
1591
- for (const col of [
1592
- "task_type",
1593
- "phase",
1594
- "dispatch_id"
1595
- ]) try {
1596
- await adapter.exec(`ALTER TABLE turn_analysis ADD COLUMN ${col} VARCHAR(64)`);
1597
- } catch {}
1598
- await adapter.exec(`
1599
- CREATE TABLE IF NOT EXISTS efficiency_scores (
1600
- story_key VARCHAR(64) NOT NULL,
1601
- timestamp BIGINT NOT NULL,
1602
- composite_score INTEGER NOT NULL DEFAULT 0,
1603
- cache_hit_sub_score DOUBLE NOT NULL DEFAULT 0,
1604
- io_ratio_sub_score DOUBLE NOT NULL DEFAULT 0,
1605
- context_management_sub_score DOUBLE NOT NULL DEFAULT 0,
1606
- avg_cache_hit_rate DOUBLE NOT NULL DEFAULT 0,
1607
- avg_io_ratio DOUBLE NOT NULL DEFAULT 0,
1608
- context_spike_count INTEGER NOT NULL DEFAULT 0,
1609
- total_turns INTEGER NOT NULL DEFAULT 0,
1610
- per_model_json TEXT NOT NULL DEFAULT '[]',
1611
- per_source_json TEXT NOT NULL DEFAULT '[]',
1612
- dispatch_id TEXT,
1613
- task_type TEXT,
1614
- phase TEXT,
1615
- PRIMARY KEY (story_key, timestamp)
1616
- )
1617
- `);
1618
- for (const col of [
1619
- "dispatch_id",
1620
- "task_type",
1621
- "phase"
1622
- ]) try {
1623
- await adapter.exec(`ALTER TABLE efficiency_scores ADD COLUMN ${col} TEXT`);
1624
- } catch {}
1625
- await adapter.exec(`
1626
- CREATE TABLE IF NOT EXISTS recommendations (
1627
- id VARCHAR(16) NOT NULL,
1628
- story_key VARCHAR(64) NOT NULL,
1629
- sprint_id VARCHAR(64),
1630
- rule_id VARCHAR(64) NOT NULL,
1631
- severity VARCHAR(16) NOT NULL,
1632
- title TEXT NOT NULL,
1633
- description TEXT NOT NULL,
1634
- potential_savings_tokens INTEGER,
1635
- potential_savings_usd DOUBLE,
1636
- action_target TEXT,
1637
- generated_at VARCHAR(32) NOT NULL,
1638
- PRIMARY KEY (id)
1639
- )
1640
- `);
1641
- await adapter.exec(`
1642
- CREATE TABLE IF NOT EXISTS category_stats (
1643
- story_key VARCHAR(100) NOT NULL,
1644
- category VARCHAR(30) NOT NULL,
1645
- total_tokens BIGINT NOT NULL DEFAULT 0,
1646
- percentage DECIMAL(6,3) NOT NULL DEFAULT 0,
1647
- event_count INTEGER NOT NULL DEFAULT 0,
1648
- avg_tokens_per_event DECIMAL(12,2) NOT NULL DEFAULT 0,
1649
- trend VARCHAR(10) NOT NULL DEFAULT 'stable',
1650
- PRIMARY KEY (story_key, category)
1651
- )
1652
- `);
1653
- await adapter.exec(`
1654
- CREATE TABLE IF NOT EXISTS consumer_stats (
1655
- story_key VARCHAR(100) NOT NULL,
1656
- consumer_key VARCHAR(300) NOT NULL,
1657
- category VARCHAR(30) NOT NULL,
1658
- total_tokens BIGINT NOT NULL DEFAULT 0,
1659
- percentage DECIMAL(6,3) NOT NULL DEFAULT 0,
1660
- event_count INTEGER NOT NULL DEFAULT 0,
1661
- top_invocations_json TEXT,
1662
- PRIMARY KEY (story_key, consumer_key)
1663
- )
1664
- `);
1665
- await adapter.exec(`
1666
- CREATE VIEW IF NOT EXISTS ready_tasks AS
1667
- SELECT t.* FROM tasks t
1668
- WHERE t.status = 'pending'
1669
- AND NOT EXISTS (
1670
- SELECT 1 FROM task_dependencies td
1671
- JOIN tasks dep ON dep.id = td.depends_on
1672
- WHERE td.task_id = t.id
1673
- AND dep.status NOT IN ('completed', 'cancelled')
1674
- )
1675
- `);
1676
- await adapter.exec(`
1677
- CREATE VIEW IF NOT EXISTS session_cost_summary AS
1678
- SELECT
1679
- s.id AS session_id,
1680
- s.name AS session_name,
1681
- COUNT(DISTINCT t.id) AS total_tasks,
1682
- SUM(CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END) AS completed_tasks,
1683
- SUM(CASE WHEN t.status = 'failed' THEN 1 ELSE 0 END) AS failed_tasks,
1684
- SUM(CASE WHEN t.status = 'running' THEN 1 ELSE 0 END) AS running_tasks,
1685
- COALESCE(SUM(t.cost_usd), 0) AS total_cost_usd,
1686
- SUM(CASE WHEN t.billing_mode = 'subscription' THEN t.cost_usd ELSE 0 END) AS subscription_cost_usd,
1687
- SUM(CASE WHEN t.billing_mode = 'api' THEN t.cost_usd ELSE 0 END) AS api_cost_usd,
1688
- s.planning_cost_usd
1689
- FROM sessions s
1690
- LEFT JOIN tasks t ON t.session_id = s.id
1691
- GROUP BY s.id
1692
- `);
1693
- await adapter.exec(`
1694
- CREATE TABLE IF NOT EXISTS schema_migrations (
1695
- version INTEGER PRIMARY KEY,
1696
- name TEXT NOT NULL,
1697
- applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
1698
- )
1699
- `);
1700
- }
1701
-
1702
- //#endregion
1703
- //#region src/modules/state/dolt-init.ts
1704
- /**
1705
- * Thrown when the `dolt` binary cannot be found in PATH.
1706
- */
1707
- var DoltNotInstalled = class extends Error {
1708
- constructor() {
1709
- super("Dolt CLI not found in PATH. Install Dolt from https://docs.dolthub.com/introduction/installation");
1710
- this.name = "DoltNotInstalled";
1711
- }
1712
- };
1713
- /**
1714
- * Thrown when a Dolt CLI command exits with a non-zero status code.
1715
- */
1716
- var DoltInitError = class extends Error {
1717
- constructor(args, exitCode, stderr) {
1718
- super(`Dolt command "dolt ${args.join(" ")}" failed with exit code ${exitCode}${stderr ? `: ${stderr}` : ""}`);
1719
- this.name = "DoltInitError";
1720
- }
1721
- };
1722
- /**
1723
- * Verify that the `dolt` binary is installed and accessible.
1724
- *
1725
- * @throws {DoltNotInstalled} If the binary is not found in PATH.
1726
- */
1727
- async function checkDoltInstalled() {
1728
- return new Promise((resolve$2, reject) => {
1729
- let child;
1730
- try {
1731
- child = spawn("dolt", ["version"], { stdio: "ignore" });
1732
- } catch (err) {
1733
- const nodeErr = err;
1734
- if (nodeErr.code === "ENOENT") reject(new DoltNotInstalled());
1735
- else reject(err);
1736
- return;
1737
- }
1738
- child.on("error", (err) => {
1739
- if (err.code === "ENOENT") reject(new DoltNotInstalled());
1740
- else reject(err);
1741
- });
1742
- child.on("close", (code) => {
1743
- if (code === 0) resolve$2();
1744
- else resolve$2();
1745
- });
1746
- });
1747
- }
1748
- /**
1749
- * Run a Dolt CLI command in the given working directory.
1750
- *
1751
- * @param args - Arguments to pass to `dolt` (e.g. `['init']`).
1752
- * @param cwd - Working directory for the command.
1753
- * @throws {DoltInitError} If the command exits with a non-zero code.
1754
- */
1755
- async function runDoltCommand(args, cwd) {
1756
- return new Promise((resolve$2, reject) => {
1757
- const stderrChunks = [];
1758
- const child = spawn("dolt", args, {
1759
- cwd,
1760
- stdio: [
1761
- "ignore",
1762
- "ignore",
1763
- "pipe"
1764
- ]
1765
- });
1766
- child.stderr?.on("data", (chunk) => {
1767
- stderrChunks.push(chunk);
1768
- });
1769
- child.on("error", (err) => {
1770
- reject(err);
1771
- });
1772
- child.on("close", (code) => {
1773
- if (code === 0) resolve$2();
1774
- else {
1775
- const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
1776
- reject(new DoltInitError(args, code ?? -1, stderr));
1777
- }
1778
- });
1779
- });
1780
- }
1781
- /**
1782
- * Ensure that Dolt has a global user identity configured.
1783
- * `dolt init` and `dolt commit` fail with "empty ident name not allowed"
1784
- * when no identity exists. This function checks for an existing identity
1785
- * and configures a default one if absent.
1786
- */
1787
- async function ensureDoltIdentity() {
1788
- const hasIdentity = await doltConfigGet("user.name");
1789
- if (hasIdentity) return;
1790
- await runDoltConfigSet("user.name", "substrate");
1791
- await runDoltConfigSet("user.email", "substrate@localhost");
1792
- }
1793
- /**
1794
- * Check if a Dolt global config key has a value set.
1795
- */
1796
- async function doltConfigGet(key) {
1797
- return new Promise((resolve$2) => {
1798
- const child = spawn("dolt", [
1799
- "config",
1800
- "--global",
1801
- "--get",
1802
- key
1803
- ], { stdio: [
1804
- "ignore",
1805
- "ignore",
1806
- "ignore"
1807
- ] });
1808
- child.on("error", () => resolve$2(false));
1809
- child.on("close", (code) => resolve$2(code === 0));
1810
- });
1811
- }
1812
- /**
1813
- * Set a Dolt global config value.
1814
- */
1815
- async function runDoltConfigSet(key, value) {
1816
- return new Promise((resolve$2, reject) => {
1817
- const child = spawn("dolt", [
1818
- "config",
1819
- "--global",
1820
- "--add",
1821
- key,
1822
- value
1823
- ], { stdio: [
1824
- "ignore",
1825
- "ignore",
1826
- "pipe"
1827
- ] });
1828
- const stderrChunks = [];
1829
- child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
1830
- child.on("error", reject);
1831
- child.on("close", (code) => {
1832
- if (code === 0) resolve$2();
1833
- else {
1834
- const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
1835
- reject(new DoltInitError([
1836
- "config",
1837
- "--global",
1838
- "--add",
1839
- key,
1840
- value
1841
- ], code ?? -1, stderr));
1842
- }
1843
- });
1844
- });
1845
- }
1846
- /**
1847
- * Initialize a Dolt repository for Substrate state storage.
1848
- *
1849
- * This function is idempotent: running it a second time on an already-
1850
- * initialized repository is safe — `dolt init` is skipped, existing tables
1851
- * are not re-created (IF NOT EXISTS guards), and the schema version row is
1852
- * not duplicated (INSERT IGNORE).
1853
- *
1854
- * @param config - Initialization configuration.
1855
- * @throws {DoltNotInstalled} If the `dolt` binary is not in PATH.
1856
- * @throws {DoltInitError} If any Dolt CLI command fails.
1857
- */
1858
- async function initializeDolt(config) {
1859
- const statePath = config.statePath ?? join$1(config.projectRoot, ".substrate", "state");
1860
- const schemaPath = config.schemaPath ?? fileURLToPath(new URL("./schema.sql", import.meta.url));
1861
- await checkDoltInstalled();
1862
- await mkdir(statePath, { recursive: true });
1863
- await ensureDoltIdentity();
1864
- const doltDir = join$1(statePath, ".dolt");
1865
- let doltDirExists = false;
1866
- try {
1867
- await access(doltDir);
1868
- doltDirExists = true;
1869
- } catch {
1870
- doltDirExists = false;
1871
- }
1872
- if (!doltDirExists) await runDoltCommand(["init"], statePath);
1873
- await runDoltCommand([
1874
- "sql",
1875
- "-f",
1876
- schemaPath
1877
- ], statePath);
1878
- let hasCommits = false;
1879
- try {
1880
- await runDoltCommand(["log", "--oneline"], statePath);
1881
- hasCommits = await doltLogHasCommits(statePath);
1882
- } catch {
1883
- hasCommits = false;
1884
- }
1885
- if (!hasCommits) {
1886
- await runDoltCommand(["add", "-A"], statePath);
1887
- await runDoltCommand([
1888
- "commit",
1889
- "-m",
1890
- "Initialize substrate state schema v1"
1891
- ], statePath);
1892
- }
1893
- }
1894
- /**
1895
- * Returns `true` if there is at least one commit in the Dolt repo.
1896
- */
1897
- async function doltLogHasCommits(cwd) {
1898
- return new Promise((resolve$2) => {
1899
- const stdoutChunks = [];
1900
- const child = spawn("dolt", ["log", "--oneline"], {
1901
- cwd,
1902
- stdio: [
1903
- "ignore",
1904
- "pipe",
1905
- "ignore"
1906
- ]
1907
- });
1908
- child.stdout?.on("data", (chunk) => {
1909
- stdoutChunks.push(chunk);
1910
- });
1911
- child.on("error", () => resolve$2(false));
1912
- child.on("close", (code) => {
1913
- if (code !== 0) {
1914
- resolve$2(false);
1915
- return;
1916
- }
1917
- const output = Buffer.concat(stdoutChunks).toString("utf8").trim();
1918
- resolve$2(output.length > 0);
1919
- });
1920
- });
92
+ function createDatabaseAdapter$1(config) {
93
+ return createDatabaseAdapter(config, (repoPath) => new DoltClient({ repoPath }));
1921
94
  }
1922
95
 
1923
96
  //#endregion
@@ -1969,7 +142,7 @@ const __dirname = dirname(__filename);
1969
142
  function findPackageRoot(startDir) {
1970
143
  let dir = startDir;
1971
144
  while (dir !== dirname(dir)) {
1972
- if (existsSync(join(dir, "package.json"))) return dir;
145
+ if (existsSync$1(join(dir, "package.json"))) return dir;
1973
146
  dir = dirname(dir);
1974
147
  }
1975
148
  return startDir;
@@ -2667,6 +840,40 @@ var FileStateStore = class {
2667
840
  }
2668
841
  };
2669
842
 
843
+ //#endregion
844
+ //#region src/modules/state/errors.ts
845
+ /**
846
+ * Typed error classes for the Dolt state store.
847
+ */
848
+ var StateStoreError = class extends Error {
849
+ code;
850
+ constructor(code, message) {
851
+ super(message);
852
+ this.name = "StateStoreError";
853
+ this.code = code;
854
+ }
855
+ };
856
+ var DoltMergeConflictError = class extends StateStoreError {
857
+ table;
858
+ conflictingKeys;
859
+ rowKey;
860
+ ourValue;
861
+ theirValue;
862
+ constructor(table, conflictingKeys, options) {
863
+ super("DOLT_MERGE_CONFLICT", `Merge conflict in table '${table}' on keys: ${conflictingKeys.join(", ")}`);
864
+ this.name = "DoltMergeConflictError";
865
+ this.table = table;
866
+ this.conflictingKeys = conflictingKeys;
867
+ if (options) {
868
+ this.rowKey = options.rowKey;
869
+ this.ourValue = options.ourValue;
870
+ this.theirValue = options.theirValue;
871
+ }
872
+ }
873
+ };
874
+ /** Alias for DoltMergeConflictError — used by orchestrator branch lifecycle. */
875
+ const DoltMergeConflict = DoltMergeConflictError;
876
+
2670
877
  //#endregion
2671
878
  //#region src/modules/state/dolt-store.ts
2672
879
  const log = createLogger("modules:state:dolt");
@@ -3271,7 +1478,7 @@ function detectDoltAvailableSync(basePath) {
3271
1478
  reason: "dolt binary not found on PATH"
3272
1479
  };
3273
1480
  const stateDoltDir = join$1(basePath, ".substrate", "state", ".dolt");
3274
- const repoExists = existsSync$1(stateDoltDir);
1481
+ const repoExists = existsSync(stateDoltDir);
3275
1482
  if (!repoExists) return {
3276
1483
  available: false,
3277
1484
  reason: `Dolt repo not initialised at ${stateDoltDir}`
@@ -3366,7 +1573,7 @@ function inspectProcessTree(opts) {
3366
1573
  }
3367
1574
  const lines = psOutput.split("\n");
3368
1575
  if (substrateDirPath !== void 0) try {
3369
- const readFileSyncFn = readFileSyncOverride ?? ((path$1, encoding) => readFileSync$1(path$1, encoding));
1576
+ const readFileSyncFn = readFileSyncOverride ?? ((path$1, encoding) => readFileSync(path$1, encoding));
3370
1577
  const pidContent = readFileSyncFn(join(substrateDirPath, "orchestrator.pid"), "utf-8");
3371
1578
  const pid = parseInt(pidContent.trim(), 10);
3372
1579
  if (!isNaN(pid) && pid > 0) {
@@ -3470,7 +1677,7 @@ async function getAutoHealthData(options) {
3470
1677
  if (stateStoreConfig?.backend === "dolt" && stateStore) {
3471
1678
  const repoPath = stateStoreConfig.basePath ?? projectRoot;
3472
1679
  const doltDirPath = join(repoPath, ".dolt");
3473
- const initialized = existsSync$1(doltDirPath);
1680
+ const initialized = existsSync(doltDirPath);
3474
1681
  let responsive = false;
3475
1682
  let version;
3476
1683
  let branches;
@@ -3533,8 +1740,8 @@ async function getAutoHealthData(options) {
3533
1740
  ...doltStateInfo !== void 0 ? { dolt_state: doltStateInfo } : {}
3534
1741
  };
3535
1742
  const doltDir = join(dbRoot, ".substrate", "state", ".dolt");
3536
- if (!existsSync$1(dbPath) && !existsSync$1(doltDir)) return NO_PIPELINE;
3537
- const adapter = createDatabaseAdapter({
1743
+ if (!existsSync(dbPath) && !existsSync(doltDir)) return NO_PIPELINE;
1744
+ const adapter = createDatabaseAdapter$1({
3538
1745
  backend: "auto",
3539
1746
  basePath: dbRoot
3540
1747
  });
@@ -3546,7 +1753,7 @@ async function getAutoHealthData(options) {
3546
1753
  let currentRunId;
3547
1754
  try {
3548
1755
  const currentRunIdPath = join(dbRoot, ".substrate", "current-run-id");
3549
- const content = readFileSync$1(currentRunIdPath, "utf-8").trim();
1756
+ const content = readFileSync(currentRunIdPath, "utf-8").trim();
3550
1757
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3551
1758
  if (UUID_RE.test(content)) currentRunId = content;
3552
1759
  } catch {}
@@ -3687,7 +1894,7 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
3687
1894
  let stateStore;
3688
1895
  let stateStoreConfig;
3689
1896
  const doltStatePath = join(root, ".substrate", "state", ".dolt");
3690
- if (existsSync$1(doltStatePath)) {
1897
+ if (existsSync(doltStatePath)) {
3691
1898
  const basePath = join(root, ".substrate", "state");
3692
1899
  stateStoreConfig = {
3693
1900
  backend: "dolt",
@@ -3722,5 +1929,5 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
3722
1929
  }
3723
1930
 
3724
1931
  //#endregion
3725
- export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltClient, DoltMergeConflict, DoltNotInstalled, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, buildPipelineStatusOutput, checkDoltInstalled, createDatabaseAdapter, createDoltClient, createStateStore, detectCycles, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, inspectProcessTree, isOrchestratorProcessLine, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runHealthAction, validateStoryKey };
3726
- //# sourceMappingURL=health-C-VRJruD.js.map
1932
+ export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createStateStore, detectCycles, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runHealthAction, validateStoryKey };
1933
+ //# sourceMappingURL=health-CiDi90gC.js.map