@cat-factory/node-server 0.62.2 → 0.64.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,8 +1,8 @@
1
1
  import { LLM_WARNING_FINISH_REASONS } from '@cat-factory/kernel';
2
- import { agentRunKindSchema } from '@cat-factory/contracts';
2
+ import { agentRunKindSchema, decodeInitiativeRow } from '@cat-factory/contracts';
3
3
  import { decodeEnum, tryDecodeRows, blockInsertValues, blockPatchToColumns, rowToBlock, rowToExecution, executionToDetail, rowToPipeline, rowToSandboxExperiment, rowToSandboxFixture, rowToSandboxGrade, rowToSandboxPromptVersion, rowToSandboxRun, rowToWorkspace, } from '@cat-factory/server';
4
4
  import { and, asc, count, desc, eq, gte, inArray, isNull, lt, ne, notInArray, or, sql, } from 'drizzle-orm';
5
- import { accountInvitations, passwordResetTokens, accountSettings, localSettings, accounts, agentContextSnapshots, agentRuns, blocks, consensusSessions, incidentEnrichmentConnections, observabilityConnections, packageRegistryConnections, emailConnections, llmCallMetrics, provisioningLog, memberships, mergeThresholdPresets, releaseHealthConfigs, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, kaizenGradings, kaizenVerifiedCombos, clarityReviews, binaryArtifacts, brainstormSessions, sandboxPromptVersions, sandboxFixtures, sandboxExperiments, sandboxRuns, sandboxGrades, services, tokenUsage, trackerSettings, modelPresets, userIdentities, users, workspaceFragmentDefaults, workspaceServices, workspaceSettings, workspaces, } from '../db/schema.js';
5
+ import { accountInvitations, passwordResetTokens, accountSettings, localSettings, accounts, agentContextSnapshots, agentRuns, blocks, consensusSessions, incidentEnrichmentConnections, observabilityConnections, packageRegistryConnections, emailConnections, llmCallMetrics, provisioningLog, memberships, mergeThresholdPresets, releaseHealthConfigs, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, kaizenGradings, kaizenVerifiedCombos, clarityReviews, binaryArtifacts, brainstormSessions, initiatives, sandboxPromptVersions, sandboxFixtures, sandboxExperiments, sandboxRuns, sandboxGrades, services, tokenUsage, trackerSettings, modelPresets, userIdentities, users, workspaceFragmentDefaults, workspaceServices, workspaceSettings, workspaces, } from '../db/schema.js';
6
6
  // Drizzle/Postgres implementations of the core kernel repository ports. The
7
7
  // row<->domain mapping is the SAME shared mapping the Cloudflare D1 repos use
8
8
  // (@cat-factory/server), so behaviour matches across stores; this layer only owns
@@ -177,6 +177,13 @@ class DrizzleBlockRepository {
177
177
  .delete(blocks)
178
178
  .where(and(eq(blocks.workspace_id, workspaceId), inArray(blocks.id, ids)));
179
179
  }
180
+ async countActiveInternal(workspaceId) {
181
+ const [row] = await this.db
182
+ .select({ n: count() })
183
+ .from(blocks)
184
+ .where(and(eq(blocks.workspace_id, workspaceId), eq(blocks.internal, 1), eq(blocks.status, 'in_progress')));
185
+ return row?.n ?? 0;
186
+ }
180
187
  }
181
188
  class DrizzlePipelineRepository {
182
189
  db;
@@ -219,6 +226,7 @@ class DrizzlePipelineRepository {
219
226
  archived: pipeline.archived ? 1 : null,
220
227
  builtin: pipeline.builtin ? 1 : null,
221
228
  version: pipeline.version ?? null,
229
+ public: pipeline.public ? 1 : null,
222
230
  });
223
231
  }
224
232
  async update(workspaceId, pipeline) {
@@ -240,6 +248,7 @@ class DrizzlePipelineRepository {
240
248
  labels: pipeline.labels ? JSON.stringify(pipeline.labels) : null,
241
249
  archived: pipeline.archived ? 1 : null,
242
250
  version: pipeline.version ?? null,
251
+ public: pipeline.public ? 1 : null,
243
252
  })
244
253
  .where(and(eq(pipelines.workspace_id, workspaceId), eq(pipelines.id, pipeline.id)));
245
254
  }
@@ -2485,6 +2494,85 @@ export class DrizzleBrainstormSessionRepository {
2485
2494
  .where(and(eq(brainstormSessions.workspace_id, workspaceId), eq(brainstormSessions.block_id, blockId), eq(brainstormSessions.stage, stage)));
2486
2495
  }
