deepline 0.3.87 → 0.3.89

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 (69) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +532 -2
  2. package/dist/bundling-sources/sdk/src/config.ts +77 -0
  3. package/dist/bundling-sources/sdk/src/http.ts +76 -30
  4. package/dist/bundling-sources/sdk/src/index.ts +6 -0
  5. package/dist/bundling-sources/sdk/src/play.ts +15 -1
  6. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
  7. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  8. package/dist/bundling-sources/sdk/src/types.ts +83 -4
  9. package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
  10. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
  11. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +9 -1
  12. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
  14. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +90 -12
  15. package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +78 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +407 -53
  18. package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
  19. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +57 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
  21. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +6 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
  23. package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +18 -4
  29. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +10 -8
  33. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
  35. package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +24 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
  37. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
  38. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +152 -17
  39. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
  40. package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -14
  41. package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
  42. package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
  43. package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
  44. package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
  45. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +44 -14
  46. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
  47. package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
  48. package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +560 -0
  49. package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
  50. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +59 -0
  51. package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
  52. package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
  53. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
  54. package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
  55. package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
  56. package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
  57. package/dist/cli/index.js +1065 -190
  58. package/dist/cli/index.mjs +1052 -177
  59. package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-DsLbQaew.d.mts} +132 -8
  60. package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-DsLbQaew.d.ts} +132 -8
  61. package/dist/index.d.mts +833 -11
  62. package/dist/index.d.ts +833 -11
  63. package/dist/index.js +467 -38
  64. package/dist/index.mjs +467 -38
  65. package/dist/install-integrity.json +20 -2
  66. package/dist/plays/bundle-play-file.d.mts +8 -3
  67. package/dist/plays/bundle-play-file.d.ts +8 -3
  68. package/dist/plays/bundle-play-file.mjs +100 -20
  69. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Shared, provider-agnostic vocabulary for a durable monitor operation.
