deepline 0.1.83 → 0.1.85

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.
@@ -81,6 +81,10 @@ import type {
81
81
  ToolExecuteResult,
82
82
  ToolResultMetadataInput,
83
83
  } from '../../shared_libs/play-runtime/tool-result-types.js';
84
+ import type {
85
+ AuthoredStaleAfterSeconds,
86
+ PreviousCell,
87
+ } from '../../shared_libs/play-runtime/cell-staleness.js';
84
88
  import type {
85
89
  DeeplineClientOptions,
86
90
  PlayDetail,
@@ -187,6 +191,22 @@ export type {
187
191
  ToolResultListAccessor,
188
192
  ToolResultTargetAccessor as ToolExtractedValue,
189
193
  } from '../../shared_libs/play-runtime/tool-result-types.js';
194
+ export {
195
+ DEEPLINE_EXTRACTOR_TARGETS,
196
+ DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS,
197
+ JOB_CHANGE_STATUS_VALUES,
198
+ PHONE_STATUS_VALUES,
199
+ isDeeplineExtractorTarget,
200
+ } from '../../shared_libs/play-runtime/extractor-targets.js';
201
+ export type {
202
+ DeeplineEmailStatusGetterValue,
203
+ DeeplineExtractorTarget,
204
+ DeeplineGetterValue,
205
+ DeeplineGetterValueMap,
206
+ JobChangeStatus,
207
+ PhoneStatus,
208
+ } from '../../shared_libs/play-runtime/extractor-targets.js';
209
+ export type { PreviousCell } from '../../shared_libs/play-runtime/cell-staleness.js';
190
210
 
191
211
  /**
192
212
  * Keyword-style request object for `ctx.tools.execute(...)`.
@@ -206,12 +226,35 @@ export type ToolExecutionRequest = {
206
226
  staleAfterSeconds?: number;
207
227
  };
208
228
 
229
+ export type StaleAfterSeconds<Value = unknown> =
230
+ AuthoredStaleAfterSeconds<Value>;
231
+
209
232
  export type StepResolver<Row, Value> = (
210
233
  row: Row,
211
234
  ctx: DeeplinePlayRuntimeContext,
212
235
  index: number,
236
+ previousCell?: PreviousCell<Value>,
213
237
  ) => Value | Promise<Value>;
214
238
 
239
+ export type DatasetColumnRunInput<Row, Value> = {
240
+ row: Row;
241
+ ctx: DeeplinePlayRuntimeContext;
242
+ index: number;
243
+ /**
244
+ * The prior stored value for this exact row+column when the runtime has
245
+ * decided the cell is due to run again. `previousCell.value` is the same type
246
+ * this column returns; metadata such as `completedAt` and `staleAt` lives
247
+ * beside it and is not mixed into the value.
248
+ */
249
+ previousCell?: PreviousCell<Value>;
250
+ };
251
+
252
+ export type DatasetColumnDefinition<Row, Value> = {
253
+ run: (input: DatasetColumnRunInput<Row, Value>) => Value | Promise<Value>;
254
+ readonly runIf?: (row: Row, index: number) => boolean | Promise<boolean>;
255
+ readonly staleAfterSeconds?: StaleAfterSeconds<Value>;
256
+ };
257
+
215
258
  export type ConditionalStepResolver<Row, Value, Else = null> = {
216
259
  readonly kind: 'conditional';
217
260
  readonly when: (row: Row, index: number) => boolean | Promise<boolean>;
@@ -222,9 +265,9 @@ export type ConditionalStepResolver<Row, Value, Else = null> = {
222
265
  ): ConditionalStepResolver<Row, Value, ValueElse>;
223
266
  };
224
267
 
