gencow 0.1.241 → 0.1.242

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 (37) hide show
  1. package/core/index.js +259 -140
  2. package/core/package.json +1 -1
  3. package/lib/component-readme.mjs +24 -55
  4. package/lib/deploy-command.mjs +6 -2
  5. package/lib/deploy-failure-diagnostic.mjs +86 -2
  6. package/lib/deployment-operation-poll.mjs +17 -33
  7. package/lib/init-command.mjs +39 -2
  8. package/lib/jobs-command.mjs +17 -1
  9. package/lib/readme-codegen.mjs +13 -20
  10. package/lib/static-deploy-command.mjs +39 -5
  11. package/package.json +2 -2
  12. package/runtime/server.mjs +12106 -9328
  13. package/runtime/tooling.mjs +259 -142
  14. package/scripts/bundle-core.mjs +3 -2
  15. package/templateFeature/agent/agent.ts +1 -1
  16. package/templateFeature/agent/manifest.json +1 -1
  17. package/templateFeature/ai/ai-facade.ts +16 -10
  18. package/templateFeature/ai/ai-provider.ts +16 -3
  19. package/templateFeature/ai/manifest.json +5 -5
  20. package/templateFeature/guardrails/guardrails.ts +22 -13
  21. package/templateFeature/guardrails/manifest.json +1 -1
  22. package/templateFeature/memory/manifest.json +1 -1
  23. package/templateFeature/memory/memory.ts +2 -1
  24. package/templateFeature/rag/manifest.json +1 -1
  25. package/templateFeature/rag/rag.ts +1 -1
  26. package/templateFeature/reranker/manifest.json +4 -9
  27. package/templateFeature/reranker/reranker.ts +3 -6
  28. package/templateFeature/search/manifest.json +1 -0
  29. package/templateFeature/search/schema-search.ts +1 -1
  30. package/templateFeature/search/search-index-contract.ts +20 -0
  31. package/templateFeature/tools/manifest.json +1 -1
  32. package/templates/SECURITY.md +18 -19
  33. package/templates/ai-chat/README.md +18 -28
  34. package/templates/ai-chat/chat.ts +11 -7
  35. package/templates/ai-chat/prompt.md +3 -3
  36. package/templates/fullstack/README.md +8 -33
  37. package/templates/fullstack/prompt.md +1 -1