3
+ *
4
+ * A batch operation is deliberately a small control document. It names a
5
+ * stable scope and a retry key; monitor definitions remain the source of
6
+ * truth for every individual item and the retained operation evidence is only
7
+ * a log of what the runner observed or did. Keeping this vocabulary pure
8
+ * makes CLI/API parsing, Convex admission and the background runner agree on
9
+ * the exact same request identity.
10
+ */
11
+
12
+ import { requireMonitorDeactivationReason } from './deactivation-reasons';
13
+
14
+ export const PROVIDER_MONITOR_BATCH_OPERATION_KINDS = [
15
+ 'deploy',
16
+ 'deactivate',
17
+ 'reactivate',
18
+ 'audit',
19
+ 'repair',
20
+ ] as const;
21
+
22
+ export type ProviderMonitorBatchOperationKind =
23
+ (typeof PROVIDER_MONITOR_BATCH_OPERATION_KINDS)[number];
24
+
25
+ export const PROVIDER_MONITOR_BATCH_TARGETS = [
26
+ 'all',
27
+ 'all-active',
28
+ 'billing-suspended',
29
+ 'needs-repair',
30
+ ] as const;
31
+
32
+ export type ProviderMonitorBatchTarget =
33
+ (typeof PROVIDER_MONITOR_BATCH_TARGETS)[number];
34
+
35
+ /**
36
+ * A manifest lives in durable blob storage. The runner reads it in bounded
37
+ * pages; never embed its definitions or a growing result array in a run
38
+ * record. `count` is declared at upload time and is used for honest progress
39
+ * before the first item has been processed.
40
+ */
41
+ export type ProviderMonitorBatchManifestScope = {
42
+ kind: 'manifest';
43
+ storageId: string;
44
+ count: number;
45
+ /** Content address used for idempotent retries; storage ids are ephemeral. */
46
+ sha256?: string;
47
+ };
48
+
49
+ export type ProviderMonitorBatchScope =
50
+ | { kind: 'keys'; monitorKeys: readonly string[] }
51
+ | { kind: 'target'; target: ProviderMonitorBatchTarget }
52
+ | { kind: 'fleet'; fleetId: string }
53
+ | ProviderMonitorBatchManifestScope;
54
+
55
+ export type ProviderMonitorBatchOperationRequest = {
56
+ kind: ProviderMonitorBatchOperationKind;
57
+ scope: ProviderMonitorBatchScope;
58
+ dryRun: boolean;
59
+ /** Required for deactivate; absent for every other operation. */
60
+ deactivationReason?: string;
61
+ };
62
+
63
+ export type ProviderMonitorBatchOperationState =
64
+ | 'running'
65
+ | 'retry_scheduled'
66
+ | 'completed'
67
+ | 'failed';
68
+
69
+ const IDEMPOTENCY_KEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/;
70
+
71
+ function canonicalJson(value: unknown): string {
72
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
73
+ if (value && typeof value === 'object') {
74
+ return `{${Object.entries(value as Record<string, unknown>)
75
+ .sort(([left], [right]) => left.localeCompare(right))
76
+ .map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
77
+ .join(',')}}`;
78
+ }
79
+ return JSON.stringify(value);
80
+ }
81
+
82
+ /**
83
+ * A job id is deterministic on purpose. Convex's existing `by_run` point
84
+ * lookup is then the idempotency index: the same org/key can only own one run,
85
+ * and its canonical request below decides whether it is a rejoin or conflict.
86
+ */
87
+ export function providerMonitorBatchOperationId(input: {
88
+ orgId: string;
89
+ idempotencyKey: string;
90
+ }): string {
91
+ const orgId = input.orgId.trim();
92
+ const idempotencyKey = input.idempotencyKey.trim();
93
+ if (!orgId)
94
+ throw new Error('A monitor batch operation requires an organization.');
95
+ if (!IDEMPOTENCY_KEY.test(idempotencyKey)) {
96
+ throw new Error(
97
+ 'idempotencyKey must be 1-96 characters of letters, numbers, dot, underscore, or hyphen.',
98
+ );
99
+ }
100
+ return `monitor-batch:${orgId}:${idempotencyKey}`;
101
+ }
102
+
103
+ export function isProviderMonitorBatchOperationKind(
104
+ value: string,
105
+ ): value is ProviderMonitorBatchOperationKind {
106
+ return (PROVIDER_MONITOR_BATCH_OPERATION_KINDS as readonly string[]).includes(
107
+ value,
108
+ );
109
+ }
110
+
111
+ export function isProviderMonitorBatchTarget(
112
+ value: string,
113
+ ): value is ProviderMonitorBatchTarget {
114
+ return (PROVIDER_MONITOR_BATCH_TARGETS as readonly string[]).includes(value);
115
+ }
116
+
117
+ /**
118
+ * Validate the representation shared by all entry points before any state is
119
+ * mutated. A request chooses exactly one selection mechanism. Key lists are
120
+ * kept intentionally small because a large explicit selection is a manifest,
121
+ * not an unbounded mutation argument.
122
+ */
123
+ export function validateProviderMonitorBatchOperationRequest(
124
+ request: ProviderMonitorBatchOperationRequest,
125
+ ): ProviderMonitorBatchOperationRequest {
126
+ if (!isProviderMonitorBatchOperationKind(request.kind)) {
127
+ throw new Error(`Unknown monitor batch operation '${request.kind}'.`);
128
+ }
129
+ if (!request.scope || typeof request.scope !== 'object') {
130
+ throw new Error('A monitor batch operation requires exactly one scope.');
131
+ }
132
+ const scope = request.scope;
133
+ if (scope.kind === 'keys') {
134
+ const keys = [...new Set(scope.monitorKeys.map((key) => key.trim()))];
135
+ if (
136
+ keys.length === 0 ||
137
+ keys.length !== scope.monitorKeys.length ||
138
+ keys.some((key) => key.length === 0)
139
+ ) {
140
+ throw new Error(
141
+ 'Monitor key scopes must contain unique, non-empty keys.',
142
+ );
143
+ }
144
+ if (keys.length > 100) {
145
+ throw new Error(
146
+ 'An explicit monitor batch contains at most 100 keys. Upload a manifest for larger selections.',
147
+ );
148
+ }
149
+ if (request.kind === 'deploy') {
150
+ throw new Error(
151
+ 'Batch deploy requires a durable manifest of complete definitions.',
152
+ );
153
+ }
154
+ if (request.kind === 'deactivate' && !request.deactivationReason) {
155
+ throw new Error(
156
+ 'Batch deactivate requires a declared deactivation reason.',
157
+ );
158
+ }
159
+ if (request.kind === 'deactivate') {
160
+ requireMonitorDeactivationReason(request.deactivationReason!);
161
+ }
162
+ return { ...request, scope: { kind: 'keys', monitorKeys: keys } };
163
+ }
164
+ if (scope.kind === 'target') {
165
+ if (request.kind === 'deploy') {
166
+ throw new Error(
167
+ 'Batch deploy requires a durable manifest of complete definitions.',
168
+ );
169
+ }
170
+ if (!isProviderMonitorBatchTarget(scope.target)) {
171
+ throw new Error(`Unknown monitor batch target '${scope.target}'.`);
172
+ }
173
+ if (request.kind === 'deactivate' && !request.deactivationReason) {
174
+ throw new Error(
175
+ 'Batch deactivate requires a declared deactivation reason.',
176
+ );
177
+ }
178
+ if (request.kind === 'deactivate') {
179
+ requireMonitorDeactivationReason(request.deactivationReason!);
180
+ }
181
+ return request;
182
+ }
183
+ if (scope.kind === 'fleet') {
184
+ if (request.kind === 'deploy') {
185
+ throw new Error(
186
+ 'Batch deploy requires a durable manifest of complete definitions.',
187
+ );
188
+ }
189
+ if (!scope.fleetId.trim())
190
+ throw new Error('A Fleet scope requires fleetId.');
191
+ if (request.kind === 'deactivate' && !request.deactivationReason) {
192
+ throw new Error(
193
+ 'Batch deactivate requires a declared deactivation reason.',
194
+ );
195
+ }
196
+ if (request.kind === 'deactivate') {
197
+ requireMonitorDeactivationReason(request.deactivationReason!);
198
+ }
199
+ return {
200
+ ...request,
201
+ scope: { kind: 'fleet', fleetId: scope.fleetId.trim() },
202
+ };
203
+ }
204
+ if (scope.kind === 'manifest') {
205
+ if (
206
+ !scope.storageId.trim() ||
207
+ !Number.isSafeInteger(scope.count) ||
208
+ scope.count < 1
209
+ ) {
210
+ throw new Error(
211
+ 'A manifest scope requires a storage id and a positive item count.',
212
+ );
213
+ }
214
+ if (request.kind !== 'deploy') {
215
+ throw new Error('Only batch deploy accepts a manifest scope.');
216
+ }
217
+ if (
218
+ scope.sha256 !== undefined &&
219
+ !/^[a-f0-9]{64}$/i.test(scope.sha256.trim())
220
+ ) {
221
+ throw new Error('A manifest sha256 must be a 64-character hex digest.');
222
+ }
223
+ return {
224
+ ...request,
225
+ scope: {
226
+ kind: 'manifest',
227
+ storageId: scope.storageId.trim(),
228
+ count: scope.count,
229
+ ...(scope.sha256 === undefined
230
+ ? {}
231
+ : { sha256: scope.sha256.trim().toLowerCase() }),
232
+ },
233
+ };
234
+ }
235
+ throw new Error(
236
+ 'A monitor batch operation requires exactly one supported scope.',
237
+ );
238
+ }
239
+
240
+ /**
241
+ * The persisted fingerprint is an equality proof, not a display hash. It is
242
+ * compact for normal target/key requests, collision-free, and avoids a Node
243
+ * crypto dependency in code shared with Convex.
244
+ */
245
+ export function providerMonitorBatchOperationRequestFingerprint(
246
+ request: ProviderMonitorBatchOperationRequest,
247
+ ): string {
248
+ const normalized = validateProviderMonitorBatchOperationRequest(request);
249
+ // A storage id is an implementation detail of an uploaded immutable file.
250
+ // Re-uploading identical bytes after a lost HTTP response must rejoin the
251
+ // same operation, not conflict merely because storage issued another id.
252
+ if (normalized.scope.kind === 'manifest' && normalized.scope.sha256) {
253
+ return canonicalJson({
254
+ ...normalized,
255
+ scope: {
256
+ kind: 'manifest',
257
+ count: normalized.scope.count,
258
+ sha256: normalized.scope.sha256,
259
+ },
260
+ });
261
+ }
262
+ return canonicalJson(normalized);
263
+ }
@@ -307,6 +307,14 @@ export async function retryProviderMonitorLifecycleAdmission<T>(
307
307
  export async function beginProviderMonitorLifecycleReservation(input: {
308
308
  auth: OrgAuthContext;
309
309
  monitorKey: string;
310
+ /**
311
+ * A durable bulk operation supplies its stable item attempt id. The
312
+ * ordinary scalar path intentionally leaves this unset and mints a fresh
313
+ * UUID. Reusing the same id lets a reclaimed batch refresh its own fence
314
+ * without ever turning an already-recorded provider intent into a second
315
+ * create.
316
+ */
317
+ operationId?: string;
310
318
  /** Tool and payload of the monitor being provisioned, so capacity can be
311
319
  * counted against the account it watches rather than against the row. */
312
320
  entityKey?: string | null;
@@ -315,7 +323,7 @@ export async function beginProviderMonitorLifecycleReservation(input: {
315
323
  expectedControlStateRevision?: number;
316
324
  requiredMonitorStatus?: 'active';
317
325
  }): Promise<ProviderMonitorLifecycleReservation> {
318
- const operationId = randomUUID();
326
+ const operationId = input.operationId ?? randomUUID();
319
327
  const action =
320
328
  input.state === 'provisioning'
321
329
  ? internal.providerMonitorIdentityClaims.beginLifecycleProvisioning
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The customer-visible state of a monitor. Provider work and audit evidence
3
+ * intentionally live on separate axes; neither may add a third value here.
4
+ */
5
+ export type ProviderMonitorState = 'active' | 'inactive';
6
+
7
+ declare const providerMonitorResourceIdBrand: unique symbol;
8
+
9
+ /**
10
+ * A non-empty provider-owned resource handle. This deliberately says
11
+ * nothing about the provider or its wire format: an adapter is the only
12
+ * layer that knows whether this happens to be a radar, subscription, etc.
13
+ */
14
+ export type ProviderMonitorResourceId = string & {
15
+ readonly [providerMonitorResourceIdBrand]: 'ProviderMonitorResourceId';
16
+ };
17
+
18
+ /** Turn untrusted storage/API input into the proof used by active state. */
19
+ export function providerMonitorResourceId(
20
+ value: string | null | undefined,
21
+ ): ProviderMonitorResourceId | null {
22
+ const normalized = typeof value === 'string' ? value.trim() : '';
23
+ return normalized === '' ? null : (normalized as ProviderMonitorResourceId);
24
+ }
25
+
26
+ /**
27
+ * A provider-neutral proof that this control record has an upstream
28
+ * counterpart. The identifier might be a radar id today and a different
29
+ * provider's resource id tomorrow; callers outside an adapter must never need
30
+ * to know which.
31
+ */
32
+ export type ProviderMonitorResourceBinding =
33
+ | { kind: 'bound'; providerResourceId: ProviderMonitorResourceId }
34
+ | { kind: 'unbound'; providerResourceId: null };
35
+
36
+ /**
37
+ * The state safe to show as a monitor's current operational state.
38
+ *
39
+ * An active intent without a resource is valid *work* (a create is pending or
40
+ * a historical record needs repair), but it is not an active monitor. The
41
+ * discriminated `active` branch therefore proves a non-empty provider id to
42
+ * TypeScript at every API/worker boundary that administers a live resource.
43
+ */
44
+ export type OperationalProviderMonitorState =
45
+ | {
46
+ state: 'active';
47
+ desired: 'active';
48
+ providerResourceId: ProviderMonitorResourceId;
49
+ binding: 'bound';
50
+ }
51
+ | {
52
+ state: 'inactive';
53
+ desired: ProviderMonitorState;
54
+ providerResourceId: string | null;
55
+ binding: 'bound' | 'unbound';
56
+ };
57
+
58
+ export type ActiveOperationalProviderMonitorState = Extract<
59
+ OperationalProviderMonitorState,
60
+ { state: 'active' }
61
+ >;
62
+
63
+ export function providerMonitorOperationalState(input: {
64
+ desired: ProviderMonitorState;
65
+ providerResourceId: string | null | undefined;
66
+ }): OperationalProviderMonitorState {
67
+ const providerResourceId = providerMonitorResourceId(input.providerResourceId);
68
+ if (input.desired === 'active' && providerResourceId !== null) {
69
+ return {
70
+ state: 'active',
71
+ desired: 'active',
72
+ providerResourceId,
73
+ binding: 'bound',
74
+ };
75
+ }
76
+ return {
77
+ state: 'inactive',
78
+ desired: input.desired,
79
+ providerResourceId,
80
+ binding: providerResourceId === null ? 'unbound' : 'bound',
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Runtime boundary for code that is about to publish or administer an active
86
+ * resource. Its return type is the same proof exposed to callers: an active
87
+ * record necessarily has a non-empty, provider-neutral resource id.
88
+ */
89
+ export function requireActiveProviderMonitorBinding(input: {
90
+ providerResourceId: string | null | undefined;
91
+ }): ActiveOperationalProviderMonitorState {
92
+ const operational = providerMonitorOperationalState({
93
+ desired: 'active',
94
+ providerResourceId: input.providerResourceId,
95
+ });
96
+ if (operational.state !== 'active') {
97
+ throw new Error(
98
+ 'An active provider monitor requires a non-empty provider resource id. Keep it pending until upstream provisioning returns one.',
99
+ );
100
+ }
101
+ return operational;
102
+ }
103
+
104
+ /**
105
+ * Translate the historical control-record lifecycle label at the sole
106
+ * compatibility boundary. New code persists `desired` and returns this type;
107
+ * this mapping exists only while the online backfill drains existing rows.
108
+ */
109
+ export function providerMonitorStateForLegacyStatus(
110
+ status: string | undefined,
111
+ ): ProviderMonitorState {
112
+ switch (status) {
113
+ case 'active':
114
+ case 'pending_create':
115
+ case 'replacement_pending':
116
+ case 'replacement_active_old_cleanup_pending':
117
+ case 'replacement_cleanup_failed':
118
+ return 'active';
119
+ default:
120
+ return 'inactive';
121
+ }
122
+ }
@@ -101,7 +101,11 @@ export type PlayActivityState =
101
101
  };
102
102
 
103
103
  export type PlayActivityObservation = {
104
- schemaVersion: 1;
104
+ /**
105
+ * Retired marker. Producers stopped writing it; envelopes still in flight
106
+ * from an older producer carry `1`, so readers tolerate both shapes.
107
+ */
108
+ schemaVersion?: 1;
105
109
  /** Stable within one run, for example `tool:discover_ctos` or `dataset:leads`. */
106
110
  activityId: string;
107
111
  /** Optional owning play step. */
@@ -126,7 +130,7 @@ export function isPlayActivityObservation(
126
130
  ): value is PlayActivityObservation {
127
131
  if (
128
132
  !isRecord(value) ||
129
- value.schemaVersion !== 1 ||
133
+ (value.schemaVersion !== undefined && value.schemaVersion !== 1) ||
130
134
  typeof value.activityId !== 'string' ||
131
135
  !value.activityId.trim() ||
132
136
  typeof value.observedAt !== 'number' ||
@@ -316,7 +320,6 @@ export function createPlayActivityReporter(input: {
316
320
  type: 'activity.observed',
317
321
  observation: {
318
322
  ...observation,
319
- schemaVersion: 1,
320
323
  observedAt: observation.observedAt ?? input.now?.() ?? Date.now(),
321
324
  },
322
325
  });
@@ -402,7 +405,6 @@ export function projectPlayRunActivity(input: {
402
405
  if (input.waitKind === 'detached_runner') {
403
406
  return projection(
404
407
  {
405
- schemaVersion: 1,
406
408
  activityId: 'runner:managed',
407
409
  target: {
408
410
  kind: 'runner',
@@ -418,7 +420,6 @@ export function projectPlayRunActivity(input: {
418
420
  if (input.waitKind === 'sleep') {
419
421
  return projection(
420
422
  {
421
- schemaVersion: 1,
422
423
  activityId: 'run:sleep',
423
424
  target: {
424
425
  kind: 'step',
@@ -441,7 +442,6 @@ export function projectPlayRunActivity(input: {
441
442
  ) {
442
443
  return projection(
443
444
  {
444
- schemaVersion: 1,
445
445
  activityId: `event:${input.activeNodeId ?? 'run'}`,
446
446
  stepId: input.activeNodeId ?? undefined,
447
447
  target: {
@@ -468,7 +468,6 @@ export function projectPlayRunActivity(input: {
468
468
  if (normalizedStatus === 'waiting') {
469
469
  return projection(
470
470
  {
471
- schemaVersion: 1,
472
471
  activityId: 'run:legacy-wait',
473
472
  target: {
474
473
  kind: 'step',
@@ -506,7 +505,6 @@ export function projectPlayRunActivity(input: {
506
505
  const datasetProgress = datasetStep?.progress ?? null;
507
506
  return projection(
508
507
  {
509
- schemaVersion: 1,
510
508
  activityId: `dataset:${pendingDataset.datasetId ?? pendingDataset.tableNamespace}`,
511
509
  target: {
512
510
  kind: 'dataset',
@@ -548,7 +546,6 @@ export function projectPlayRunActivity(input: {
548
546
  const provider = toolId?.split(/[._]/)[0]?.trim() || null;
549
547
  return projection(
550
548
  {
551
- schemaVersion: 1,
552
549
  activityId: `step:${activeStep.nodeId}`,
553
550
  stepId: activeStep.nodeId,
554
551
  target:
@@ -577,7 +574,6 @@ export function projectPlayRunActivity(input: {
577
574
  if (normalizedStatus === 'queued') {
578
575
  return projection(
579
576
  {
580
- schemaVersion: 1,
581
577
  activityId: 'capacity:worker',
582
578
  target: { kind: 'capacity', pool: 'worker' },
583
579
  state: { kind: 'queued', reason: 'capacity' },
@@ -588,7 +584,6 @@ export function projectPlayRunActivity(input: {
588
584
  }
589
585
 
590
586
  const fallback: PlayActivityObservation = {
591
- schemaVersion: 1,
592
587
  activityId: 'run:legacy',
593
588
  target: {
594
589
  kind: 'step',
@@ -4,6 +4,7 @@ import { stat } from 'node:fs/promises';
4
4
  import { Readable } from 'node:stream';
5
5
  import { pipeline } from 'node:stream/promises';
6
6
  import type { PlaySheetContract } from '../plays/static-pipeline';
7
+ import type { AsyncPlayRunRef } from '../plays/authoring-contract';
7
8
  import type { PlayRowUpdate } from './/ctx-types';
8
9
  import type { PlayRunTimelineEntry } from './/live-events';
9
10
  import type { PlayDatasetBornFrom } from './/cell-provenance';
@@ -179,6 +180,51 @@ type RuntimeApiRequest =
179
180
  runId: string;
180
181
  firstClaimLatencyMs: number;
181
182
  }
183
+ | {
184
+ action: 'launch_play_async';
185
+ key: string;
186
+ invocationKey: string;
187
+ playRef: string;
188
+ input: Record<string, unknown>;
189
+ description: string;
190
+ schedulerSchema?: string | null;
191
+ ancestryPlayIds: string[];
192
+ }
193
+ | {
194
+ action: 'project_run_created';
195
+ idempotencyKey: string;
196
+ playName: string;
197
+ playReference?: string | null;
198
+ workflowId: string;
199
+ runId: string;
200
+ workflowFamilyKey: string;
201
+ definitionScope?: 'org' | 'system';
202
+ revisionId?: string | null;
203
+ artifactStorageKey?: string | null;
204
+ artifactHash?: string | null;
205
+ graphHash?: string | null;
206
+ runtimeBackend?: string | null;
207
+ schedulerBackend?: string | null;
208
+ schedulerSchema?: string | null;
209
+ executionProfile?: string | null;
210
+ runtimeReleaseId?: string | null;
211
+ runtimeDeployVersion?: string | null;
212
+ runtimeCallbackBaseUrl?: string | null;
213
+ coordinatorUrl?: string | null;
214
+ coordinatorWorkerName?: string | null;
215
+ runtimeWorkflowName?: string | null;
216
+ runtimeHarnessWorkerName?: string | null;
217
+ maxCreditsPerRun?: number | null;
218
+ staticPipeline?: unknown;
219
+ source?: 'published' | 'ad_hoc' | 'draft';
220
+ triggerSource?: 'webhook' | 'cron' | 'sql_listener' | 'api';
221
+ inputFileId?: string;
222
+ inputBytes?: number;
223
+ inputSha256?: string;
224
+ replayedFromRunId?: string | null;
225
+ secretRefs?: unknown[];
226
+ inputSummary?: Record<string, unknown>;
227
+ }
182
228
  | ({
183
229
  action: 'save_results';
184
230
  } & RuntimeSaveResults)
@@ -719,7 +765,8 @@ function isRetryableAppRuntimeAction(
719
765
  action === 'release_runtime_step_receipt' ||
720
766
  action === 'save_results' ||
721
767
  action === 'skip_runtime_step_receipt' ||
722
- action === 'start_run'
768
+ action === 'start_run' ||
769
+ action === 'launch_play_async'
723
770
  );
724
771
  }
725
772
 
@@ -1073,7 +1120,8 @@ export class AppRuntimeApiResponseError extends Error {
1073
1120
  this.requestId = input.requestId?.trim() || null;
1074
1121
  this.retryable = input.retryable;
1075
1122
  this.retryAfterMs =
1076
- typeof input.retryAfterMs === 'number' && Number.isFinite(input.retryAfterMs)
1123
+ typeof input.retryAfterMs === 'number' &&
1124
+ Number.isFinite(input.retryAfterMs)
1077
1125
  ? Math.max(0, Math.floor(input.retryAfterMs))
1078
1126
  : null;
1079
1127
  this.detail = input.detail;
@@ -1963,16 +2011,14 @@ export async function appendRunEventsViaAppRuntime(
1963
2011
  let terminalBatchIndex = -1;
1964
2012
  for (let index = batches.length - 1; index >= 0; index -= 1) {
1965
2013
  if (
1966
- batches[index]!.some(
1967
- (event) => {
1968
- const key = event.key ?? event.type;
1969
- return (
1970
- key === 'run.completed' ||
1971
- key === 'run.failed' ||
1972
- key === 'run.cancelled'
1973
- );
1974
- },
1975
- )
2014
+ batches[index]!.some((event) => {
2015
+ const key = event.key ?? event.type;
2016
+ return (
2017
+ key === 'run.completed' ||
2018
+ key === 'run.failed' ||
2019
+ key === 'run.cancelled'
2020
+ );
2021
+ })
1976
2022
  ) {
1977
2023
  terminalBatchIndex = index;
1978
2024
  break;
@@ -2021,6 +2067,19 @@ export async function startRunViaAppRuntime(
2021
2067
  });
2022
2068
  }
2023
2069
 
2070
+ export async function projectRunCreatedViaAppRuntime(
2071
+ context: WorkerRuntimeApiContext,
2072
+ input: Omit<
2073
+ Extract<RuntimeApiRequest, { action: 'project_run_created' }>,
2074
+ 'action'
2075
+ >,
2076
+ ): Promise<void> {
2077
+ await postAppRuntimeApi<{ ok: true; status: 'queued' }>(context, {
2078
+ action: 'project_run_created',
2079
+ ...input,
2080
+ });
2081
+ }
2082
+
2024
2083
  export async function recordFirstClaimLatencyViaAppRuntime(
2025
2084
  context: WorkerRuntimeApiContext,
2026
2085
  input: { runId: string; firstClaimLatencyMs: number },
@@ -2031,6 +2090,25 @@ export async function recordFirstClaimLatencyViaAppRuntime(
2031
2090
  });
2032
2091
  }
2033
2092
 
2093
+ export async function launchPlayAsyncViaAppRuntime(
2094
+ context: WorkerRuntimeApiContext,
2095
+ input: {
2096
+ key: string;
2097
+ invocationKey: string;
2098
+ playRef: string;
2099
+ input: Record<string, unknown>;
2100
+ description: string;
2101
+ schedulerSchema?: string | null;
2102
+ ancestryPlayIds: readonly string[];
2103
+ },
2104
+ ): Promise<AsyncPlayRunRef> {
2105
+ return await postAppRuntimeApi<AsyncPlayRunRef>(context, {
2106
+ action: 'launch_play_async',
2107
+ ...input,
2108
+ ancestryPlayIds: [...input.ancestryPlayIds],
2109
+ });
2110
+ }
2111
+
2034
2112
  export async function saveResultsViaAppRuntime(
2035
2113
  context: WorkerRuntimeApiContext,
2036
2114
  input: RuntimeSaveResults,