deepline 0.3.45 → 0.3.47

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 (88) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +2 -3
  2. package/dist/bundling-sources/sdk/src/errors.ts +2 -2
  3. package/dist/bundling-sources/sdk/src/http.ts +1 -1
  4. package/dist/bundling-sources/sdk/src/play.ts +7 -10
  5. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +3 -3
  6. package/dist/bundling-sources/sdk/src/release.ts +3 -3
  7. package/dist/bundling-sources/sdk/src/version.ts +2 -2
  8. package/dist/bundling-sources/shared_libs/play-data-plane/contracts.ts +25 -0
  9. package/dist/bundling-sources/shared_libs/play-data-plane/index.ts +1 -0
  10. package/dist/bundling-sources/shared_libs/play-data-plane/sheet-contract.ts +1 -1
  11. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +19 -19
  12. package/dist/bundling-sources/shared_libs/play-runtime/backend.ts +5 -6
  13. package/dist/bundling-sources/shared_libs/play-runtime/cell-staleness.ts +1 -66
  14. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +11 -11
  15. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +3 -3
  16. package/dist/bundling-sources/shared_libs/play-runtime/dataset-id.ts +4 -3
  17. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +3 -4
  18. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +2 -2
  19. package/dist/bundling-sources/shared_libs/play-runtime/email-status.ts +1 -275
  20. package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +1 -1
  21. package/dist/bundling-sources/shared_libs/play-runtime/extractor-targets.ts +1 -106
  22. package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +1 -2
  23. package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +13 -4
  24. package/dist/bundling-sources/shared_libs/play-runtime/live-events.ts +1 -5
  25. package/dist/bundling-sources/shared_libs/play-runtime/map-row-identity.ts +2 -3
  26. package/dist/bundling-sources/shared_libs/play-runtime/map-row-outcome.ts +2 -1
  27. package/dist/bundling-sources/shared_libs/play-runtime/modal-runtime-config.ts +1 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +1 -1
  29. package/dist/bundling-sources/shared_libs/play-runtime/projection.ts +5 -5
  30. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -3
  31. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +1 -1
  32. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
  33. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +4 -4
  34. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-modal-fallback.ts +3 -3
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +8 -8
  36. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +1 -1
  37. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +4 -4
  38. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +7 -7
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +7 -7
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +13 -2
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +2 -2
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runner-events.ts +1 -1
  43. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -1
  44. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +2 -2
  45. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +1 -1
  46. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +6 -3
  47. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +1 -1
  48. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constraints.ts +1 -2
  49. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +55 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runtime-limits.ts +1 -109
  51. package/dist/bundling-sources/shared_libs/play-runtime/step-program-dataset-builder.ts +1 -1
  52. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +1 -1
  53. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +1 -1
  54. package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +6 -5
  55. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
  56. package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +1 -206
  57. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +10 -7
  58. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +1 -1
  59. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +8 -3
  60. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +4 -4
  61. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +3 -6
  62. package/dist/bundling-sources/shared_libs/plays/cell-staleness.ts +66 -0
  63. package/dist/bundling-sources/shared_libs/plays/compiler-manifest.ts +1 -1
  64. package/dist/bundling-sources/shared_libs/plays/contracts.ts +2 -2
  65. package/dist/bundling-sources/shared_libs/plays/core.ts +466 -0
  66. package/dist/bundling-sources/shared_libs/plays/email-status.ts +287 -0
  67. package/dist/bundling-sources/shared_libs/plays/extractor-targets.ts +106 -0
  68. package/dist/bundling-sources/shared_libs/plays/row-identity.ts +1 -3
  69. package/dist/bundling-sources/shared_libs/plays/runtime-constraints.ts +2 -0
  70. package/dist/bundling-sources/shared_libs/plays/runtime-validation.ts +4 -2
  71. package/dist/bundling-sources/shared_libs/plays/sandbox-runtime-limits.ts +109 -0
  72. package/dist/bundling-sources/shared_libs/plays/tool-execution-error.ts +624 -0
  73. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +206 -0
  74. package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +1 -1
  75. package/dist/cli/index.js +181 -169
  76. package/dist/cli/index.mjs +189 -170
  77. package/dist/{compiler-manifest-DwYe2C2S.d.mts → compiler-manifest-BuoqasqI.d.mts} +585 -540
  78. package/dist/{compiler-manifest-DwYe2C2S.d.ts → compiler-manifest-BuoqasqI.d.ts} +585 -540
  79. package/dist/index.d.mts +9 -2
  80. package/dist/index.d.ts +9 -2
  81. package/dist/index.js +47 -40
  82. package/dist/index.mjs +47 -40
  83. package/dist/install-integrity.json +13 -3
  84. package/dist/plays/bundle-play-file.d.mts +4 -61
  85. package/dist/plays/bundle-play-file.d.ts +4 -61
  86. package/dist/plays/bundle-play-file.mjs +157 -155
  87. package/package.json +9 -6
  88. /package/dist/bundling-sources/shared_libs/{play-runtime → plays}/tool-response-contract.ts +0 -0