package/core/index.js CHANGED
@@ -2925,15 +2925,8 @@ function deriveWorkflowStatus(status, currentStep) {
2925
2925
 
2926
2926
  // ../core/src/workflows-api.ts
2927
2927
  import { sql as sql4 } from "drizzle-orm";
2928
- var WORKFLOW_STATUSES = /* @__PURE__ */ new Set(["pending", "running", "completed", "failed"]);
2929
- var WORKFLOW_DERIVED_PENDING_STATUSES = /* @__PURE__ */ new Set(["queued", "waiting", "sleeping"]);
2930
- function rowsFromResult(result) {
2931
- if (Array.isArray(result)) return result;
2932
- if (result && typeof result === "object" && Array.isArray(result.rows)) {
2933
- return result.rows;
2934
- }
2935
- return [];
2936
- }
2928
+
2929
+ // ../core/src/workflow-public-read-model.ts
2937
2930
  function parseJsonField(value) {
2938
2931
  if (typeof value !== "string") return value;
2939
2932
  try {
@@ -2950,6 +2943,57 @@ function toIsoString(value) {
2950
2943
  function toOptionalIsoString(value) {
2951
2944
  return value ? toIsoString(value) : null;
2952
2945
  }
2946
+ function publicV2Status(status) {
2947
+ if (status === "completed" || status === "failed") return status;
2948
+ if (status === "cancelled") return "failed";
2949
+ if (status === "running") return "running";
2950
+ return "pending";
2951
+ }
2952
+ function publicV2DerivedStatus(status) {
2953
+ if (status === "queued" || status === "waiting" || status === "sleeping") return status;
2954
+ return publicV2Status(status);
2955
+ }
2956
+ function mapV2WorkflowSummary(row) {
2957
+ const error = parseJsonField(row.error_json);
2958
+ return {
2959
+ id: row.id,
2960
+ name: row.workflow_name,
2961
+ status: publicV2Status(row.status),
2962
+ derivedStatus: publicV2DerivedStatus(row.status),
2963
+ currentStep: row.current_step_key,
2964
+ error: typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(error.message) : null,
2965
+ errorCode: row.status === "cancelled" ? "cancelled" : row.error_code,
2966
+ retryCount: row.retry_count,
2967
+ maxRetries: row.max_retries,
2968
+ maxDurationMs: Number(row.max_active_duration_ms),
2969
+ startedAt: toIsoString(row.created_at),
2970
+ updatedAt: toIsoString(row.updated_at),
2971
+ completedAt: toOptionalIsoString(row.completed_at)
2972
+ };
2973
+ }
2974
+ function mapV2WorkflowStep(row) {
2975
+ const error = parseJsonField(row.error_json);
2976
+ return {
2977
+ name: row.step_key,
2978
+ status: row.status === "running" || row.status === "failed" || row.status === "completed" ? row.status : row.status === "skipped" ? "completed" : "pending",
2979
+ output: parseJsonField(row.output_json),
2980
+ error: typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(error.message) : null,
2981
+ startedAt: toOptionalIsoString(row.started_at),
2982
+ updatedAt: toIsoString(row.updated_at),
2983
+ completedAt: toOptionalIsoString(row.completed_at)
2984
+ };
2985
+ }
2986
+
2987
+ // ../core/src/workflows-api.ts
2988
+ var WORKFLOW_STATUSES = /* @__PURE__ */ new Set(["pending", "running", "completed", "failed"]);
2989
+ var WORKFLOW_DERIVED_PENDING_STATUSES = /* @__PURE__ */ new Set(["queued", "waiting", "sleeping"]);
2990
+ function rowsFromResult(result) {
2991
+ if (Array.isArray(result)) return result;
2992
+ if (result && typeof result === "object" && Array.isArray(result.rows)) {
2993
+ return result.rows;
2994
+ }
2995
+ return [];
2996
+ }
2953
2997
  function mapWorkflowSummary(row) {
2954
2998
  return {
2955
2999
  id: row.id,
@@ -2967,6 +3011,26 @@ function mapWorkflowSummary(row) {
2967
3011
  completedAt: toOptionalIsoString(row.completed_at)
2968
3012
  };
2969
3013
  }
3014
+ function mapJoinedWorkflowSummary(row) {
3015
+ if (!row.v2_id) return mapWorkflowSummary(row);
3016
+ return mapV2WorkflowSummary({
3017
+ id: row.v2_id,
3018
+ workflow_name: row.v2_name,
3019
+ status: row.v2_status,
3020
+ args_json: null,
3021
+ result_json: null,
3022
+ error_json: row.v2_error_json,
3023
+ error_code: row.v2_error_code,
3024
+ current_step_key: row.v2_current_step,
3025
+ retry_count: row.v2_retry_count,
3026
+ max_retries: row.v2_max_retries,
3027
+ max_active_duration_ms: row.v2_max_duration_ms,
3028
+ created_at: row.v2_created_at,
3029
+ updated_at: row.v2_updated_at,
3030
+ completed_at: row.v2_completed_at,
3031
+ user_id: null
3032
+ });
3033
+ }
2970
3034
  function mapWorkflowStep(row) {
2971
3035
  return {
2972
3036
  name: row.step_name,
@@ -2999,13 +3063,6 @@ function normalizeDerivedStatus(status) {
2999
3063
  }
3000
3064
  return normalizeStatus(status);
3001
3065
  }
3002
- function toWorkflowStatusFilter(status) {
3003
- if (status == null) return void 0;
3004
- if (WORKFLOW_DERIVED_PENDING_STATUSES.has(status)) {
3005
- return "pending";
3006
- }
3007
- return status;
3008
- }
3009
3066
  async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
3010
3067
  if (currentToken && currentToken.trim() !== "") return currentToken;
3011
3068
  const nextToken = createWorkflowRealtimeToken();
@@ -3028,7 +3085,8 @@ async function ensureWorkflowRealtimeToken(db, workflowId, currentToken) {
3028
3085
  return rereadToken && rereadToken.trim() !== "" ? rereadToken : null;
3029
3086
  }
3030
3087
  async function loadWorkflowSignalTarget(db, workflowId) {
3031
- const result = await db.execute(sql4`
3088
+ try {
3089
+ const result = await db.execute(sql4`
3032
3090
  SELECT
3033
3091
  id,
3034
3092
  name,
@@ -3038,8 +3096,12 @@ async function loadWorkflowSignalTarget(db, workflowId) {
3038
3096
  FROM _gencow_workflows
3039
3097
  WHERE id = ${workflowId}
3040
3098
  LIMIT 1
3041
- `);
3042
- return rowsFromResult(result)[0] ?? null;
3099
+ `);
3100
+ return rowsFromResult(result)[0] ?? null;
3101
+ } catch (error) {
3102
+ if (isMissingWorkflowV1Table(error)) return null;
3103
+ throw error;
3104
+ }
3043
3105
  }
3044
3106
  function isMissingWorkflowV2SignalSchemaError(error) {
3045
3107
  const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
@@ -3055,39 +3117,55 @@ function isMissingWorkflowV2PayloadSchemaError(error) {
3055
3117
  const message = error instanceof Error ? error.message : String(error);
3056
3118
  return code === "42P01" || causeCode === "42P01" || message.includes('relation "_gencow_workflow_runs_v2" does not exist') || message.includes("relation _gencow_workflow_runs_v2 does not exist") || message.includes('relation "_gencow_workflow_steps_v2" does not exist') || message.includes("relation _gencow_workflow_steps_v2 does not exist");
3057
3119
  }
3058
- async function loadWorkflowV2PayloadSnapshot(db, workflowId) {
3120
+ function isMissingWorkflowV1Table(error) {
3121
+ const cause = error && typeof error === "object" && "cause" in error ? error.cause : null;
3122
+ const message = `${error instanceof Error ? error.message : String(error)} ${cause instanceof Error ? cause.message : String(cause ?? "")}`;
3123
+ return /relation "?_gencow_workflows"? does not exist|relation "?_gencow_workflow_steps"? does not exist|relation "?_gencow_workflow_events"? does not exist/.test(
3124
+ message
3125
+ );
3126
+ }
3127
+ async function loadWorkflowV2Run(db, workflowId) {
3059
3128
  try {
3060
- const runResult = await db.execute(sql4`
3061
- SELECT args_json, result_json
3129
+ const result = await db.execute(sql4`
3130
+ SELECT id, workflow_name, status, args_json, result_json, error_json, error_code,
3131
+ current_step_key, retry_count, max_retries, max_active_duration_ms,
3132
+ created_at, updated_at, completed_at, user_id
3062
3133
  FROM _gencow_workflow_runs_v2
3063
3134
  WHERE id = ${workflowId}
3064
3135
  LIMIT 1
3065
3136
  `);
3066
- const run = rowsFromResult(runResult)[0];
3067
- if (!run) return null;
3137
+ return rowsFromResult(result)[0] ?? null;
3138
+ } catch (error) {
3139
+ if (isMissingWorkflowV2PayloadSchemaError(error)) return null;
3140
+ throw error;
3141
+ }
3142
+ }
3143
+ async function loadWorkflowV2Steps(db, workflowId) {
3144
+ try {
3068
3145
  const stepResult = await db.execute(sql4`
3069
- SELECT step_key, output_json
3146
+ SELECT step_key, status, output_json, error_json, started_at, updated_at, completed_at
3070
3147
  FROM _gencow_workflow_steps_v2
3071
3148
  WHERE run_id = ${workflowId}
3149
+ ORDER BY step_index ASC, COALESCE(started_at, updated_at) ASC, step_key ASC
3072
3150
  `);
3073
- return {
3074
- run,
3075
- steps: new Map(
3076
- rowsFromResult(stepResult).map((step) => [
3077
- step.step_key,
3078
- parseJsonField(step.output_json)
3079
- ])
3080
- )
3081
- };
3151
+ return rowsFromResult(stepResult);
3082
3152
  } catch (error) {
3083
- if (isMissingWorkflowV2PayloadSchemaError(error)) return null;
3153
+ if (isMissingWorkflowV2PayloadSchemaError(error)) return [];
3084
3154
  throw error;
3085
3155
  }
3086
3156
  }
3087
3157
  async function tryRecordWorkflowV2Signal(options) {
3088
3158
  try {
3089
- await options.db.execute(sql4`
3090
- WITH inserted AS (
3159
+ const result = await options.db.execute(sql4`
3160
+ WITH eligible_run AS (
3161
+ SELECT id
3162
+ FROM _gencow_workflow_runs_v2
3163
+ WHERE id = ${options.workflowId}
3164
+ AND status IN ('queued', 'running', 'sleeping', 'waiting')
3165
+ AND completed_at IS NULL
3166
+ AND cancel_requested_at IS NULL
3167
+ FOR UPDATE
3168
+ ), inserted AS (
3091
3169
  INSERT INTO _gencow_workflow_signals_v2 (
3092
3170
  id,
3093
3171
  run_id,
@@ -3095,30 +3173,15 @@ async function tryRecordWorkflowV2Signal(options) {
3095
3173
  payload_json,
3096
3174
  idempotency_key
3097
3175
  )
3098
- VALUES (
3099
- ${crypto.randomUUID()},
3100
- ${options.workflowId},
3176
+ SELECT
3177
+ ${options.signalId},
3178
+ eligible_run.id,
3101
3179
  ${options.event},
3102
3180
  ${workflowJsonb(options.payload)},
3103
- ${crypto.randomUUID()}
3104
- )
3181
+ ${options.signalId}
3182
+ FROM eligible_run
3105
3183
  RETURNING id, run_id
3106
3184
  ),
3107
- inserted_legacy AS (
3108
- INSERT INTO _gencow_workflow_events (
3109
- id,
3110
- workflow_id,
3111
- event_name,
3112
- payload
3113
- )
3114
- VALUES (
3115
- ${options.eventId},
3116
- ${options.workflowId},
3117
- ${options.event},
3118
- NULL
3119
- )
3120
- RETURNING id
3121
- ),
3122
3185
  queued_run AS (
3123
3186
  UPDATE _gencow_workflow_runs_v2 run
3124
3187
  SET
@@ -3160,17 +3223,34 @@ async function tryRecordWorkflowV2Signal(options) {
3160
3223
  )
3161
3224
  SELECT 1
3162
3225
  FROM inserted
3163
- LEFT JOIN inserted_legacy ON TRUE
3164
3226
  LEFT JOIN wake_notified ON TRUE
3165
3227
  `);
3166
- return true;
3228
+ return rowsFromResult(result).length > 0 ? "accepted" : "rejected";
3167
3229
  } catch (error) {
3168
- if (isMissingWorkflowV2SignalSchemaError(error)) return false;
3230
+ if (isMissingWorkflowV2SignalSchemaError(error)) return "absent";
3169
3231
  throw error;
3170
3232
  }
3171
3233
  }
3234
+ async function mirrorWorkflowV2SignalToV1(options) {
3235
+ try {
3236
+ await options.db.execute(sql4`
3237
+ INSERT INTO _gencow_workflow_events (id, workflow_id, event_name, payload)
3238
+ SELECT ${options.signalId}, workflow.id, ${options.event}, NULL
3239
+ FROM _gencow_workflows workflow
3240
+ WHERE workflow.id = ${options.workflowId}
3241
+ ON CONFLICT (id) DO NOTHING
3242
+ `);
3243
+ } catch (error) {
3244
+ if (!isMissingWorkflowV1Table(error)) {
3245
+ console.warn("Workflow V1 signal mirror failed", { signalId: options.signalId });
3246
+ }
3247
+ }
3248
+ }
3172
3249
  async function loadWorkflowSnapshot(db, workflowId, options) {
3173
- const workflowResult = await db.execute(sql4`
3250
+ const v2Run = await loadWorkflowV2Run(db, workflowId);
3251
+ let workflowResult;
3252
+ try {
3253
+ workflowResult = await db.execute(sql4`
3174
3254
  SELECT
3175
3255
  id,
3176
3256
  name,
@@ -3192,16 +3272,25 @@ async function loadWorkflowSnapshot(db, workflowId, options) {
3192
3272
  WHERE id = ${workflowId}
3193
3273
  LIMIT 1
3194
3274
  `);
3275
+ } catch (error) {
3276
+ if (!v2Run || !isMissingWorkflowV1Table(error)) throw error;
3277
+ workflowResult = [];
3278
+ }
3195
3279
  const row = rowsFromResult(workflowResult)[0] ?? null;
3196
- if (!row) return null;
3280
+ if (!v2Run && !row) return null;
3197
3281
  const viewerUserId = options?.viewerUserId ?? null;
3198
- if (options?.requireViewerMatch && row.user_id && row.user_id !== viewerUserId) {
3282
+ const ownerId = v2Run ? v2Run.user_id : row?.user_id;
3283
+ if (options?.requireViewerMatch && ownerId && ownerId !== viewerUserId) {
3199
3284
  return null;
3200
3285
  }
3201
- const realtimeToken = await ensureWorkflowRealtimeToken(db, workflowId, row.realtime_token);
3286
+ const realtimeToken = row ? await ensureWorkflowRealtimeToken(db, workflowId, row.realtime_token) : v2Run?.id ?? null;
3202
3287
  if (!realtimeToken) return null;
3203
- const v2Payload = await loadWorkflowV2PayloadSnapshot(db, workflowId);
3204
- const stepsResult = await db.execute(sql4`
3288
+ const v2Steps = v2Run ? await loadWorkflowV2Steps(db, workflowId) : [];
3289
+ let steps = v2Steps.map(mapV2WorkflowStep);
3290
+ if (steps.length === 0) {
3291
+ let stepsResult;
3292
+ try {
3293
+ stepsResult = await db.execute(sql4`
3205
3294
  SELECT
3206
3295
  step_name,
3207
3296
  status,
@@ -3213,17 +3302,19 @@ async function loadWorkflowSnapshot(db, workflowId, options) {
3213
3302
  FROM _gencow_workflow_steps
3214
3303
  WHERE workflow_id = ${workflowId}
3215
3304
  ORDER BY COALESCE(started_at, updated_at) ASC, step_name ASC
3216
- `);
3217
- const steps = rowsFromResult(stepsResult).map((stepRow) => {
3218
- const mapped = mapWorkflowStep(stepRow);
3219
- return v2Payload?.steps.has(stepRow.step_name) ? { ...mapped, output: v2Payload.steps.get(stepRow.step_name) } : mapped;
3220
- });
3305
+ `);
3306
+ } catch (error) {
3307
+ if (!v2Run || !isMissingWorkflowV1Table(error)) throw error;
3308
+ stepsResult = [];
3309
+ }
3310
+ steps = rowsFromResult(stepsResult).map(mapWorkflowStep);
3311
+ }
3221
3312
  return {
3222
- ...mapWorkflowSummary(row),
3223
- args: v2Payload ? deserializeWorkflowValue(parseJsonField(v2Payload.run.args_json)) : deserializeWorkflowValue(parseJsonField(row.args)),
3224
- result: v2Payload ? parseJsonField(v2Payload.run.result_json) : deserializeWorkflowValue(parseJsonField(row.result)),
3313
+ ...v2Run ? mapV2WorkflowSummary(v2Run) : mapWorkflowSummary(row),
3314
+ args: deserializeWorkflowValue(parseJsonField(v2Run ? v2Run.args_json : row.args)),
3315
+ result: v2Run ? parseJsonField(v2Run.result_json) : deserializeWorkflowValue(parseJsonField(row.result)),
3225
3316
  steps,
3226
- realtimeKey: getWorkflowRealtimeKey(row.id, realtimeToken)
3317
+ realtimeKey: getWorkflowRealtimeKey(workflowId, realtimeToken)
3227
3318
  };
3228
3319
  }
3229
3320
  function buildWorkflowBuiltinDefinitions() {
@@ -3251,9 +3342,11 @@ function buildWorkflowBuiltinDefinitions() {
3251
3342
  if (!normalizedEvent) {
3252
3343
  throw new GencowValidationError(`Argument "event": expected a non-empty string`);
3253
3344
  }
3345
+ const v2Run = await loadWorkflowV2Run(ctx.unsafeDb, args.id);
3254
3346
  const workflow2 = await loadWorkflowSignalTarget(ctx.unsafeDb, args.id);
3255
3347
  const viewerUserId = ctx.auth.getUserIdentity()?.id ?? null;
3256
- if (!workflow2 || workflow2.user_id && workflow2.user_id !== viewerUserId) {
3348
+ const ownerId = v2Run ? v2Run.user_id : workflow2?.user_id;
3349
+ if (!v2Run && !workflow2 || ownerId && ownerId !== viewerUserId) {
3257
3350
  return {
3258
3351
  ok: false,
3259
3352
  workflowId: args.id,
@@ -3261,29 +3354,43 @@ function buildWorkflowBuiltinDefinitions() {
3261
3354
  scheduledJobId: null
3262
3355
  };
3263
3356
  }
3264
- if (workflow2.status === "completed" || workflow2.status === "failed") {
3357
+ const workflowId = v2Run?.id ?? workflow2.id;
3358
+ const workflowName = v2Run?.workflow_name ?? workflow2.name;
3359
+ if (v2Run ? v2Run.status === "completed" || v2Run.status === "failed" || v2Run.status === "cancelled" || v2Run.completed_at !== null : workflow2.status === "completed" || workflow2.status === "failed") {
3265
3360
  return {
3266
3361
  ok: false,
3267
- workflowId: workflow2.id,
3362
+ workflowId,
3268
3363
  event: normalizedEvent,
3269
3364
  scheduledJobId: null
3270
3365
  };
3271
3366
  }
3272
3367
  const persistedPayload = serializeWorkflowValue(args.payload);
3273
3368
  assertWorkflowPayloadWithinLimit({
3274
- workflowName: workflow2.name,
3369
+ workflowName,
3275
3370
  kind: "signal",
3276
3371
  serializedValue: persistedPayload
3277
3372
  });
3278
- const eventId = crypto.randomUUID();
3279
- const recordedV2 = await tryRecordWorkflowV2Signal({
3280
- db: ctx.unsafeDb,
3281
- workflowId: workflow2.id,
3282
- event: normalizedEvent,
3283
- eventId,
3284
- payload: persistedPayload
3285
- });
3286
- if (!recordedV2) {
3373
+ const signalId = crypto.randomUUID();
3374
+ if (v2Run) {
3375
+ const recordedV2 = await tryRecordWorkflowV2Signal({
3376
+ db: ctx.unsafeDb,
3377
+ workflowId,
3378
+ event: normalizedEvent,
3379
+ signalId,
3380
+ payload: persistedPayload
3381
+ });
3382
+ if (recordedV2 !== "accepted") {
3383
+ return { ok: false, workflowId, event: normalizedEvent, scheduledJobId: null };
3384
+ }
3385
+ if (workflow2) {
3386
+ await mirrorWorkflowV2SignalToV1({
3387
+ db: ctx.unsafeDb,
3388
+ workflowId,
3389
+ event: normalizedEvent,
3390
+ signalId
3391
+ });
3392
+ }
3393
+ } else {
3287
3394
  await ctx.unsafeDb.execute(sql4`
3288
3395
  INSERT INTO _gencow_workflow_events (
3289
3396
  id,
@@ -3292,18 +3399,18 @@ function buildWorkflowBuiltinDefinitions() {
3292
3399
  payload
3293
3400
  )
3294
3401
  VALUES (
3295
- ${eventId},
3296
- ${workflow2.id},
3402
+ ${signalId},
3403
+ ${workflowId},
3297
3404
  ${normalizedEvent},
3298
3405
  ${workflowJsonb(persistedPayload)}
3299
3406
  )
3300
3407
  `);
3301
3408
  }
3302
3409
  let scheduledJobId = null;
3303
- if (workflow2.status === "pending" && workflow2.current_step?.startsWith("wait:")) {
3410
+ if (workflow2?.status === "pending" && workflow2.current_step?.startsWith("wait:")) {
3304
3411
  try {
3305
- scheduledJobId = ctx.scheduler.runAfter(0, getWorkflowResumeActionName(workflow2.name), {
3306
- workflowId: workflow2.id
3412
+ scheduledJobId = ctx.scheduler.runAfter(0, getWorkflowResumeActionName(workflowName), {
3413
+ workflowId
3307
3414
  });
3308
3415
  } catch {
3309
3416
  scheduledJobId = null;
@@ -3311,7 +3418,7 @@ function buildWorkflowBuiltinDefinitions() {
3311
3418
  }
3312
3419
  return {
3313
3420
  ok: true,
3314
- workflowId: workflow2.id,
3421
+ workflowId,
3315
3422
  event: normalizedEvent,
3316
3423
  scheduledJobId
3317
3424
  };
@@ -3328,52 +3435,64 @@ function buildWorkflowBuiltinDefinitions() {
3328
3435
  const userId = ctx.auth.requireAuth().id;
3329
3436
  const limit = normalizeListLimit(args.limit);
3330
3437
  const requestedStatus = normalizeDerivedStatus(args.status);
3331
- const status = toWorkflowStatusFilter(requestedStatus);
3332
- const result = status == null ? await ctx.unsafeDb.execute(sql4`
3333
- SELECT
3334
- id,
3335
- name,
3336
- args,
3337
- status,
3338
- current_step,
3339
- result,
3340
- error,
3341
- error_code,
3342
- retry_count,
3343
- max_retries,
3344
- max_duration_ms,
3345
- started_at,
3346
- updated_at,
3347
- completed_at,
3348
- user_id
3349
- FROM _gencow_workflows
3350
- WHERE user_id = ${userId}
3351
- ORDER BY started_at DESC
3352
- LIMIT ${limit}
3353
- `) : await ctx.unsafeDb.execute(sql4`
3354
- SELECT
3355
- id,
3356
- name,
3357
- args,
3358
- status,
3359
- current_step,
3360
- result,
3361
- error,
3362
- error_code,
3363
- retry_count,
3364
- max_retries,
3365
- max_duration_ms,
3366
- started_at,
3367
- updated_at,
3368
- completed_at,
3369
- user_id
3370
- FROM _gencow_workflows
3371
- WHERE user_id = ${userId}
3372
- AND status = ${status}
3373
- ORDER BY started_at DESC
3374
- LIMIT ${limit}
3375
- `);
3376
- return rowsFromResult(result).map(mapWorkflowSummary).filter((row) => requestedStatus == null || row.derivedStatus === requestedStatus);
3438
+ const v1Derived = sql4`CASE WHEN w.status = 'pending' AND w.current_step LIKE 'sleep#%' THEN 'sleeping'
3439
+ WHEN w.status = 'pending' AND w.current_step LIKE 'wait:%' THEN 'waiting'
3440
+ WHEN w.status = 'pending' THEN 'queued' ELSE w.status END`;
3441
+ const effectiveStatus = sql4`CASE WHEN v.id IS NOT NULL THEN
3442
+ CASE WHEN v.status = 'cancelled' THEN 'failed'
3443
+ WHEN v.status IN ('queued', 'waiting', 'sleeping') THEN 'pending'
3444
+ ELSE v.status END
3445
+ ELSE w.status END`;
3446
+ const effectiveDerived = sql4`CASE WHEN v.id IS NOT NULL THEN
3447
+ CASE WHEN v.status = 'cancelled' THEN 'failed'
3448
+ WHEN v.status IN ('queued', 'waiting', 'sleeping') THEN v.status
3449
+ ELSE v.status END
3450
+ ELSE ${v1Derived} END`;
3451
+ const statusForFilter = requestedStatus === "pending" ? effectiveStatus : effectiveDerived;
3452
+ const v1StatusForFilter = requestedStatus === "pending" ? sql4`w.status` : v1Derived;
3453
+ try {
3454
+ const result = await ctx.unsafeDb.execute(sql4`
3455
+ SELECT w.*, v.id AS v2_id, v.workflow_name AS v2_name,
3456
+ v.status AS v2_status, v.error_json AS v2_error_json,
3457
+ v.error_code AS v2_error_code, v.current_step_key AS v2_current_step,
3458
+ v.retry_count AS v2_retry_count, v.max_retries AS v2_max_retries,
3459
+ v.max_active_duration_ms AS v2_max_duration_ms,
3460
+ v.created_at AS v2_created_at, v.updated_at AS v2_updated_at,
3461
+ v.completed_at AS v2_completed_at
3462
+ FROM _gencow_workflows w
3463
+ FULL OUTER JOIN _gencow_workflow_runs_v2 v ON v.id = w.id
3464
+ WHERE (CASE WHEN v.id IS NOT NULL THEN v.user_id ELSE w.user_id END) = ${userId}
3465
+ AND (${requestedStatus ?? null}::text IS NULL OR ${statusForFilter} = ${requestedStatus ?? null})
3466
+ ORDER BY COALESCE(v.created_at, w.started_at) DESC, COALESCE(v.id, w.id) DESC
3467
+ LIMIT ${limit}
3468
+ `);
3469
+ return rowsFromResult(result).map(mapJoinedWorkflowSummary);
3470
+ } catch (error) {
3471
+ if (isMissingWorkflowV1Table(error)) {
3472
+ const v2StatusForFilter = requestedStatus === "pending" ? sql4`CASE WHEN status IN ('queued', 'waiting', 'sleeping') THEN 'pending'
3473
+ WHEN status = 'cancelled' THEN 'failed' ELSE status END` : sql4`CASE WHEN status = 'cancelled' THEN 'failed' ELSE status END`;
3474
+ const result2 = await ctx.unsafeDb.execute(sql4`
3475
+ SELECT id, workflow_name, status, args_json, result_json, error_json, error_code,
3476
+ current_step_key, retry_count, max_retries, max_active_duration_ms,
3477
+ created_at, updated_at, completed_at, user_id
3478
+ FROM _gencow_workflow_runs_v2
3479
+ WHERE user_id = ${userId}
3480
+ AND (${requestedStatus ?? null}::text IS NULL OR ${v2StatusForFilter} = ${requestedStatus ?? null})
3481
+ ORDER BY created_at DESC, id DESC
3482
+ LIMIT ${limit}
3483
+ `);
3484
+ return rowsFromResult(result2).map(mapV2WorkflowSummary);
3485
+ }
3486
+ if (!isMissingWorkflowV2PayloadSchemaError(error)) throw error;
3487
+ const result = await ctx.unsafeDb.execute(sql4`
3488
+ SELECT w.* FROM _gencow_workflows w
3489
+ WHERE w.user_id = ${userId}
3490
+ AND (${requestedStatus ?? null}::text IS NULL OR ${v1StatusForFilter} = ${requestedStatus ?? null})
3491
+ ORDER BY w.started_at DESC, w.id DESC
3492
+ LIMIT ${limit}
3493
+ `);
3494
+ return rowsFromResult(result).map(mapWorkflowSummary);
3495
+ }
3377
3496
  }
3378
3497
  };
3379
3498
  const definitions = Object.freeze({
package/core/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gencow/core",
3
- "version": "0.1.0",
3
+ "version": "0.1.45",
4
4
  "type": "module",
5
5
  "main": "index.js"
6
6
  }