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,68 @@
1
+ import { RUNTIME_CAPACITY_POLICY } from '../../runtime-capacity-policy';
2
+
3
+ export type DaytonaSandboxStartAdmission = {
4
+ run<T>(operation: () => Promise<T>): Promise<T>;
5
+ };
6
+
7
+ /**
8
+ * Daytona sandbox starts are control-plane transitions, not execution work.
9
+ * Keep their admission independent from Absurd worker slots so a run burst can
10
+ * still multiplex started sandboxes without stampeding the provider's create
11
+ * transition. A permit covers timeout reconciliation too: releasing it when
12
+ * `create` times out would admit another create while the first named sandbox
13
+ * is still transitioning, recreating the unsafe overlap this gate prevents.
14
+ */
15
+ export function createDaytonaSandboxStartAdmission(
16
+ concurrency =
17
+ RUNTIME_CAPACITY_POLICY.sandboxAcquisition
18
+ .daytonaConcurrentStartsPerProcess,
19
+ ): DaytonaSandboxStartAdmission {
20
+ if (!Number.isInteger(concurrency) || concurrency <= 0) {
21
+ throw new Error(
22
+ 'Daytona sandbox-start concurrency must be a positive integer.',
23
+ );
24
+ }
25
+
26
+ let active = 0;
27
+ const waiters: Array<(release: () => void) => void> = [];
28
+
29
+ const createRelease = (): (() => void) => {
30
+ let released = false;
31
+ return () => {
32
+ if (released) return;
33
+ released = true;
34
+ const next = waiters.shift();
35
+ if (next) {
36
+ // Transfer the occupied permit directly. Keeping `active` unchanged
37
+ // prevents a new eager caller from jumping ahead of the FIFO waiter.
38
+ next(createRelease());
39
+ return;
40
+ }
41
+ active -= 1;
42
+ };
43
+ };
44
+
45
+ return {
46
+ async run<T>(operation: () => Promise<T>): Promise<T> {
47
+ // Preserve eager prepare semantics when capacity is free: the provider
48
+ // create begins in this turn, just as it did before admission existed.
49
+ // Only contended callers cross an asynchronous queue boundary.
50
+ const release =
51
+ active < concurrency
52
+ ? (() => {
53
+ active += 1;
54
+ return createRelease();
55
+ })()
56
+ : await new Promise<() => void>((resolve) => waiters.push(resolve));
57
+ try {
58
+ return await operation();
59
+ } finally {
60
+ release();
61
+ }
62
+ },
63
+ };
64
+ }
65
+
66
+ /** One admission queue per worker process; all run slots share it. */
67
+ export const daytonaSandboxStartAdmission =
68
+ createDaytonaSandboxStartAdmission();
@@ -43,6 +43,7 @@ import {
43
43
  prepareDetachedDaytonaRunner,
44
44
  } from './daytona-session-execution';
45
45
  import { RUNTIME_RELIABILITY_POLICY } from '../../runtime-reliability-policy';
46
+ import { daytonaSandboxStartAdmission } from './daytona-start-admission';
46
47
 
47
48
  const DAYTONA_COMMAND_RECOVERY_TIMEOUT_MS =
48
49
  RUNTIME_RELIABILITY_POLICY.sandbox.daytonaCommandRecoveryTimeoutMs;
@@ -973,6 +974,7 @@ function prepareDaytonaExecution(
973
974
  callbacks.reserveSandboxCapacity!('daytona', providerAttempt)
974
975
  : undefined,
975
976
  releaseSandboxCapacity: callbacks?.releaseSandboxCapacity,
977
+ startAdmission: daytonaSandboxStartAdmission,
976
978
  });