@@ -0,0 +1,287 @@
1
+ export type EmailStatusVerdict =
2
+ | 'send'
3
+ | 'send_with_caution'
4
+ | 'verify_next'
5
+ | 'hold'
6
+ | 'drop';
7
+
8
+ export type EmailStatusValue =
9
+ | 'valid'
10
+ | 'invalid'
11
+ | 'catch_all'
12
+ | 'valid_catch_all'
13
+ | 'unknown'
14
+ | 'do_not_mail'
15
+ | 'spamtrap'
16
+ | 'abuse'
17
+ | 'disposable';
18
+
19
+ export type EmailDeliverability = 'high' | 'medium' | 'low' | 'unknown';
20
+
21
+ export type EmailMxClass =
22
+ | 'consumer_mailbox'
23
+ | 'workspace_mailbox'
24
+ | 'security_gateway'
25
+ | 'on_prem'
26
+ | 'unknown';
27
+
28
+ export type EmailStatus = {
29
+ verdict: EmailStatusVerdict;
30
+ status: EmailStatusValue;
31
+ verified: boolean;
32
+ confidence: number | null;
33
+ reasons: string[];
34
+ signals: {
35
+ catch_all: boolean | null;
36
+ deliverability: EmailDeliverability;
37
+ mx_class: EmailMxClass;
38
+ mx_provider: string | null;
39
+ mx_record: string | null;
40
+ fraud_score: number | null;
41
+ disposable: boolean | null;
42
+ role_based: boolean | null;
43
+ free_email: boolean | null;
44
+ abuse: boolean | null;
45
+ spamtrap: boolean | null;
46
+ suspect: boolean | null;
47
+ valid: boolean | null;
48
+ };
49
+ provider: {
50
+ name: string;
51
+ raw_status: string | boolean | number | null;
52
+ raw_score: number | null;
53
+ };
54
+ };
55
+
56
+ export type EmailStatusMapEntry = {
57
+ status: EmailStatusValue;
58
+ verdict?: EmailStatusVerdict;
59
+ verified?: boolean;
60
+ reason?: string;
61
+ };
62
+
63
+ export type EmailStatusRule = EmailStatusMapEntry & {
64
+ when: Record<string, string | number | boolean | null>;
65
+ };
66
+
67
+ export type EmailStatusExtractorConfig = {
68
+ provider: string;
69
+ rawStatus?: string[];
70
+ rawScore?: string[];
71
+ valid?: string[];
72
+ deliverability?: string[];
73
+ catchAll?: string[];
74
+ mxProvider?: string[];
75
+ mxRecord?: string[];
76
+ fraudScore?: string[];
77
+ disposable?: string[];
78
+ roleBased?: string[];
79
+ freeEmail?: string[];
80
+ abuse?: string[];
81
+ spamtrap?: string[];
82
+ suspect?: string[];
83
+ statusMap?: Record<string, EmailStatusMapEntry>;
84
+ rules?: EmailStatusRule[];
85
+ };
86
+
87
+ export type EmailStatusBuildInput = {
88
+ config: EmailStatusExtractorConfig;
89
+ values: Record<string, unknown>;
90
+ };
91
+
92
+ // There is intentionally NO shared default status map. Each provider must
93
+ // declare its own `statusMap` / `rules` for the raw status strings it emits
94
+ // (see the provider registries under src/lib/integrations/**). A coarse global
95
+ // map silently coerced provider-specific strings ("deliverable", "accept_all",
96
+ // etc.) into canonical verdicts and let validators depend on guesses instead of
97
+ // documented behavior. With it gone, an unmapped raw status falls through to
98
+ // the typed signal inference below (and ultimately to `unknown`/`hold`) rather
99
+ // than being normalized by a global lookup — loud and provider-declared over
100
+ // silent and implicit.
101
+
102
+ function normalizeKey(value: unknown): string | null {
103
+ if (value == null) return null;
104
+ if (typeof value === 'boolean') return String(value);
105
+ const normalized = String(value).trim().toLowerCase().replace(/\s+/g, '_');
106
+ return normalized || null;
107
+ }
108
+
109
+ function boolish(value: unknown): boolean | null {
110
+ if (typeof value === 'boolean') return value;
111
+ if (typeof value === 'number')
112
+ return value === 1 ? true : value === 0 ? false : null;
113
+ if (typeof value !== 'string') return null;
114
+ const normalized = value.trim().toLowerCase();
115
+ if (['true', 'yes', 'y', '1'].includes(normalized)) return true;
116
+ if (['false', 'no', 'n', '0'].includes(normalized)) return false;
117
+ return null;
118
+ }
119
+
120
+ function numberish(value: unknown): number | null {
121
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
122
+ if (typeof value !== 'string' || value.trim() === '') return null;
123
+ const parsed = Number(value);
124
+ return Number.isFinite(parsed) ? parsed : null;
125
+ }
126
+
127
+ function stringish(value: unknown): string | null {
128
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
129
+ }
130
+
131
+ function deliverability(value: unknown): EmailDeliverability {
132
+ const normalized = normalizeKey(value);
133
+ return normalized === 'high' ||
134
+ normalized === 'medium' ||
135
+ normalized === 'low'
136
+ ? normalized
137
+ : 'unknown';
138
+ }
139
+
140
+ function mxClass(mxProvider: unknown, mxRecord: unknown): EmailMxClass {
141
+ const haystack =
142
+ `${stringish(mxProvider) ?? ''} ${stringish(mxRecord) ?? ''}`.toLowerCase();
143
+ if (!haystack.trim()) return 'unknown';
144
+ if (
145
+ /proofpoint|pphosted|mimecast|barracuda|ess\.barracudanetworks|ironport|cisco|iphmx|messagelabs|symantec/.test(
146
+ haystack,
147
+ )
148
+ ) {
149
+ return 'security_gateway';
150
+ }
151
+ if (/aspmx\.l\.google|google|g-suite|google workspace/.test(haystack)) {
152
+ return 'workspace_mailbox';
153
+ }
154
+ if (
155
+ /protection\.outlook|office365|microsoft|outlook|exchange online/.test(
156
+ haystack,
157
+ )
158
+ ) {
159
+ return 'workspace_mailbox';
160
+ }
161
+ if (/gmail|yahoo|icloud|aol|hotmail/.test(haystack))
162
+ return 'consumer_mailbox';
163
+ if (/postfix|exim|sendmail|zimbra|plesk|cpanel|mail\./.test(haystack))
164
+ return 'on_prem';
165
+ return 'unknown';
166
+ }
167
+
168
+ function entryForStatus(
169
+ key: string | null,
170
+ map: Record<string, EmailStatusMapEntry> | undefined,
171
+ ): EmailStatusMapEntry | null {
172
+ if (!key) return null;
173
+ return map?.[key] ?? null;
174
+ }
175
+
176
+ function read(values: Record<string, unknown>, name: string): unknown {
177
+ return values[name];
178
+ }
179
+
180
+ function matchesRule(
181
+ rule: EmailStatusRule,
182
+ values: Record<string, unknown>,
183
+ ): boolean {
184
+ return Object.entries(rule.when).every(([key, expected]) => {
185
+ const actual = read(values, key);
186
+ if (key.endsWith('Lt')) {
187
+ const source = numberish(read(values, key.slice(0, -2)));
188
+ return (
189
+ typeof expected === 'number' && source != null && source < expected
190
+ );
191
+ }
192
+ if (typeof expected === 'boolean') return boolish(actual) === expected;
193
+ if (typeof expected === 'number') return numberish(actual) === expected;
194
+ return normalizeKey(actual) === normalizeKey(expected);
195
+ });
196
+ }
197
+
198
+ export function buildEmailStatus({
199
+ config,
200
+ values,
201
+ }: EmailStatusBuildInput): EmailStatus {
202
+ const rawStatus = read(values, 'rawStatus');
203
+ const rawScore = numberish(read(values, 'rawScore'));
204
+ const valid = boolish(read(values, 'valid'));
205
+ const catchAll = boolish(read(values, 'catchAll'));
206
+ const disposable = boolish(read(values, 'disposable'));
207
+ const abuse = boolish(read(values, 'abuse'));
208
+ const spamtrap = boolish(read(values, 'spamtrap'));
209
+ const suspect = boolish(read(values, 'suspect'));
210
+ const rawKey = normalizeKey(rawStatus);
211
+ const mapped =
212
+ config.rules?.find((rule) => matchesRule(rule, values)) ??
213
+ entryForStatus(rawKey, config.statusMap) ??
214
+ entryForStatus(valid == null ? null : String(valid), config.statusMap);
215
+
216
+ const status =
217
+ mapped?.status ??
218
+ (disposable
219
+ ? 'disposable'
220
+ : abuse
221
+ ? 'abuse'
222
+ : spamtrap
223
+ ? 'spamtrap'
224
+ : catchAll
225
+ ? 'catch_all'
226
+ : valid === true
227
+ ? 'valid'
228
+ : valid === false
229
+ ? 'invalid'
230
+ : 'unknown');
231
+ const defaultVerdict: EmailStatusVerdict =
232
+ status === 'valid'
233
+ ? 'send'
234
+ : status === 'valid_catch_all'
235
+ ? 'send_with_caution'
236
+ : status === 'catch_all'
237
+ ? 'verify_next'
238
+ : status === 'unknown'
239
+ ? 'hold'
240
+ : 'drop';
241
+ const verdict = mapped?.verdict ?? defaultVerdict;
242
+ const verified =
243
+ mapped?.verified ??
244
+ (status === 'valid' || status === 'valid_catch_all' || verdict === 'send');
245
+ const reasons = [
246
+ mapped?.reason,
247
+ catchAll ? 'catch_all_domain' : null,
248
+ mxClass(read(values, 'mxProvider'), read(values, 'mxRecord')) ===
249
+ 'security_gateway'
250
+ ? 'security_gateway_mx'
251
+ : null,
252
+ suspect ? 'provider_marked_suspect' : null,
253
+ ].filter((reason): reason is string => typeof reason === 'string');
254
+
255
+ return {
256
+ verdict,
257
+ status,
258
+ verified,
259
+ confidence: rawScore,
260
+ reasons,
261
+ signals: {
262
+ catch_all: catchAll,
263
+ deliverability: deliverability(read(values, 'deliverability')),
264
+ mx_class: mxClass(read(values, 'mxProvider'), read(values, 'mxRecord')),
265
+ mx_provider: stringish(read(values, 'mxProvider')),
266
+ mx_record: stringish(read(values, 'mxRecord')),
267
+ fraud_score: numberish(read(values, 'fraudScore')),
268
+ disposable,
269
+ role_based: boolish(read(values, 'roleBased')),
270
+ free_email: boolish(read(values, 'freeEmail')),
271
+ abuse,
272
+ spamtrap,
273
+ suspect,
274
+ valid,
275
+ },
276
+ provider: {
277
+ name: config.provider,
278
+ raw_status:
279
+ typeof rawStatus === 'string' ||
280
+ typeof rawStatus === 'boolean' ||
281
+ typeof rawStatus === 'number'
282
+ ? rawStatus
283
+ : null,
284
+ raw_score: rawScore,
285
+ },
286
+ };
287
+ }
@@ -0,0 +1,106 @@
1
+ import type { EmailStatus } 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;
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: EmailStatus;
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
+ }
@@ -157,9 +157,7 @@ function validateIdentifierPart(
157
157
 
158
158
  export function normalizePlayName(value: string): string {
159
159
  if (value.includes('/')) {
160
- throw new Error(
161
- 'Play name cannot contain "/". Slash is reserved.',
162
- );
160
+ throw new Error('Play name cannot contain "/". Slash is reserved.');
163
161
  }
164
162
  return validateIdentifierPart(value, 'Play name', PLAY_NAME_MAX_LENGTH);
165
163
  }
@@ -0,0 +1,2 @@
1
+ export const DISALLOWED_RUN_JAVASCRIPT_TOOL_MESSAGE =
2
+ 'No run_javascript tool in plays. Just write regular code.';
@@ -7,7 +7,7 @@ import {
7
7
  MAP_KEY_NAMESPACE_MAX_LENGTH,
8
8
  normalizeTableNamespace,
9
9
  } from './row-identity';
10
- import { DISALLOWED_RUN_JAVASCRIPT_TOOL_MESSAGE } from '../play-runtime/runtime-constraints';
10
+ import { DISALLOWED_RUN_JAVASCRIPT_TOOL_MESSAGE } from './runtime-constraints';
11
11
 
12
12
  export async function validatePlay(
13
13
  code: string | null | undefined,
@@ -311,7 +311,9 @@ function isCtxDatasetCall(node: acorn.Node): node is acorn.CallExpression {
311
311
  return callee.object.name === 'ctx' || callee.object.name.endsWith('Ctx');
312
312
  }
313
313
 
314
- function isDeprecatedCtxMapCall(node: acorn.Node): node is acorn.CallExpression {
314
+ function isDeprecatedCtxMapCall(
315
+ node: acorn.Node,
316
+ ): node is acorn.CallExpression {
315
317
  if (node.type !== 'CallExpression') return false;
316
318
  const callee = (node as acorn.CallExpression).callee;
317
319
  return (
@@ -0,0 +1,109 @@
1
+ export type PlaySandboxRuntimeLimits = {
2
+ timeoutSeconds: number;
3
+ memoryGiB: number;
4
+ cpu: number;
5
+ diskGiB: number;
6
+ };
7
+
8
+ export type PlaySandboxSize = 'standard';
9
+
10
+ export const PLAY_SANDBOX_SIZE_LIMITS = {
11
+ standard: {
12
+ memoryGiB: 1,
13
+ cpu: 1,
14
+ diskGiB: 3,
15
+ },
16
+ } as const satisfies Record<
17
+ PlaySandboxSize,
18
+ Omit<PlaySandboxRuntimeLimits, 'timeoutSeconds'>
19
+ >;
20
+
21
+ export const STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
22
+ timeoutSeconds: 30 * 60,
23
+ ...PLAY_SANDBOX_SIZE_LIMITS.standard,
24
+ };
25
+
26
+ // Product-wide ceilings. Organisation entitlements may lower these, but a
27
+ // customer-authored Play can never ask Daytona for an unbounded resource.
28
+ export const MAX_PLAY_SANDBOX_RUNTIME_LIMITS: PlaySandboxRuntimeLimits = {
29
+ timeoutSeconds: 4 * 60 * 60,
30
+ memoryGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.memoryGiB,
31
+ cpu: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.cpu,
32
+ diskGiB: STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS.diskGiB,
33
+ };
34
+
35
+ export type PlaySandboxRuntimeDeclaration = {
36
+ timeout?: string;
37
+ size?: PlaySandboxSize;
38
+ };
39
+
40
+ function parseTimeout(value: string): number | null {
41
+ const match = /^(\d+)\s*([mh])$/i.exec(value.trim());
42
+ if (!match) return null;
43
+ const amount = Number(match[1]);
44
+ if (!Number.isSafeInteger(amount) || amount <= 0) return null;
45
+ return amount * (match[2].toLowerCase() === 'h' ? 3600 : 60);
46
+ }
47
+
48
+ export function validatePlaySandboxRuntimeLimits(
49
+ value: PlaySandboxRuntimeLimits,
50
+ ): PlaySandboxRuntimeLimits {
51
+ const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
52
+ for (const key of ['memoryGiB', 'cpu', 'diskGiB'] as const) {
53
+ if (value[key] !== base[key]) {
54
+ throw new Error(
55
+ `Unsupported runtime sandbox resources: ${key}=${value[key]}. ` +
56
+ `Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard" ` +
57
+ `(${base.cpu} CPU, ${base.memoryGiB}GiB memory, ${base.diskGiB}GiB disk).`,
58
+ );
59
+ }
60
+ }
61
+ if (
62
+ !Number.isSafeInteger(value.timeoutSeconds) ||
63
+ value.timeoutSeconds <= 0
64
+ ) {
65
+ throw new Error(
66
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".',
67
+ );
68
+ }
69
+ if (value.timeoutSeconds > MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds) {
70
+ throw new Error(
71
+ `Requested runtime timeoutSeconds=${value.timeoutSeconds} exceeds this organisation's maximum ${MAX_PLAY_SANDBOX_RUNTIME_LIMITS.timeoutSeconds}.`,
72
+ );
73
+ }
74
+ return { ...value };
75
+ }
76
+
77
+ export function resolvePlaySandboxRuntimeLimits(
78
+ declaration: PlaySandboxRuntimeDeclaration | null | undefined,
79
+ ): PlaySandboxRuntimeLimits {
80
+ const base = STANDARD_PLAY_SANDBOX_RUNTIME_LIMITS;
81
+ if (!declaration) return { ...base };
82
+ const unsupportedProperties = Object.keys(declaration).filter(
83
+ (key) => key !== 'timeout' && key !== 'size',
84
+ );
85
+ if (unsupportedProperties.length > 0) {
86
+ throw new Error(
87
+ `Unsupported runtime sandbox option${unsupportedProperties.length === 1 ? '' : 's'}: ${unsupportedProperties.join(', ')}. ` +
88
+ 'Use runtime.size with a Deepline prebuilt sandbox. Supported sizes: "standard".',
89
+ );
90
+ }
91
+ if (declaration.size !== undefined && declaration.size !== 'standard') {
92
+ throw new Error(
93
+ `Unsupported runtime sandbox size "${String(declaration.size)}". Supported sizes: "standard".`,
94
+ );
95
+ }
96
+ const timeoutSeconds = declaration.timeout
97
+ ? parseTimeout(declaration.timeout)
98
+ : base.timeoutSeconds;
99
+ if (timeoutSeconds === null) {
100
+ throw new Error(
101
+ 'Invalid runtime sandbox timeout. Use timeout like "90m" or "2h".',
102
+ );
103
+ }
104
+ const size = declaration.size ?? 'standard';
105
+ return validatePlaySandboxRuntimeLimits({
106
+ timeoutSeconds,
107
+ ...PLAY_SANDBOX_SIZE_LIMITS[size],
108
+ });
109
+ }