substrate-ai 0.4.11 → 0.5.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.
@@ -1,3 +1,3 @@
1
- import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Db8GTbH2.js";
1
+ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-C6MF2Cax.js";
2
2
 
3
3
  export { getLatestRun };
@@ -135,60 +135,68 @@ const AddTokenUsageInputSchema = z.object({
135
135
  /**
136
136
  * Insert a new decision record with a generated UUID.
137
137
  */
138
- function createDecision(db, input) {
138
+ async function createDecision(adapter, input) {
139
139
  const validated = CreateDecisionInputSchema.parse(input);
140
140
  const id = crypto.randomUUID();
141
- const stmt = db.prepare(`
142
- INSERT INTO decisions (id, pipeline_run_id, phase, category, key, value, rationale)
143
- VALUES (?, ?, ?, ?, ?, ?, ?)
144
- `);
145
- stmt.run(id, validated.pipeline_run_id ?? null, validated.phase, validated.category, validated.key, validated.value, validated.rationale ?? null);
146
- const row = db.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
147
- return row;
141
+ await adapter.query(`INSERT INTO decisions (id, pipeline_run_id, phase, category, \`key\`, value, rationale)
142
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
143
+ id,
144
+ validated.pipeline_run_id ?? null,
145
+ validated.phase,
146
+ validated.category,
147
+ validated.key,
148
+ validated.value,
149
+ validated.rationale ?? null
150
+ ]);
151
+ const rows = await adapter.query("SELECT * FROM decisions WHERE id = ?", [id]);
152
+ return rows[0];
148
153
  }
149
154
  /**
150
155
  * Insert or update a decision record.
151
156
  * If a decision with the same pipeline_run_id, category, and key already exists,
152
157
  * update its value and rationale. Otherwise, insert a new record.
153
158
  */
154
- function upsertDecision(db, input) {
159
+ async function upsertDecision(adapter, input) {
155
160
  const validated = CreateDecisionInputSchema.parse(input);
156
- const existing = db.prepare("SELECT * FROM decisions WHERE pipeline_run_id = ? AND category = ? AND key = ? LIMIT 1").get(validated.pipeline_run_id ?? null, validated.category, validated.key);
161
+ const rows = await adapter.query("SELECT * FROM decisions WHERE pipeline_run_id = ? AND category = ? AND `key` = ? LIMIT 1", [
162
+ validated.pipeline_run_id ?? null,
163
+ validated.category,
164
+ validated.key
165
+ ]);
166
+ const existing = rows[0];
157
167
  if (existing) {
158
- updateDecision(db, existing.id, {
168
+ await updateDecision(adapter, existing.id, {
159
169
  value: validated.value,
160
170
  rationale: validated.rationale ?? void 0
161
171
  });
162
- return db.prepare("SELECT * FROM decisions WHERE id = ?").get(existing.id);
172
+ const updated = await adapter.query("SELECT * FROM decisions WHERE id = ?", [existing.id]);
173
+ return updated[0];
163
174
  }
164
- return createDecision(db, input);
175
+ return createDecision(adapter, input);
165
176
  }
166
177
  /**
167
178
  * Get all decisions for a given phase, ordered by created_at ascending.
168
179
  */
169
- function getDecisionsByPhase(db, phase) {
170
- const stmt = db.prepare("SELECT * FROM decisions WHERE phase = ? ORDER BY created_at ASC");
171
- return stmt.all(phase);
180
+ async function getDecisionsByPhase(adapter, phase) {
181
+ return adapter.query("SELECT * FROM decisions WHERE phase = ? ORDER BY created_at ASC", [phase]);
172
182
  }
173
183
  /**
174
184
  * Get all decisions for a given phase scoped to a specific pipeline run,
175
185
  * ordered by created_at ascending.
176
186
  */
177
- function getDecisionsByPhaseForRun(db, runId, phase) {
178
- const stmt = db.prepare("SELECT * FROM decisions WHERE pipeline_run_id = ? AND phase = ? ORDER BY created_at ASC");
179
- return stmt.all(runId, phase);
187
+ async function getDecisionsByPhaseForRun(adapter, runId, phase) {
188
+ return adapter.query("SELECT * FROM decisions WHERE pipeline_run_id = ? AND phase = ? ORDER BY created_at ASC", [runId, phase]);
180
189
  }
181
190
  /**
182
191
  * Get all decisions for a given category, ordered by created_at ascending.
183
192
  */
184
- function getDecisionsByCategory(db, category) {
185
- const stmt = db.prepare("SELECT * FROM decisions WHERE category = ? ORDER BY created_at ASC");
186
- return stmt.all(category);
193
+ async function getDecisionsByCategory(adapter, category) {
194
+ return adapter.query("SELECT * FROM decisions WHERE category = ? ORDER BY created_at ASC", [category]);
187
195
  }
188
196
  /**
189
197
  * Update a decision's value and/or rationale and set updated_at.
190
198
  */
191
- function updateDecision(db, id, updates) {
199
+ async function updateDecision(adapter, id, updates) {
192
200
  const setClauses = [];
193
201
  const values = [];
194
202
  if (updates.value !== void 0) {
@@ -200,29 +208,33 @@ function updateDecision(db, id, updates) {
200
208
  values.push(updates.rationale);
201
209
  }
202
210
  if (setClauses.length === 0) return;
203
- setClauses.push("updated_at = datetime('now')");
211
+ setClauses.push("updated_at = ?");
212
+ values.push(new Date().toISOString());
204
213
  values.push(id);
205
- const stmt = db.prepare(`UPDATE decisions SET ${setClauses.join(", ")} WHERE id = ?`);
206
- stmt.run(...values);
214
+ await adapter.query(`UPDATE decisions SET ${setClauses.join(", ")} WHERE id = ?`, values);
207
215
  }
208
216
  /**
209
217
  * Insert a new requirement with status = 'active'.
210
218
  */
211
- function createRequirement(db, input) {
219
+ async function createRequirement(adapter, input) {
212
220
  const validated = CreateRequirementInputSchema.parse(input);
213
221
  const id = crypto.randomUUID();
214
- const stmt = db.prepare(`
215
- INSERT INTO requirements (id, pipeline_run_id, source, type, description, priority, status)
216
- VALUES (?, ?, ?, ?, ?, ?, 'active')
217
- `);
218
- stmt.run(id, validated.pipeline_run_id ?? null, validated.source, validated.type, validated.description, validated.priority);
219
- const row = db.prepare("SELECT * FROM requirements WHERE id = ?").get(id);
220
- return row;
222
+ await adapter.query(`INSERT INTO requirements (id, pipeline_run_id, source, type, description, priority, status)
223
+ VALUES (?, ?, ?, ?, ?, ?, 'active')`, [
224
+ id,
225
+ validated.pipeline_run_id ?? null,
226
+ validated.source,
227
+ validated.type,
228
+ validated.description,
229
+ validated.priority
230
+ ]);
231
+ const rows = await adapter.query("SELECT * FROM requirements WHERE id = ?", [id]);
232
+ return rows[0];
221
233
  }
222
234
  /**
223
235
  * List requirements with optional filtering by type, priority, and status.
224
236
  */
225
- function listRequirements(db, filters) {
237
+ async function listRequirements(adapter, filters) {
226
238
  const conditions = [];
227
239
  const values = [];
228
240
  if (filters?.type !== void 0) {
@@ -238,71 +250,83 @@ function listRequirements(db, filters) {
238
250
  values.push(filters.status);
239
251
  }
240
252
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
241
- const stmt = db.prepare(`SELECT * FROM requirements ${where} ORDER BY created_at ASC`);
242
- return stmt.all(...values);
253
+ return adapter.query(`SELECT * FROM requirements ${where} ORDER BY created_at ASC`, values);
243
254
  }
244
255
  /**
245
256
  * Register a new artifact record.
246
257
  */
247
- function registerArtifact(db, input) {
258
+ async function registerArtifact(adapter, input) {
248
259
  const validated = RegisterArtifactInputSchema.parse(input);
249
260
  const id = crypto.randomUUID();
250
- const stmt = db.prepare(`
251
- INSERT INTO artifacts (id, pipeline_run_id, phase, type, path, content_hash, summary)
252
- VALUES (?, ?, ?, ?, ?, ?, ?)
253
- `);
254
- stmt.run(id, validated.pipeline_run_id ?? null, validated.phase, validated.type, validated.path, validated.content_hash ?? null, validated.summary ?? null);
255
- const row = db.prepare("SELECT * FROM artifacts WHERE id = ?").get(id);
256
- return row;
261
+ await adapter.query(`INSERT INTO artifacts (id, pipeline_run_id, phase, type, path, content_hash, summary)
262
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
263
+ id,
264
+ validated.pipeline_run_id ?? null,
265
+ validated.phase,
266
+ validated.type,
267
+ validated.path,
268
+ validated.content_hash ?? null,
269
+ validated.summary ?? null
270
+ ]);
271
+ const rows = await adapter.query("SELECT * FROM artifacts WHERE id = ?", [id]);
272
+ return rows[0];
257
273
  }
258
274
  /**
259
275
  * Get the latest artifact of a given type for a specific pipeline run.
260
276
  * Filters by pipeline_run_id, phase, and type.
261
277
  * Returns undefined if none found.
262
278
  */
263
- function getArtifactByTypeForRun(db, runId, phase, type) {
264
- const stmt = db.prepare("SELECT * FROM artifacts WHERE pipeline_run_id = ? AND phase = ? AND type = ? ORDER BY created_at DESC, rowid DESC LIMIT 1");
265
- return stmt.get(runId, phase, type);
279
+ async function getArtifactByTypeForRun(adapter, runId, phase, type) {
280
+ const rows = await adapter.query("SELECT * FROM artifacts WHERE pipeline_run_id = ? AND phase = ? AND type = ? ORDER BY created_at DESC, id DESC LIMIT 1", [
281
+ runId,
282
+ phase,
283
+ type
284
+ ]);
285
+ return rows[0];
266
286
  }
267
287
  /**
268
288
  * Get all artifacts registered for a specific pipeline run, ordered by created_at ascending.
269
289
  */
270
- function getArtifactsByRun(db, runId) {
271
- const stmt = db.prepare("SELECT * FROM artifacts WHERE pipeline_run_id = ? ORDER BY created_at ASC");
272
- return stmt.all(runId);
290
+ async function getArtifactsByRun(adapter, runId) {
291
+ return adapter.query("SELECT * FROM artifacts WHERE pipeline_run_id = ? ORDER BY created_at ASC", [runId]);
273
292
  }
274
293
  /**
275
294
  * Get a pipeline run by its ID. Returns undefined if not found.
276
295
  */
277
- function getPipelineRunById(db, id) {
278
- const stmt = db.prepare("SELECT * FROM pipeline_runs WHERE id = ?");
279
- return stmt.get(id);
296
+ async function getPipelineRunById(adapter, id) {
297
+ const rows = await adapter.query("SELECT * FROM pipeline_runs WHERE id = ?", [id]);
298
+ return rows[0];
280
299
  }
281
300
  /**
282
301
  * Update a pipeline run's config_json field.
283
302
  */
284
- function updatePipelineRunConfig(db, id, configJson) {
285
- const stmt = db.prepare("UPDATE pipeline_runs SET config_json = ?, updated_at = datetime('now') WHERE id = ?");
286
- stmt.run(configJson, id);
303
+ async function updatePipelineRunConfig(adapter, id, configJson) {
304
+ await adapter.query("UPDATE pipeline_runs SET config_json = ?, updated_at = ? WHERE id = ?", [
305
+ configJson,
306
+ new Date().toISOString(),
307
+ id
308
+ ]);
287
309
  }
288
310
  /**
289
311
  * Create a new pipeline run with status = 'running'.
290
312
  */
291
- function createPipelineRun(db, input) {
313
+ async function createPipelineRun(adapter, input) {
292
314
  const validated = CreatePipelineRunInputSchema.parse(input);
293
315
  const id = crypto.randomUUID();
294
- const stmt = db.prepare(`
295
- INSERT INTO pipeline_runs (id, methodology, current_phase, status, config_json)
296
- VALUES (?, ?, ?, 'running', ?)
297
- `);
298
- stmt.run(id, validated.methodology, validated.start_phase ?? null, validated.config_json ?? null);
299
- const row = db.prepare("SELECT * FROM pipeline_runs WHERE id = ?").get(id);
300
- return row;
316
+ await adapter.query(`INSERT INTO pipeline_runs (id, methodology, current_phase, status, config_json)
317
+ VALUES (?, ?, ?, 'running', ?)`, [
318
+ id,
319
+ validated.methodology,
320
+ validated.start_phase ?? null,
321
+ validated.config_json ?? null
322
+ ]);
323
+ const rows = await adapter.query("SELECT * FROM pipeline_runs WHERE id = ?", [id]);
324
+ return rows[0];
301
325
  }
302
326
  /**
303
327
  * Update a pipeline run's current_phase, status, and/or token_usage_json.
304
328
  */
305
- function updatePipelineRun(db, id, updates) {
329
+ async function updatePipelineRun(adapter, id, updates) {
306
330
  const setClauses = [];
307
331
  const values = [];
308
332
  if (updates.current_phase !== void 0) {
@@ -318,42 +342,45 @@ function updatePipelineRun(db, id, updates) {
318
342
  values.push(updates.token_usage_json);
319
343
  }
320
344
  if (setClauses.length === 0) return;
321
- setClauses.push("updated_at = datetime('now')");
345
+ setClauses.push("updated_at = ?");
346
+ values.push(new Date().toISOString());
322
347
  values.push(id);
323
- const stmt = db.prepare(`UPDATE pipeline_runs SET ${setClauses.join(", ")} WHERE id = ?`);
324
- stmt.run(...values);
348
+ await adapter.query(`UPDATE pipeline_runs SET ${setClauses.join(", ")} WHERE id = ?`, values);
325
349
  }
326
350
  /**
327
351
  * Get all pipeline runs with status = 'running'.
328
352
  */
329
- function getRunningPipelineRuns(db) {
330
- const stmt = db.prepare("SELECT * FROM pipeline_runs WHERE status = 'running'");
331
- return stmt.all();
353
+ async function getRunningPipelineRuns(adapter) {
354
+ return adapter.query("SELECT * FROM pipeline_runs WHERE status = 'running'");
332
355
  }
333
356
  /**
334
357
  * Get the most recently created pipeline run. Returns undefined if none found.
335
358
  */
336
- function getLatestRun(db) {
337
- const stmt = db.prepare("SELECT * FROM pipeline_runs ORDER BY created_at DESC, rowid DESC LIMIT 1");
338
- return stmt.get();
359
+ async function getLatestRun(adapter) {
360
+ const rows = await adapter.query("SELECT * FROM pipeline_runs ORDER BY created_at DESC, id DESC LIMIT 1");
361
+ return rows[0];
339
362
  }
340
363
  /**
341
364
  * Append a token usage record for a pipeline run.
342
365
  */
343
- function addTokenUsage(db, runId, usage) {
366
+ async function addTokenUsage(adapter, runId, usage) {
344
367
  const validated = AddTokenUsageInputSchema.parse(usage);
345
- const stmt = db.prepare(`
346
- INSERT INTO token_usage (pipeline_run_id, phase, agent, input_tokens, output_tokens, cost_usd, metadata)
347
- VALUES (?, ?, ?, ?, ?, ?, ?)
348
- `);
349
- stmt.run(runId, validated.phase, validated.agent, validated.input_tokens, validated.output_tokens, validated.cost_usd, validated.metadata ?? null);
368
+ await adapter.query(`INSERT INTO token_usage (pipeline_run_id, phase, agent, input_tokens, output_tokens, cost_usd, metadata)
369
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
370
+ runId,
371
+ validated.phase,
372
+ validated.agent,
373
+ validated.input_tokens,
374
+ validated.output_tokens,
375
+ validated.cost_usd,
376
+ validated.metadata ?? null
377
+ ]);
350
378
  }
351
379
  /**
352
380
  * Aggregate token usage by phase and agent for a given pipeline run.
353
381
  */
354
- function getTokenUsageSummary(db, runId) {
355
- const stmt = db.prepare(`
356
- SELECT
382
+ async function getTokenUsageSummary(adapter, runId) {
383
+ return adapter.query(`SELECT
357
384
  phase,
358
385
  agent,
359
386
  SUM(input_tokens) AS total_input_tokens,
@@ -362,11 +389,9 @@ function getTokenUsageSummary(db, runId) {
362
389
  FROM token_usage
363
390
  WHERE pipeline_run_id = ?
364
391
  GROUP BY phase, agent
365
- ORDER BY phase ASC, agent ASC
366
- `);
367
- return stmt.all(runId);
392
+ ORDER BY phase ASC, agent ASC`, [runId]);
368
393
  }
369
394
 
370
395
  //#endregion
371
396
  export { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision };
372
- //# sourceMappingURL=decisions-Db8GTbH2.js.map
397
+ //# sourceMappingURL=decisions-C6MF2Cax.js.map
@@ -1,6 +1,6 @@
1
1
  import "./logger-D2fS2ccL.js";
2
- import { createDecision } from "./decisions-Db8GTbH2.js";
3
- import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-C0_y8DAs.js";
2
+ import { createDecision } from "./decisions-C6MF2Cax.js";
3
+ import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-CidppHy-.js";
4
4
  import { spawnGit } from "./git-utils-C-fdrHF_.js";
5
5
  import { spawn } from "node:child_process";
6
6
  import { join } from "node:path";
@@ -350,10 +350,10 @@ function createExperimenter(config, deps) {
350
350
  * Returns false (budget exceeded) if the experiment used more than the cap.
351
351
  * Returns true (within budget) if the cap is satisfied or metrics are unavailable.
352
352
  */
353
- function isWithinTokenBudget(db, storyKey, baselineRunId, experimentRunId) {
353
+ async function isWithinTokenBudget(db, storyKey, baselineRunId, experimentRunId) {
354
354
  try {
355
- const baselineStories = getStory(db, baselineRunId);
356
- const experimentStories = getStory(db, experimentRunId);
355
+ const baselineStories = await getStory(db, baselineRunId);
356
+ const experimentStories = await getStory(db, experimentRunId);
357
357
  const baselineStory = baselineStories.find((m) => m.story_key === storyKey);
358
358
  const experimentStory = experimentStories.find((m) => m.story_key === storyKey);
359
359
  if (!baselineStory || !experimentStory) return true;
@@ -404,15 +404,15 @@ function createExperimenter(config, deps) {
404
404
  experimentRunId = runId;
405
405
  log(`[experimenter] Experiment run completed: ${runId} (exit: ${exitCode})`);
406
406
  currentPhase = "COMPARING";
407
- const baselineMetrics = getRun(db, baselineRunId);
408
- const experimentMetrics = getRun(db, runId);
407
+ const baselineMetrics = await getRun(db, baselineRunId);
408
+ const experimentMetrics = await getRun(db, runId);
409
409
  if (!baselineMetrics || !experimentMetrics) {
410
410
  log(`[experimenter] Warning: metrics unavailable for comparison`);
411
411
  verdict = "REGRESSED";
412
412
  caughtError = "Could not retrieve metrics for comparison";
413
413
  } else {
414
414
  deltas = computeDeltas(baselineMetrics, experimentMetrics);
415
- const withinBudget = isWithinTokenBudget(db, rec.story_key, baselineRunId, runId);
415
+ const withinBudget = await isWithinTokenBudget(db, rec.story_key, baselineRunId, runId);
416
416
  if (!withinBudget) {
417
417
  verdict = "REGRESSED";
418
418
  caughtError = `Token budget cap exceeded (${config.tokenBudgetMultiplier}x baseline)`;
@@ -463,7 +463,7 @@ function createExperimenter(config, deps) {
463
463
  try {
464
464
  const targetMetricValue = rec.type === "token_regression" ? rec.tokens_actual ?? 0 : rec.type === "review_cycles" ? rec.review_cycles ?? 0 : rec.timing_seconds ?? 0;
465
465
  const afterValue = rec.type === "token_regression" ? deltas.tokens_pct !== null ? Math.round(targetMetricValue * (1 + deltas.tokens_pct / 100)) : targetMetricValue : rec.type === "review_cycles" ? deltas.review_cycles_pct !== null ? Math.round(targetMetricValue * (1 + deltas.review_cycles_pct / 100)) : targetMetricValue : deltas.wall_clock_pct !== null ? Math.round(targetMetricValue * (1 + deltas.wall_clock_pct / 100)) : targetMetricValue;
466
- createDecision(db, {
466
+ await createDecision(db, {
467
467
  pipeline_run_id: baselineRunId,
468
468
  phase: "supervisor",
469
469
  category: EXPERIMENT_RESULT,
@@ -500,4 +500,4 @@ function createExperimenter(config, deps) {
500
500
 
501
501
  //#endregion
502
502
  export { createExperimenter };
503
- //# sourceMappingURL=experimenter-CvxtqzXz.js.map
503
+ //# sourceMappingURL=experimenter-CoR0k66d.js.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { childLogger, createLogger, logger } from "./logger-D2fS2ccL.js";
2
- import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-Cd-7lG5v.js";
2
+ import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-BkUvZSKJ.js";
3
3
  import { AdtError, BudgetExceededError, ConfigError, ConfigIncompatibleFormatError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError, assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-BihqWgVe.js";
4
4
 
5
5
  //#region src/core/di.ts