977
979
  return {
978
980
  kind: 'daytona',
@@ -11,6 +11,7 @@ import type { DetachedRunnerSuspension } from '../suspension';
11
11
  export type RuntimeSandboxProviderId = 'daytona' | 'modal';
12
12
 
13
13
  export type RuntimeSandboxRef = {
14
+ /** Frozen at 1: persisted in suspension_json, so readers must keep taking 1. */
14
15
  schemaVersion: 1;
15
16
  provider: RuntimeSandboxProviderId;
16
17
  resourceId: string;
@@ -3361,8 +3361,8 @@ async function writeRuntimeRows(
3361
3361
  const insertedRowsCte =
3362
3362
  input.mode === 'upsert'
3363
3363
  ? `inserted_rows AS (
3364
- INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index${physicalInsertColumnsSql})
3365
- SELECT _key, 'pending', $4::text, _input_index${physicalInsertValuesSql}
3364
+ INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index, _created_at${physicalInsertColumnsSql})
3365
+ SELECT _key, 'pending', $4::text, _input_index, now()${physicalInsertValuesSql}
3366
3366
  FROM input_rows
3367
3367
  ON CONFLICT (_key) DO UPDATE SET
3368
3368
  _status = 'pending',
@@ -3374,8 +3374,8 @@ async function writeRuntimeRows(
3374
3374
  RETURNING _key
3375
3375
  ),`
3376
3376
  : `inserted_rows AS (
3377
- INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index${physicalInsertColumnsSql})
3378
- SELECT _key, 'pending', $4::text, _input_index${physicalInsertValuesSql}
3377
+ INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index, _created_at${physicalInsertColumnsSql})
3378
+ SELECT _key, 'pending', $4::text, _input_index, now()${physicalInsertValuesSql}
3379
3379
  FROM input_rows
3380
3380
  RETURNING _key
3381
3381
  ),`;
@@ -3660,8 +3660,8 @@ async function prepareRuntimeSheetDatasetRows(
3660
3660
  RETURNING target._key
3661
3661
  ),
3662
3662
  inserted_rows AS (
3663
- INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index, _attempt_id, _attempt_owner_run_id, _attempt_expires_at, _attempt_seq, _write_version, _writer_run_id${input.physicalInsertColumnsSql})
3664
- SELECT _key, 'pending', $4::text, _input_index, $7::text, $8::text, $9::timestamptz, $10::integer, $12::bigint, $4::text${input.physicalInsertValuesSql}
3663
+ INSERT INTO ${sheetTable(session)} (_key, _status, _run_id, _input_index, _attempt_id, _attempt_owner_run_id, _attempt_expires_at, _attempt_seq, _write_version, _writer_run_id, _created_at${input.physicalInsertColumnsSql})
3664
+ SELECT _key, 'pending', $4::text, _input_index, $7::text, $8::text, $9::timestamptz, $10::integer, $12::bigint, $4::text, now()${input.physicalInsertValuesSql}
3665
3665
  FROM input_rows
3666
3666
  ON CONFLICT (_key) DO UPDATE SET
3667
3667
  _status = 'pending',
@@ -3991,9 +3991,9 @@ async function prepareNetNewRuntimeSheetDatasetRows(
3991
3991
  inserted_rows AS (
3992
3992
  INSERT INTO ${sheetTable(session)}
3993
3993
  (_key, _status, _run_id, _input_index, _attempt_id, _attempt_owner_run_id,
3994
- _attempt_expires_at, _attempt_seq, _write_version, _writer_run_id${input.physicalInsertColumnsSql})
3994
+ _attempt_expires_at, _attempt_seq, _write_version, _writer_run_id, _created_at${input.physicalInsertColumnsSql})
3995
3995
  SELECT _key, 'pending', $4::text, _input_index, $5::text, $6::text,
3996
- $7::timestamptz, $8::integer, $9::bigint, $4::text${input.physicalInsertValuesSql}
3996
+ $7::timestamptz, $8::integer, $9::bigint, $4::text, now()${input.physicalInsertValuesSql}
3997
3997
  FROM input_rows
3998
3998
  ON CONFLICT (_key) DO NOTHING
3999
3999
  RETURNING _key
@@ -7642,6 +7642,7 @@ async function insertMissingCompletedMapRowChunks(
7642
7642
  _attempt_owner_run_id,
7643
7643
  _attempt_expires_at,
7644
7644
  _attempt_seq,
7645
+ _created_at,
7645
7646
  _cell_meta${physicalInsertColumnsSql}
7646
7647
  )
7647
7648
  SELECT _key,
@@ -7652,6 +7653,7 @@ async function insertMissingCompletedMapRowChunks(
7652
7653
  $9::text,
7653
7654
  $10::timestamptz,
7654
7655
  $11::integer,
7656
+ now(),
7655
7657
  cell_meta_patch${physicalInsertValuesSql}
7656
7658
  FROM missing_rows
7657
7659
  ON CONFLICT (_key) DO NOTHING
@@ -26,6 +26,14 @@ export const RUNTIME_CAPACITY_POLICY = {
26
26
  claimOperationTimeoutMs: 20_000,
27
27
  requestTimeoutMs: 30_000,
28
28
  },
29
+ sandboxAcquisition: {
30
+ /**
31
+ * Starting a Daytona sandbox is a provider control-plane transition, not
32
+ * an Absurd execution slot. Serialize starts within each worker process;
33
+ * once started, all eight execution slots may run concurrently.
34
+ */
35
+ daytonaConcurrentStartsPerProcess: 1,
36
+ },
29
37
  } as const;
30
38
 
31
39
  export const ABSURD_GLOBAL_RUN_CONCURRENCY =
@@ -96,6 +104,18 @@ function nonNegativeNumber(value: number): number {
96
104
  }
97
105
 
98
106
  export function assertRuntimeCapacityPolicy(): void {
107
+ if (
108
+ !Number.isInteger(
109
+ RUNTIME_CAPACITY_POLICY.sandboxAcquisition
110
+ .daytonaConcurrentStartsPerProcess,
111
+ ) ||
112
+ RUNTIME_CAPACITY_POLICY.sandboxAcquisition
113
+ .daytonaConcurrentStartsPerProcess <= 0
114
+ ) {
115
+ throw new Error(
116
+ 'Daytona sandbox-start concurrency must be a positive integer.',
117
+ );
118
+ }
99
119
  if (
100
120
  RUNTIME_CAPACITY_POLICY.receiptGateway.requestTimeoutMs <=
101
121
  RUNTIME_CAPACITY_POLICY.receiptGateway.admissionTimeoutMs
@@ -3,11 +3,17 @@
3
3
  * receipt presentations. New operations must not use this as a version gate.
4
4
  */
5
5
  export const LEGACY_RUNTIME_RECEIPT_WIRE_HEADER = 'x-deepline-runtime-contract';
6
+ // Frozen: production lane main-9281ae259f still sends wire format 2. Delete
7
+ // this and `resolveLegacyRuntimeReceiptWireFormat` after that lane retires.
6
8
  export const LEGACY_RUNTIME_RECEIPT_WIRE_FORMAT = 3 as const;
7
9
  /**
8
10
  * Historical workers validate this readiness range before they claim work.
9
11
  * It is an output-only compatibility presentation; current callers use named
10
12
  * operation capabilities and do not treat it as a global admission contract.
13
+ *
14
+ * Frozen: a lane built before the global compatibility gate was removed calls
15
+ * /readyz at boot and throws when this range is absent. Delete after every such
16
+ * lane has retired.
11
17
  */
12
18
  export const LEGACY_RUNTIME_READINESS_MIN_SUPPORTED = 1 as const;
13
19
  export const LEGACY_RUNTIME_READINESS_CURRENT = 6 as const;
@@ -12,6 +12,30 @@
12
12
  */
13
13
  export const PROD_DEFAULT_RUNTIME_SCHEDULER_SCHEMA = 'runtime_scheduler';
14
14
 
15
+ /** A wire-safe Postgres schema name that identifies one runtime topology. */
16
+ export const RUNTIME_SCHEDULER_SCHEMA_RE = /^[a-z_][a-z0-9_]{0,62}$/;
17
+
18
+ export function normalizeRuntimeSchedulerSchema(value: unknown): string | null {
19
+ if (typeof value !== 'string') return null;
20
+ const schema = value.trim();
21
+ return RUNTIME_SCHEDULER_SCHEMA_RE.test(schema) ? schema : null;
22
+ }
23
+
24
+ /**
25
+ * The one release-id shape. A release id is minted by the release controller
26
+ * and doubles as the lane's queue suffix (`runs_<releaseId>`) and the
27
+ * `DEEPLINE_RUNTIME_DEPLOY_VERSION` on every Machine in the lane, so it must be
28
+ * a stable lowercase engine-safe slug: no uppercase, no underscore, no repeated
29
+ * or trailing hyphen, 6 to 64 characters.
30
+ *
31
+ * It lives here, dependency-free, because the Fly lane operator, the release
32
+ * controller, the internal registry route, and the admin routes must all agree
33
+ * on it — a second copy is how one surface silently starts accepting an id the
34
+ * queue cannot name.
35
+ */
36
+ export const ABSURD_RELEASE_ID_RE =
37
+ /^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){5,63}$/;
38
+
15
39
  /**
16
40
  * True when the scheduler schema names an isolated (preview CI / worktree)
17
41
  * topology. Null/empty/prod-default => false (production topology). Callers
@@ -41,10 +41,36 @@ export type { PlayRunInputPayload };
41
41
 
42
42
  export type PlaySchedulerSubmitInput = {
43
43
  runId: string;
44
+ /** First-class async-child lineage. Absent for root launches. */
45
+ parentRunId?: string | null;
46
+ rootRunId?: string | null;
47
+ asyncLaunchKey?: string | null;
48
+ asyncInvocationKey?: string | null;
49
+ asyncRequestFingerprint?: string | null;
50
+ /** Full play ancestry for a scheduled child, including this play. */
51
+ asyncAncestryPlayIds?: string[] | null;
44
52
  playId: string;
45
53
  playName: string;
54
+ playReference?: string | null;
55
+ /**
56
+ * Immutable scheduler namespace for this admission. The durable
57
+ * `run.created` projector persists it onto the Play Run so replay/control
58
+ * paths stay on the original runtime lane instead of falling back to a host
59
+ * default.
60
+ */
61
+ schedulerSchema?: string | null;
62
+ definitionScope?: 'org' | 'system' | null;
63
+ revisionId?: string | null;
46
64
  workflowFamilyKey?: string | null;
47
65
  artifactStorageKey: string;
66
+ /**
67
+ * The runtime PostgreSQL admission path committed this launch before its
68
+ * Convex `run.created` projection exists. The worker must deliver that
69
+ * causal fact before it can claim an execution attempt.
70
+ *
71
+ * Omitted for legacy launches whose Play Run row was persisted synchronously.
72
+ */
73
+ requiresRunCreatedProjection?: boolean;
48
74
  /** Optional inline artifact for the Node scheduler. */
49
75
  runtimeArtifact?: unknown;
50
76
  artifactHash: string;
@@ -52,6 +78,9 @@ export type PlaySchedulerSubmitInput = {
52
78
  /** Immutable scheduler/worker/runner ABI snapshot (not a Git SHA). */
53
79
  runtimeProtocolVersion?: PlayRuntimeProtocolVersion | null;
54
80
  input: PlayRunInputPayload;
81
+ /** Bounded launch metadata used only by the asynchronous ledger projector. */
82
+ staticPipeline?: unknown;
83
+ inputSummary?: Record<string, unknown>;
55
84
  /** Convex metadata for the exact input saved before scheduler submission. */
56
85
  inputFileId?: string;
57
86
  inputBytes?: number;
@@ -127,7 +156,7 @@ export type PlaySchedulerSubmitInput = {
127
156
  userId?: string | null;
128
157
  source?: 'published' | 'ad_hoc' | 'draft';
129
158
  /** Invocation origin, distinct from the artifact source above. */
130
- triggerSource?: 'api' | 'webhook' | 'cron' | 'sql_listener';
159
+ triggerSource?: 'api' | 'webhook' | 'cron' | 'sql_listener' | 'async_child';
131
160
  /** Null lets a durable trigger wait for capacity; a number bounds queue delay. */
132
161
  queueMaxDelaySeconds?: number | null;
133
162
  /**
@@ -20,10 +20,13 @@ export type PlayRuntimeFeature =
20
20
  | 'packaged_files';
21
21
 
22
22
  export type PlayArtifactCompatibility = {
23
- apiVersion: number;
23
+ /** Historical marker carried by stored artifacts. Nothing branches on it. */
24
+ apiVersion?: number;
24
25
  artifactVersion: number;
25
- minRunnerVersion: number;
26
- runtimeFeatures: PlayRuntimeFeature[];
26
+ /** Historical marker carried by stored artifacts. Every runner reads every artifact. */
27
+ minRunnerVersion?: number;
28
+ /** Historical marker carried by stored artifacts. Capabilities are named per operation. */
29
+ runtimeFeatures?: PlayRuntimeFeature[];
27
30
  runtimeBackend?: string | null;
28
31
  /** Missing preserves the legacy schema used before this field existed. */
29
32
  toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
@@ -7,10 +7,10 @@ import type { ToolExecutionErrorSchemaVersion } from './tool-execution-error';
7
7
  import type { PlayDataset, PlayDatasetInput, PlayDatasetRow } from './dataset';
8
8
 
9
9
  export const LEGACY_PLAY_AUTHORING_CONTRACT_EDITION = 1 as const;
10
- export const PLAY_AUTHORING_CONTRACT_EDITION = 6 as const;
10
+ export const PLAY_AUTHORING_CONTRACT_EDITION = 7 as const;
11
11
  export const PLAY_AUTHORING_INPUT_SCHEMA_SNAPSHOT_EDITION = 3 as const;
12
12
  export const SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS = [
13
- 1, 2, 3, 4, 5, 6,
13
+ 1, 2, 3, 4, 5, 6, 7,
14
14
  ] as const;
15
15
 
16
16
  export type PlayAuthoringContractEdition =
@@ -67,6 +67,14 @@ export const PLAY_AUTHORING_CONTRACT_CHANGELOG = [
67
67
  newWritesEnd: null,
68
68
  readerRemoval: null,
69
69
  },
70
+ {
71
+ edition: 7,
72
+ changed:
73
+ 'Slack HITL calls that omit timeout wait one week. Editions 1–6 retain the historical 24-hour default.',
74
+ compatibilityOwner: 'Plays Runtime',
75
+ newWritesEnd: null,
76
+ readerRemoval: null,
77
+ },
70
78
  ] as const;
71
79
 
72
80
  /**
@@ -917,6 +925,8 @@ export type PlayAuthoringDefinedPlay<
917
925
  readonly runtime?: PlayAuthoringBindings<TInput>['runtime'];
918
926
  readonly compatibility?: PlayAuthoringBindings<TInput>['compatibility'];
919
927
  readonly playName: string;
928
+ /** Type-only input carrier used by typed Play-reference consumers. */
929
+ readonly __inputType?: TInput;
920
930
  };
921
931
 
922
932
  /** Canonical resolver shape for a customer-authored durable step. */
@@ -1153,20 +1163,14 @@ export type PlayAuthoringDatasetBuilder<
1153
1163
  TContext
1154
1164
  >;
1155
1165
  /** Add all columns declared by one reusable step program. */
1156
- withColumns<
1157
- Program extends PlayAuthoringStepProgram<
1166
+ withColumns<ProgramOutput extends OutputRow, Return>(
1167
+ program: PlayAuthoringStepProgram<
1158
1168
  OutputRow,
1159
- object,
1169
+ ProgramOutput,
1160
1170
  TContext,
1161
- unknown
1171
+ Return
1162
1172
  >,
1163
- >(
1164
- program: Program,
1165
- ): PlayAuthoringDatasetBuilder<
1166
- InputRow,
1167
- PlayAuthoringStepProgramOutput<Program>,
1168
- TContext
1169
- >;
1173
+ ): PlayAuthoringDatasetBuilder<InputRow, ProgramOutput, TContext>;
1170
1174
  /** @deprecated Dataset `.step(...)` was replaced by `.withColumn(...)`. */
1171
1175
  step<Name extends string, Value>(
1172
1176
  name: Name,
@@ -1183,9 +1187,34 @@ export type PlayAuthoringDatasetBuilder<
1183
1187
  ): Promise<PlayDataset<OutputRow>>;
1184
1188
  };
1185
1189
 
1186
- export type PlayAuthoringReferenceLike =
1190
+ export type PlayAuthoringReferenceLike<
1191
+ TInput extends Record<string, unknown> = Record<string, unknown>,
1192
+ > = (
1187
1193
  | { readonly playName: string; readonly name?: string }
1188
- | { readonly name: string; readonly playName?: string };
1194
+ | { readonly name: string; readonly playName?: string }
1195
+ ) & {
1196
+ /** Type-only input carrier used by ctx.runPlayAsync. */
1197
+ readonly __inputType?: TInput;
1198
+ };
1199
+
1200
+ declare const PLAY_RUN_ID_BRAND: unique symbol;
1201
+
1202
+ /** Branded run identity returned inside {@link AsyncPlayRunRef}. */
1203
+ export type PlayRunId = string & {
1204
+ readonly [PLAY_RUN_ID_BRAND]: 'PlayRunId';
1205
+ };
1206
+
1207
+ /**
1208
+ * Serializable reference returned after an async child Run is durable.
1209
+ * @sdkReference runtime 143 AsyncPlayRunRef
1210
+ */
1211
+ export type AsyncPlayRunRef = Readonly<{
1212
+ runId: PlayRunId;
1213
+ }>;
1214
+
1215
+ export type PlayAuthoringAsyncCallOptions = Readonly<{
1216
+ description: string;
1217
+ }>;
1189
1218
 
1190
1219
  /** Discoverable, non-deprecated Runtime Context members shown to Play authors. */
1191
1220
  export const PLAY_AUTHORING_RUNTIME_CONTEXT_MEMBERS = [
@@ -1196,6 +1225,7 @@ export const PLAY_AUTHORING_RUNTIME_CONTEXT_MEMBERS = [
1196
1225
  'log',
1197
1226
  'run',
1198
1227
  'runPlay',
1228
+ 'runPlayAsync',
1199
1229
  'runSteps',
1200
1230
  'secrets',
1201
1231
  'sleep',
@@ -1541,6 +1571,27 @@ export interface PlayAuthoringRuntimeContext {
1541
1571
  options: PlayAuthoringCallOptions,
1542
1572
  ): Promise<TOutput>;
1543
1573
 
1574
+ /**
1575
+ * Admit an independent scalar Play Run and return its durable identity after scheduler admission, before child completion.
1576
+ * @sdkReference runtime 141 ctx.runPlayAsync(key, playRef, input, options)
1577
+ */
1578
+ runPlayAsync<TInput extends Record<string, unknown>>(
1579
+ /** Replay-stable identity for this launch site and row scope. */
1580
+ key: string,
1581
+ /** Independently published Play name or definePlay handle carrying its input type. */
1582
+ playRef: PlayAuthoringReferenceLike<TInput>,
1583
+ /** Input checked against the referenced definePlay handle when available. */
1584
+ input: NoInfer<TInput>,
1585
+ /** Human-readable launch purpose recorded in the parent's step history. */
1586
+ options: PlayAuthoringAsyncCallOptions,
1587
+ ): Promise<AsyncPlayRunRef>;
1588
+ runPlayAsync(
1589
+ key: string,
1590
+ playRef: string,
1591
+ input: Record<string, unknown>,
1592
+ options: PlayAuthoringAsyncCallOptions,
1593
+ ): Promise<AsyncPlayRunRef>;
1594
+
1544
1595
  /**
1545
1596
  * Write a customer-safe line to the durable Run Log Stream. Omitting level
1546
1597
  * preserves the historical info-level behavior.
@@ -2600,6 +2651,84 @@ export const PLAY_AUTHORING_FIELD_REGISTRY = {
2600
2651
  errorMessage:
2601
2652
  'ctx.runPlay timeoutMs is unsupported because child Plays execute inline.',
2602
2653
  },
2654
+ 'ctx.runPlayAsync.key': {
2655
+ schema: Type.String({ minLength: 1 }),
2656
+ fixtures: {
2657
+ valid: 'request-approval',
2658
+ invalid: '',
2659
+ absent: undefined,
2660
+ unresolved: { expression: 'callKey' },
2661
+ edition1: 'request-approval',
2662
+ },
2663
+ referenceType: 'string',
2664
+ required: true,
2665
+ resolution: 'runtime-dynamic',
2666
+ issueCode: 'play_authoring_run_play_option_invalid',
2667
+ description: 'Stable identity for one independent child Play Run.',
2668
+ errorMessage: 'ctx.runPlayAsync key must be a non-empty string.',
2669
+ },
2670
+ 'ctx.runPlayAsync.playRef': {
2671
+ schema: Type.Union([
2672
+ Type.String({ minLength: 1 }),
2673
+ Type.Union([
2674
+ Type.Object(
2675
+ { playName: Type.String({ minLength: 1 }) },
2676
+ { additionalProperties: true },
2677
+ ),
2678
+ Type.Object(
2679
+ { name: Type.String({ minLength: 1 }) },
2680
+ { additionalProperties: true },
2681
+ ),
2682
+ ]),
2683
+ ]),
2684
+ fixtures: {
2685
+ valid: 'request-approval',
2686
+ invalid: {},
2687
+ absent: undefined,
2688
+ unresolved: { expression: 'playRef' },
2689
+ edition1: 'request-approval',
2690
+ },
2691
+ referenceType: 'string | PlayReferenceLike',
2692
+ required: true,
2693
+ resolution: 'runtime-dynamic',
2694
+ issueCode: 'play_authoring_run_play_option_invalid',
2695
+ description:
2696
+ 'Independently published scalar child Play name or strongly typed definition handle.',
2697
+ errorMessage:
2698
+ 'ctx.runPlayAsync playRef must be a non-empty Play name or definition handle.',
2699
+ },
2700
+ 'ctx.runPlayAsync.input': {
2701
+ schema: Type.Record(Type.String(), Type.Unknown()),
2702
+ fixtures: {
2703
+ valid: { draftId: 'draft-1' },
2704
+ invalid: 'draft-1',
2705
+ absent: undefined,
2706
+ unresolved: 'childInput',
2707
+ edition1: {},
2708
+ },
2709
+ referenceType: 'Record<string, unknown>',
2710
+ required: true,
2711
+ resolution: 'runtime-dynamic',
2712
+ issueCode: 'play_authoring_run_play_option_invalid',
2713
+ description: 'Scalar input object submitted to the independent child Run.',
2714
+ errorMessage: 'ctx.runPlayAsync input must be an object.',
2715
+ },
2716
+ 'ctx.runPlayAsync.options.description': {
2717
+ schema: Type.String({ minLength: 1 }),
2718
+ fixtures: {
2719
+ valid: 'Request approval.',
2720
+ invalid: '',
2721
+ absent: undefined,
2722
+ unresolved: { expression: 'description' },
2723
+ edition1: 'Request approval.',
2724
+ },
2725
+ referenceType: 'string',
2726
+ required: true,
2727
+ resolution: 'runtime-dynamic',
2728
+ issueCode: 'play_authoring_run_play_option_invalid',
2729
+ description: 'Non-empty purpose for one independent child Play Run.',
2730
+ errorMessage: 'ctx.runPlayAsync options.description must be non-empty.',
2731
+ },
2603
2732
  'runtime.timeout': {
2604
2733
  schema: Type.String({ pattern: '^\\d+\\s*[mh]$' }),
2605
2734
  fixtures: {
@@ -3049,6 +3178,10 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3049
3178
  `export type CsvOptions = { description?: ${cloudReferenceType('ctx.csv.options.description')}; columns?: ${cloudReferenceType('ctx.csv.options.columns')}; rename?: ${cloudReferenceType('ctx.csv.options.rename')}; required?: ${cloudReferenceType('ctx.csv.options.required')} };`,
3050
3179
  "export type PlayCallExecution = 'inline';",
3051
3180
  `export type PlayCallOptions = { description: ${cloudReferenceType('ctx.runPlay.options.description')}; execution?: ${cloudReferenceType('ctx.runPlay.options.execution')}; timeoutMs?: ${cloudReferenceType('ctx.runPlay.options.timeoutMs')} };`,
3181
+ `export type PlayAsyncCallOptions = { description: ${cloudReferenceType('ctx.runPlayAsync.options.description')} };`,
3182
+ 'declare const PLAY_RUN_ID_BRAND: unique symbol;',
3183
+ "export type PlayRunId = string & { readonly [PLAY_RUN_ID_BRAND]: 'PlayRunId' };",
3184
+ 'export type AsyncPlayRunRef = Readonly<{ runId: PlayRunId }>;',
3052
3185
  `export type RuntimeStepOptions = { semanticKey?: ${cloudReferenceType('ctx.step.semanticKey')}; staleAfterSeconds?: ${cloudReferenceType('ctx.step.staleAfterSeconds')} };`,
3053
3186
  `export type FetchOptions = { staleAfterSeconds?: ${cloudReferenceType('ctx.fetch.staleAfterSeconds')}; transient?: boolean };`,
3054
3187
  'export type PlayFetchResponse = { ok: boolean; status: number; statusText: string; url: string; headers: Record<string, string>; bodyText: string; json: unknown | null };',
@@ -3074,11 +3207,11 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3074
3207
  ' withColumn<Name extends string, Value>(name: Name, definition: DatasetColumnDefinition<OutputRow, Value> & { readonly runIf: (row: OutputRow, index: number) => boolean | Promise<boolean> }): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;',
3075
3208
  ' withColumn<Name extends string, Value>(name: Name, definition: DatasetColumnDefinition<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;',
3076
3209
  ' withColumn<Name extends string, Value>(name: Name, resolver: StepResolver<OutputRow, Value> | RunnableColumnStepProgram<Value>, options: StepOptions<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;',
3077
- ' withColumns<Program extends StepProgram<OutputRow, object, unknown>>(program: Program): DatasetBuilder<InputRow, StepProgramOutput<Program>>;',
3210
+ ' withColumns<ProgramOutput extends OutputRow, Return>(program: StepProgram<OutputRow, ProgramOutput, Return>): DatasetBuilder<InputRow, ProgramOutput>;',
3078
3211
  ' step<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): never;',
3079
3212
  ' run(options?: DatasetRunOptions<InputRow>): Promise<PlayDataset<OutputRow>>;',
3080
3213
  '};',
3081
- 'export type PlayReferenceLike = { readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string };',
3214
+ 'export type PlayReferenceLike<TInput extends Record<string, unknown> = Record<string, unknown>> = ({ readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string }) & { readonly __inputType?: TInput };',
3082
3215
  'export interface DeeplinePlayRuntimeContext {',
3083
3216
  ' csv<T = Record<string, unknown>>(path: string | CsvInput<T & object>, options?: CsvOptions): Promise<PlayDataset<T>>;',
3084
3217
  ' dataset<TSource extends PlayDatasetInput<object>>(key: string, items: TSource): DatasetBuilder<PlayDatasetRow<TSource> & object, PlayDatasetRow<TSource> & object>;',
@@ -3092,6 +3225,8 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
3092
3225
  " fetch(key: string, url: string | URL, init?: RequestInit & { headers?: HeadersInit & { 'Idempotency-Key'?: string; 'X-Idempotency-Key'?: string }; auth?: SecretAuthInput }, options?: FetchOptions): Promise<PlayFetchResponse>;",
3093
3226
  ' secrets: { get(name: string): SecretPromise; bearer(secret: string | SecretPromise | SecretHandle): SecretAuth; header(header: string, secret: string | SecretPromise | SecretHandle): SecretAuth };',
3094
3227
  ` runPlay<TOutput = unknown>(key: string, playRef: ${cloudReferenceType('ctx.runPlay.playRef')}, input: ${cloudReferenceType('ctx.runPlay.input')}, options: PlayCallOptions): Promise<TOutput>;`,
3228
+ ' runPlayAsync<TInput extends Record<string, unknown>>(key: string, playRef: PlayReferenceLike<TInput>, input: NoInfer<TInput>, options: PlayAsyncCallOptions): Promise<AsyncPlayRunRef>;',
3229
+ ' runPlayAsync(key: string, playRef: string, input: Record<string, unknown>, options: PlayAsyncCallOptions): Promise<AsyncPlayRunRef>;',
3095
3230
  " log(message: string, options?: { level?: 'debug' | 'info' | 'warn' | 'error' }): void;",
3096
3231
  ` sleep(ms: ${cloudReferenceType('ctx.sleep.ms')}): Promise<void>;`,
3097
3232
  '}',
@@ -63,7 +63,7 @@ import { PLAY_AUTHORING_CONTRACT_EDITION } from '../authoring-contract';
63
63
  // of the artifact bytes. Do not reuse a local bundle cached before either
64
64
  // compatibility selection or docflow instrumentation entered graph analysis.
65
65
  // Keep this aligned with the app and SDK adapters' cache namespace.
66
- const PLAY_BUNDLE_CACHE_VERSION = 36;
66
+ const PLAY_BUNDLE_CACHE_VERSION = 37;
67
67
  const PLAY_ARTIFACT_CACHE_DIR = join(
68
68
  tmpdir(),
69
69
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`,
@@ -122,7 +122,12 @@ export type PlayLocalFileDiscoveryResult = {
122
122
 
123
123
  export type PlayBundlingAdapter = {
124
124
  projectRoot: string;
125
- /** Optional root used only to make source-graph identity independent of temporary absolute paths. */
125
+ /**
126
+ * Optional root that makes the artifact independent of where it was built:
127
+ * source paths in the graph hash and esbuild's working directory (which it
128
+ * writes into every module-header comment, and so into `artifactHash`) are
129
+ * both taken relative to it instead of to the build's absolute location.
130
+ */
126
131
  sourceIdentityRoot?: string;
127
132
  nodeModulesDir: string;
128
133
  cacheDir?: string;
@@ -2247,6 +2252,7 @@ async function runEsbuildForCjsNode(
2247
2252
  importedPlayDependencies: ImportedPlayDependency[],
2248
2253
  adapter: PlayBundlingAdapter,
2249
2254
  exportName: string,
2255
+ bundleRoot: string,
2250
2256
  ): Promise<EsbuildBundleOutput | string[]> {
2251
2257
  const sdkAliasPlugin = localSdkAliasPlugin(adapter);
2252
2258
  const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
@@ -2269,7 +2275,14 @@ async function runEsbuildForCjsNode(
2269
2275
  },
2270
2276
  }
2271
2277
  : { entryPoints: [entryFile] }),
2272
- absWorkingDir: adapter.projectRoot,
2278
+ // esbuild writes `// <path relative to absWorkingDir>` above every module
2279
+ // it inlines, and artifactHash is sha256 of those bytes. Rooting the build
2280
+ // at the identity root keeps the customer's own files repo-relative, so a
2281
+ // deploy worker building the same commit in a fresh temp directory each
2282
+ // time produces the same bytes. Files outside the root (the SDK on the
2283
+ // worker image) come out as a `..`-prefixed path whose shape depends only
2284
+ // on the image's layout, which is fixed, not on the build's name.
2285
+ absWorkingDir: bundleRoot,
2273
2286
  bundle: true,
2274
2287
  format: 'cjs',
2275
2288
  nodePaths: [adapter.nodeModulesDir],
@@ -2416,12 +2429,14 @@ export async function bundlePlayFile(
2416
2429
  };
2417
2430
  }
2418
2431
 
2432
+ const bundleRoot = resolve(sourceIdentityRoot ?? adapter.projectRoot);
2419
2433
  const buildOutcome = await runEsbuildForCjsNode(
2420
2434
  absolutePath,
2421
2435
  analysis.importPolicy.localFiles,
2422
2436
  analysis.importedPlayDependencies,
2423
2437
  adapter,
2424
2438
  exportName,
2439
+ bundleRoot,
2425
2440
  );
2426
2441
  if (Array.isArray(buildOutcome)) {
2427
2442
  return {
@@ -2434,7 +2449,7 @@ export async function bundlePlayFile(
2434
2449
 
2435
2450
  const normalizedSourceMap = normalizeSourceMapForRuntime(
2436
2451
  sourceMapText,
2437
- resolve(adapter.projectRoot),
2452
+ bundleRoot,
2438
2453
  );
2439
2454
  const virtualBaseName =
2440
2455
  exportName === 'default'