2487
2496
  }
2497
+ // The row → entity decode (doc blob + column-lifted keys) is the shared
2498
+ // `decodeInitiativeRow` (contracts), so the Drizzle and D1 repos can't drift.
2499
+ const rowToInitiative = decodeInitiativeRow;
2500
+ /**
2501
+ * Initiatives over Postgres — the Drizzle mirror of the Worker's
2502
+ * `D1InitiativeRepository` (migration 0035). Behaviourally identical so the
2503
+ * cross-runtime conformance suite asserts the same CRUD + rev-guarded CAS against
2504
+ * both stores.
2505
+ */
2506
+ export class DrizzleInitiativeRepository {
2507
+ db;
2508
+ constructor(db) {
2509
+ this.db = db;
2510
+ }
2511
+ async get(workspaceId, id) {
2512
+ const rows = await this.db
2513
+ .select()
2514
+ .from(initiatives)
2515
+ .where(and(eq(initiatives.workspace_id, workspaceId), eq(initiatives.id, id)))
2516
+ .limit(1);
2517
+ return rows[0] ? rowToInitiative(rows[0]) : null;
2518
+ }
2519
+ async getByBlock(workspaceId, blockId) {
2520
+ const rows = await this.db
2521
+ .select()
2522
+ .from(initiatives)
2523
+ .where(and(eq(initiatives.workspace_id, workspaceId), eq(initiatives.block_id, blockId)))
2524
+ .limit(1);
2525
+ return rows[0] ? rowToInitiative(rows[0]) : null;
2526
+ }
2527
+ async list(workspaceId) {
2528
+ const rows = await this.db
2529
+ .select()
2530
+ .from(initiatives)
2531
+ .where(eq(initiatives.workspace_id, workspaceId))
2532
+ .orderBy(asc(initiatives.created_at));
2533
+ // Snapshot-facing list read: drop a corrupt row rather than failing the board load.
2534
+ return rows.map(rowToInitiative).filter((i) => i !== null);
2535
+ }
2536
+ async listExecuting() {
2537
+ const rows = await this.db
2538
+ .select()
2539
+ .from(initiatives)
2540
+ .where(eq(initiatives.status, 'executing'))
2541
+ .orderBy(asc(initiatives.created_at));
2542
+ return rows.map(rowToInitiative).filter((i) => i !== null);
2543
+ }
2544
+ async insert(workspaceId, initiative) {
2545
+ await this.db.insert(initiatives).values({
2546
+ workspace_id: workspaceId,
2547
+ id: initiative.id,
2548
+ block_id: initiative.blockId,
2549
+ slug: initiative.slug,
2550
+ status: initiative.status,
2551
+ rev: initiative.rev,
2552
+ doc: JSON.stringify(initiative),
2553
+ created_at: initiative.createdAt,
2554
+ updated_at: initiative.updatedAt,
2555
+ });
2556
+ }
2557
+ async compareAndSwap(workspaceId, next, expectedRev) {
2558
+ const result = await this.db
2559
+ .update(initiatives)
2560
+ .set({
2561
+ slug: next.slug,
2562
+ status: next.status,
2563
+ rev: next.rev,
2564
+ doc: JSON.stringify(next),
2565
+ updated_at: next.updatedAt,
2566
+ })
2567
+ .where(and(eq(initiatives.workspace_id, workspaceId), eq(initiatives.id, next.id), eq(initiatives.rev, expectedRev)));
2568
+ return (result.rowCount ?? 0) > 0;
2569
+ }
2570
+ async delete(workspaceId, id) {
2571
+ await this.db
2572
+ .delete(initiatives)
2573
+ .where(and(eq(initiatives.workspace_id, workspaceId), eq(initiatives.id, id)));
2574
+ }
2575
+ }
2488
2576
  function rowToMergePreset(row) {
2489
2577
  return {
2490
2578
  id: row.id,
@@ -3416,6 +3504,7 @@ export function createDrizzleRepositories(db, clock) {
3416
3504
  consensusSessionRepository: new DrizzleConsensusSessionRepository(db),
3417
3505
  clarityReviewRepository: new DrizzleClarityReviewRepository(db),
3418
3506
  brainstormSessionRepository: new DrizzleBrainstormSessionRepository(db),
3507
+ initiativeRepository: new DrizzleInitiativeRepository(db),
3419
3508
  mergePresetRepository: new DrizzleMergePresetRepository(db),
3420
3509
  workspaceSettingsRepository: new DrizzleWorkspaceSettingsRepository(db),
3421
3510
  observabilityConnectionRepository: new DrizzleObservabilityConnectionRepository(db),