225
- export type StepOptions<Row> = {
268
+ export type StepOptions<Row, Value = unknown> = {
226
269
  readonly runIf?: (row: Row, index: number) => boolean | Promise<boolean>;
227
- readonly staleAfterSeconds?: number;
270
+ readonly staleAfterSeconds?: StaleAfterSeconds<Value>;
228
271
  };
229
272
 
230
273
  export type StepProgram<Input, Output, Return = Output> = {
@@ -242,7 +285,7 @@ export type StepProgram<Input, Output, Return = Output> = {
242
285
  step<Name extends string, Value>(
243
286
  name: Name,
244
287
  resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>,
245
- options: StepOptions<Output>,
288
+ options: StepOptions<Output, Value>,
246
289
  ): StepProgram<Input, Output & Record<Name, Value | null>, Return>;
247
290
  return<Value>(
248
291
  resolver: StepResolver<Output, Value>,
@@ -258,7 +301,7 @@ export type StepProgramResolver<Input, Return> = {
258
301
 
259
302
  export type PlayStepProgramStep = {
260
303
  readonly name: string;
261
- readonly staleAfterSeconds?: number;
304
+ readonly staleAfterSeconds?: StaleAfterSeconds;
262
305
  readonly resolver:
263
306
  | StepResolver<Record<string, unknown>, unknown>
264
307
  | ConditionalStepResolver<Record<string, unknown>, unknown>
@@ -294,12 +337,25 @@ export type DatasetBuilder<
294
337
  name: Name,
295
338
  resolver: ColumnResolver<OutputRow, Value>,
296
339
  ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;
340
+ withColumn<Name extends string, Value>(
341
+ name: Name,
342
+ definition: DatasetColumnDefinition<OutputRow, Value> & {
343
+ readonly runIf: (
344
+ row: OutputRow,
345
+ index: number,
346
+ ) => boolean | Promise<boolean>;
347
+ },
348
+ ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;
349
+ withColumn<Name extends string, Value>(
350
+ name: Name,
351
+ definition: DatasetColumnDefinition<OutputRow, Value>,
352
+ ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;
297
353
  withColumn<Name extends string, Value>(
298
354
  name: Name,
299
355
  resolver:
300
356
  | StepResolver<OutputRow, Value>
301
357
  | StepProgramResolver<OutputRow, Value>,
302
- options: StepOptions<OutputRow>,
358
+ options: StepOptions<OutputRow, Value>,
303
359
  ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;
304
360
  withColumns<Program extends StepProgram<OutputRow, object, unknown>>(
305
361
  program: Program,
@@ -46,6 +46,7 @@ import type {
46
46
  WorkReceipt,
47
47
  WorkReceiptClaim,
48
48
  } from '../../../shared_libs/play-runtime/work-receipts';
49
+ import type { CellStalenessPolicy } from '../../../shared_libs/play-runtime/cell-staleness';
49
50
 
50
51
  /**
51
52
  * Service-binding RPC stub shape — what `env.HARNESS` looks like inside
@@ -191,7 +192,7 @@ export async function harnessStartSheetDataset(input: {
191
192
  runId: string;
192
193
  inputOffset?: number;
193
194
  userEmail?: string | null;
194
- cellPolicies?: Record<string, { staleAfterSeconds?: number }>;
195
+ cellPolicies?: Record<string, CellStalenessPolicy>;
195
196
  }): Promise<{
196
197
  inserted: number;
197
198
  skipped: number;
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.83',
53
+ version: '0.1.85',
54
54
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
55
55
  supportPolicy: {
56
- latest: '0.1.83',
56
+ latest: '0.1.85',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -505,6 +505,10 @@ export interface PlayRunListItem {
505
505
  closeTime: string | null;
506
506
  /** Duration string (e.g. `'2.5s'`). */
507
507
  executionTime: string | null;
508
+ /** Total Deepline credits charged for the run, when available. */
509
+ billingTotalCredits?: number;
510
+ /** Configured per-run Deepline credit cap, when available. */
511
+ billingMaxCreditsPerRun?: number | null;
508
512
  /** Metadata attached to the workflow. */
509
513
  memo: {
510
514
  /** Organization that owns this run. */
@@ -1,17 +1,43 @@
1
+ export type StaleAfterSecondsResolver<Value = unknown> = (
2
+ value: Value,
3
+ ) => number | null;
4
+
5
+ export type AuthoredStaleAfterSeconds<Value = unknown> =
6
+ | number
7
+ | StaleAfterSecondsResolver<Value>;
8
+
9
+ export type AuthoredCellStalenessPolicy<Value = unknown> = {
10
+ staleAfterSeconds?: AuthoredStaleAfterSeconds<Value>;
11
+ };
12
+
1
13
  export type CellStalenessPolicy = {
2
14
  staleAfterSeconds?: number;
15
+ dynamicStaleAfterSeconds?: boolean;
3
16
  };
4
17
 
5
18
  export type CellStalenessMeta = {
6
19
  status?: string | null;
7
20
  completedAt?: number | null;
21
+ staleAt?: number | null;
22
+ staleAfterSeconds?: number | null;
23
+ };
24
+
25
+ export type PreviousCell<Value = unknown> = {
26
+ value: Value;
27
+ completedAt?: number;
28
+ staleAt?: number | null;
29
+ staleAfterSeconds?: number;
8
30
  };
9
31
 
10
32
  export type CellStalenessDecision =
11
33
  | { action: 'recompute'; reason: 'missing' | 'failed' | 'stale' }
12
- | { action: 'reuse'; reason: 'fresh' | 'no_policy' };
34
+ | { action: 'reuse'; reason: 'fresh' | 'no_policy' | 'no_expiry' };
13
35
 
14
36
  export type CellStalenessPolicyByField = Record<string, CellStalenessPolicy>;
37
+ export type AuthoredCellStalenessPolicyByField = Record<
38
+ string,
39
+ AuthoredCellStalenessPolicy
40
+ >;
15
41
 
16
42
  export const DEEPLINE_CELL_META_FIELD = '__deeplineCellMeta';
17
43
 
@@ -32,12 +58,83 @@ export function validateStaleAfterSeconds(
32
58
  }
33
59
 
34
60
  export function normalizeCellStalenessPolicy(
35
- policy: CellStalenessPolicy | undefined,
61
+ policy: AuthoredCellStalenessPolicy | undefined,
36
62
  ): CellStalenessPolicy {
37
- validateStaleAfterSeconds(policy?.staleAfterSeconds);
38
- return policy?.staleAfterSeconds === undefined
39
- ? {}
40
- : { staleAfterSeconds: policy.staleAfterSeconds };
63
+ const staleAfterSeconds = policy?.staleAfterSeconds;
64
+ if (staleAfterSeconds === undefined) {
65
+ return {};
66
+ }
67
+ if (typeof staleAfterSeconds === 'function') {
68
+ return { dynamicStaleAfterSeconds: true };
69
+ }
70
+ validateStaleAfterSeconds(staleAfterSeconds);
71
+ return { staleAfterSeconds };
72
+ }
73
+
74
+ export function resolveCompletedCellStalenessMeta<Value>(input: {
75
+ policy?: AuthoredCellStalenessPolicy<Value>;
76
+ value: Value;
77
+ completedAt: number;
78
+ }): Pick<CellStalenessMeta, 'staleAfterSeconds' | 'staleAt'> {
79
+ const staleAfterSeconds = input.policy?.staleAfterSeconds;
80
+ if (staleAfterSeconds === undefined) {
81
+ return {};
82
+ }
83
+
84
+ const resolved =
85
+ typeof staleAfterSeconds === 'function'
86
+ ? staleAfterSeconds(input.value)
87
+ : staleAfterSeconds;
88
+ if (resolved === null) {
89
+ return { staleAt: null };
90
+ }
91
+ if (typeof resolved !== 'number') {
92
+ throw new Error(
93
+ 'staleAfterSeconds(value) must return a positive whole number of seconds or null.',
94
+ );
95
+ }
96
+ validateStaleAfterSeconds(resolved, 'staleAfterSeconds(value)');
97
+ return {
98
+ staleAfterSeconds: resolved,
99
+ staleAt: input.completedAt + resolved * 1000,
100
+ };
101
+ }
102
+
103
+ export function previousCellFromValue<Value>(input: {
104
+ hasValue: boolean;
105
+ value: Value;
106
+ meta?: CellStalenessMeta | null;
107
+ }): PreviousCell<Value> | undefined {
108
+ if (!input.hasValue) {
109
+ return undefined;
110
+ }
111
+
112
+ const previous: PreviousCell<Value> = {
113
+ value: input.value,
114
+ };
115
+ if (
116
+ typeof input.meta?.completedAt === 'number' &&
117
+ Number.isFinite(input.meta.completedAt)
118
+ ) {
119
+ previous.completedAt = input.meta.completedAt;
120
+ }
121
+ if (
122
+ input.meta &&
123
+ Object.prototype.hasOwnProperty.call(input.meta, 'staleAt')
124
+ ) {
125
+ previous.staleAt =
126
+ typeof input.meta.staleAt === 'number' &&
127
+ Number.isFinite(input.meta.staleAt)
128
+ ? input.meta.staleAt
129
+ : null;
130
+ }
131
+ if (
132
+ typeof input.meta?.staleAfterSeconds === 'number' &&
133
+ Number.isFinite(input.meta.staleAfterSeconds)
134
+ ) {
135
+ previous.staleAfterSeconds = input.meta.staleAfterSeconds;
136
+ }
137
+ return previous;
41
138
  }
42
139
 
43
140
  export function shouldRecomputeCell(input: {
@@ -55,6 +152,23 @@ export function shouldRecomputeCell(input: {
55
152
  return { action: 'recompute', reason: 'failed' };
56
153
  }
57
154
 
155
+ const staleAt =
156
+ input.meta && Object.prototype.hasOwnProperty.call(input.meta, 'staleAt')
157
+ ? input.meta.staleAt
158
+ : undefined;
159
+ if (staleAt === null) {
160
+ return { action: 'reuse', reason: 'no_expiry' };
161
+ }
162
+ if (typeof staleAt === 'number' && Number.isFinite(staleAt)) {
163
+ return (input.nowMs ?? Date.now()) > staleAt
164
+ ? { action: 'recompute', reason: 'stale' }
165
+ : { action: 'reuse', reason: 'fresh' };
166
+ }
167
+
168
+ if (input.policy?.dynamicStaleAfterSeconds) {
169
+ return { action: 'recompute', reason: 'stale' };
170
+ }
171
+
58
172
  const staleAfterSeconds = input.policy?.staleAfterSeconds;
59
173
  validateStaleAfterSeconds(staleAfterSeconds);
60
174
  if (staleAfterSeconds === undefined) {
@@ -76,6 +190,37 @@ export function shouldRecomputeCell(input: {
76
190
  : { action: 'reuse', reason: 'fresh' };
77
191
  }
78
192
 
193
+ export function resolveReusableCellMetaForCurrentPolicy<Value>(input: {
194
+ hasValue: boolean;
195
+ value: Value;
196
+ meta?: CellStalenessMeta | null;
197
+ policy?: AuthoredCellStalenessPolicy<Value>;
198
+ }): Pick<CellStalenessMeta, 'staleAfterSeconds' | 'staleAt'> {
199
+ if (
200
+ !input.hasValue ||
201
+ typeof input.policy?.staleAfterSeconds !== 'function'
202
+ ) {
203
+ return {};
204
+ }
205
+ const status = String(input.meta?.status ?? '').trim();
206
+ if (status === 'failed') {
207
+ return {};
208
+ }
209
+ const completedAt =
210
+ typeof input.meta?.completedAt === 'number' &&
211
+ Number.isFinite(input.meta.completedAt)
212
+ ? input.meta.completedAt
213
+ : null;
214
+ if (completedAt === null) {
215
+ return {};
216
+ }
217
+ return resolveCompletedCellStalenessMeta({
218
+ policy: input.policy,
219
+ value: input.value,
220
+ completedAt,
221
+ });
222
+ }
223
+
79
224
  export function cellPolicyFields(
80
225
  policies: CellStalenessPolicyByField | undefined,
81
226
  ): string[] {
@@ -83,6 +228,10 @@ export function cellPolicyFields(
83
228
  return [];
84
229
  }
85
230
  return Object.entries(policies)
86
- .filter(([, policy]) => policy.staleAfterSeconds !== undefined)
231
+ .filter(
232
+ ([, policy]) =>
233
+ policy.staleAfterSeconds !== undefined ||
234
+ policy.dynamicStaleAfterSeconds === true,
235
+ )
87
236
  .map(([field]) => field);
88
237
  }
@@ -1,5 +1,5 @@
1
1
  import {
2
- getCompiledPipelineSubsteps,
2
+ flattenStaticPipeline,
3
3
  type PlayStaticPipeline,
4
4
  type PlayStaticSubstep,
5
5
  } from '../plays/static-pipeline';
@@ -189,7 +189,7 @@ function extractPlanMaps(
189
189
  pipeline: PlayStaticPipeline | null,
190
190
  ): ExecutionPlanMap[] {
191
191
  if (!pipeline) return [];
192
- const substeps = getCompiledPipelineSubsteps(pipeline);
192
+ const substeps = flattenStaticPipeline(pipeline);
193
193
  const fallbackWaterfalls = substeps.filter(
194
194
  (substep): substep is Extract<PlayStaticSubstep, { type: 'waterfall' }> =>
195
195
  substep.type === 'waterfall',
@@ -252,7 +252,7 @@ function extractToolDeclarations(
252
252
  if (!pipeline) return [];
253
253
  const seen = new Set<string>();
254
254
  const declarations: ExecutionPlan['toolDeclarations'] = [];
255
- for (const substep of getCompiledPipelineSubsteps(pipeline)) {
255
+ for (const substep of flattenStaticPipeline(pipeline)) {
256
256
  if (substep.type === 'tool') {
257
257
  const key = `${substep.toolId}:${substep.field}`;
258
258
  if (!seen.has(key)) {
@@ -0,0 +1,106 @@
1
+ import type { EmailStatus, EmailStatusValue } from './email-status';
2
+
3
+ export const JOB_CHANGE_STATUS_VALUES = [
4
+ 'moved',
5
+ 'no_change',
6
+ 'left_company',
7
+ 'unknown',
8
+ 'profile_unavailable',
9
+ 'no_new_company',
10
+ ] as const;
11
+
12
+ export type JobChangeStatus = (typeof JOB_CHANGE_STATUS_VALUES)[number];
13
+
14
+ export type JobChangeGetterValue = {
15
+ status: JobChangeStatus;
16
+ date: string | null;
17
+ new_company: string | null;
18
+ new_title: string | null;
19
+ };
20
+
21
+ export const PHONE_STATUS_VALUES = ['valid', 'invalid', 'unknown'] as const;
22
+
23
+ export type PhoneStatus = (typeof PHONE_STATUS_VALUES)[number];
24
+
25
+ export const DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS = {
26
+ id: { identity: true, valueKind: 'string' },
27
+ name: { identity: true, valueKind: 'string' },
28
+ email: { identity: true, valueKind: 'string' },
29
+ personal_email: { identity: true, valueKind: 'string' },
30
+ phone: { identity: true, valueKind: 'string' },
31
+ linkedin: { identity: true, valueKind: 'string' },
32
+ linkedin_url: { identity: true, valueKind: 'string' },
33
+ domain: { identity: true, valueKind: 'string' },
34
+ website: { identity: true, valueKind: 'string' },
35
+ first_name: { identity: true, valueKind: 'string' },
36
+ last_name: { identity: true, valueKind: 'string' },
37
+ full_name: { identity: true, valueKind: 'string' },
38
+ company: { identity: true, valueKind: 'string' },
39
+ company_name: { identity: true, valueKind: 'string' },
40
+ organization_name: { identity: true, valueKind: 'string' },
41
+ company_domain: { identity: true, valueKind: 'string' },
42
+ company_website: { identity: true, valueKind: 'string' },
43
+ company_linkedin_url: { identity: true, valueKind: 'string' },
44
+ title: { identity: false, valueKind: 'string' },
45
+ industry: { identity: false, valueKind: 'string' },
46
+ status: { identity: false, valueKind: 'string' },
47
+ job_change: { identity: false, valueKind: 'job_change' },
48
+ job_change_status: {
49
+ identity: false,
50
+ valueKind: 'job_change_status',
51
+ enum: JOB_CHANGE_STATUS_VALUES,
52
+ },
53
+ email_status: { identity: false, valueKind: 'email_status' },
54
+ phone_status: {
55
+ identity: false,
56
+ valueKind: 'phone_status',
57
+ enum: PHONE_STATUS_VALUES,
58
+ },
59
+ } as const;
60
+
61
+ export type DeeplineExtractorTarget =
62
+ keyof typeof DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS;
63
+
64
+ export const DEEPLINE_EXTRACTOR_TARGETS = Object.keys(
65
+ DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS,
66
+ ) as DeeplineExtractorTarget[];
67
+
68
+ export type DeeplineEmailStatusGetterValue = EmailStatus | EmailStatusValue;
69
+
70
+ export type DeeplineGetterValueMap = {
71
+ id: string;
72
+ name: string;
73
+ email: string;
74
+ personal_email: string;
75
+ phone: string;
76
+ linkedin: string;
77
+ linkedin_url: string;
78
+ domain: string;
79
+ website: string;
80
+ first_name: string;
81
+ last_name: string;
82
+ full_name: string;
83
+ company: string;
84
+ company_name: string;
85
+ organization_name: string;
86
+ company_domain: string;
87
+ company_website: string;
88
+ company_linkedin_url: string;
89
+ title: string;
90
+ industry: string;
91
+ status: string;
92
+ job_change: JobChangeGetterValue;
93
+ job_change_status: JobChangeStatus;
94
+ email_status: DeeplineEmailStatusGetterValue;
95
+ phone_status: PhoneStatus;
96
+ };
97
+
98
+ export type DeeplineGetterValue<
99
+ TTarget extends DeeplineExtractorTarget = DeeplineExtractorTarget,
100
+ > = DeeplineGetterValueMap[TTarget];
101
+
102
+ export function isDeeplineExtractorTarget(
103
+ value: string,
104
+ ): value is DeeplineExtractorTarget {
105
+ return value in DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS;
106
+ }
@@ -12,6 +12,9 @@ import {
12
12
 
13
13
  export const PLAY_RUNTIME_PROVIDER_IDS = {
14
14
  workersEdge: 'workers_edge',
15
+ postgresFast: 'postgres_fast',
16
+ postgresFastSandbox: 'postgres_fast_sandbox',
17
+ postgresFastWorkers: 'postgres_fast_workers',
15
18
  local: 'local',
16
19
  } as const;
17
20
 
@@ -39,6 +42,31 @@ export const PLAY_RUNTIME_PROVIDERS: Record<
39
42
  artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
40
43
  label: 'Cloudflare Dynamic Workflows + Dynamic Workers + DO dedup',
41
44
  },
45
+ postgres_fast: {
46
+ id: PLAY_RUNTIME_PROVIDER_IDS.postgresFast,
47
+ scheduler: PLAY_SCHEDULER_BACKENDS.postgres,
48
+ runner: PLAY_RUNTIME_BACKENDS.daytona,
49
+ dedup: PLAY_DEDUP_BACKENDS.durableObject,
50
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
51
+ label: 'Experimental Postgres Scheduler + warm sandbox runner + DO dedup',
52
+ },
53
+ postgres_fast_sandbox: {
54
+ id: PLAY_RUNTIME_PROVIDER_IDS.postgresFastSandbox,
55
+ scheduler: PLAY_SCHEDULER_BACKENDS.postgres,
56
+ runner: PLAY_RUNTIME_BACKENDS.daytona,
57
+ dedup: PLAY_DEDUP_BACKENDS.durableObject,
58
+ artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
59
+ label: 'Experimental Postgres Scheduler + warm sandbox runner + DO dedup',
60
+ },
61
+ postgres_fast_workers: {
62
+ id: PLAY_RUNTIME_PROVIDER_IDS.postgresFastWorkers,
63
+ scheduler: PLAY_SCHEDULER_BACKENDS.postgres,
64
+ runner: PLAY_RUNTIME_BACKENDS.cloudflareWorkers,
65
+ dedup: PLAY_DEDUP_BACKENDS.durableObject,
66
+ artifactKind: PLAY_ARTIFACT_KINDS.esmWorkers,
67
+ label:
68
+ 'Experimental Postgres Scheduler + Queue/DO-woken Workers + DO dedup',
69
+ },
42
70
  local: {
43
71
  id: PLAY_RUNTIME_PROVIDER_IDS.local,
44
72
  scheduler: PLAY_SCHEDULER_BACKENDS.temporal,
@@ -22,6 +22,7 @@ import type { PreloadedRuntimeDbSession } from './db-session';
22
22
  export const PLAY_SCHEDULER_BACKENDS = {
23
23
  temporal: 'temporal',
24
24
  cfWorkflows: 'cf-workflows',
25
+ postgres: 'postgres',
25
26
  inProcess: 'in-process',
26
27
  } as const;
27
28
 
@@ -65,6 +66,7 @@ export type PlaySchedulerSubmitInput = {
65
66
  runId: string;
66
67
  playId: string;
67
68
  playName: string;
69
+ workflowFamilyKey?: string | null;
68
70
  artifactStorageKey: string;
69
71
  /**
70
72
  * Optional inline artifact for schedulers that run the legacy Temporal
@@ -119,6 +121,8 @@ export type PlaySchedulerSubmitInput = {
119
121
  orgId: string;
120
122
  userEmail: string;
121
123
  userId?: string | null;
124
+ source?: 'published' | 'ad_hoc' | 'draft';
125
+ executionProfile?: string | null;
122
126
  /** runner backend to use for executing attempts */
123
127
  runtimeBackend: string;
124
128
  /** dedup backend for cross-attempt cross-process idempotency */
@@ -200,6 +204,13 @@ export function normalizePlaySchedulerBackend(
200
204
  if (normalized === 'cf-workflows' || normalized === 'cf_workflows') {
201
205
  return PLAY_SCHEDULER_BACKENDS.cfWorkflows;
202
206
  }
207
+ if (
208
+ normalized === 'postgres' ||
209
+ normalized === 'postgres-scheduler' ||
210
+ normalized === 'postgres_scheduler'
211
+ ) {
212
+ return PLAY_SCHEDULER_BACKENDS.postgres;
213
+ }
203
214
  if (normalized === 'in-process' || normalized === 'in_process') {
204
215
  return PLAY_SCHEDULER_BACKENDS.inProcess;
205
216
  }