deepline 0.1.214 → 0.1.216

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 (31) hide show
  1. package/dist/bundling-sources/apps/play-runner-workers/src/child-manifest-resolver.ts +108 -0
  2. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +85 -10
  3. package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-runtime-proxy.ts +34 -0
  4. package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +50 -36
  5. package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +12 -0
  6. package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +4 -0
  7. package/dist/bundling-sources/sdk/src/client.ts +1 -2
  8. package/dist/bundling-sources/sdk/src/release.ts +2 -2
  9. package/dist/bundling-sources/sdk/src/types.ts +2 -3
  10. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +21 -3
  11. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +95 -144
  12. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +14 -3
  13. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +37 -2
  14. package/dist/bundling-sources/shared_libs/play-runtime/profiles.ts +22 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +32 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +13 -0
  17. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +5 -2
  18. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +66 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +18 -55
  20. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +59 -27
  21. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +17 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +3 -2
  23. package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +11 -1
  24. package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
  25. package/dist/cli/index.js +72 -21
  26. package/dist/cli/index.mjs +72 -21
  27. package/dist/index.d.mts +2 -3
  28. package/dist/index.d.ts +2 -3
  29. package/dist/index.js +7 -6
  30. package/dist/index.mjs +7 -6
  31. package/package.json +1 -1
@@ -38,7 +38,7 @@ import {
38
38
  PLAY_RUNTIME_CONTRACT,
39
39
  PLAY_RUNTIME_CONTRACT_HEADER,
40
40
  } from '@shared_libs/play-runtime/runtime-contract';
41
- import { PLAY_RUNTIME_API_COMPAT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
41
+ import { PLAY_RUNTIME_API_CURRENT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
42
42
  import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '@shared_libs/play-runtime/test-runtime-seams';
43
43
  import { vercelProtectionBypassHeaders } from '@shared_libs/play-runtime/vercel-protection';
44
44
 
@@ -569,6 +569,19 @@ function summarizeAppRuntimeErrorBody(body: string): string {
569
569
  return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
570
570
  }
571
571
 
572
+ function appRuntimeErrorCode(response: Response, body: string): string | null {
573
+ const header = response.headers.get('x-deepline-error-code')?.trim();
574
+ if (header) return header;
575
+ try {
576
+ const parsed = JSON.parse(body) as { code?: unknown };
577
+ return typeof parsed?.code === 'string' && parsed.code.trim()
578
+ ? parsed.code.trim()
579
+ : null;
580
+ } catch {
581
+ return null;
582
+ }
583
+ }
584
+
572
585
  export function isTransientAppRuntimeFailureBody(body: string): boolean {
573
586
  return /WorkerOverloaded|"\s*code\s*"\s*:\s*"\s*InternalServerError\s*"|Your request couldn't be completed\. Try again later\.|timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT|tuple concurrently updated|RUNTIME_SCHEDULER_SATURATED|Runtime scheduler DB pool is saturated|Runtime scheduler DB circuit breaker is open|OptimisticConcurrencyControlFailure|changed while this mutation was being run|Documents read from or written to the .* table changed while this mutation/i.test(
574
587
  body,
@@ -775,7 +788,7 @@ export class AppRuntimeApiTransportError extends Error {
775
788
 
776
789
  function resolveAppRuntimeApiUrl(context: WorkerRuntimeApiContext): string {
777
790
  const baseUrl = context.baseUrl.trim().replace(/\/$/, '');
778
- return `${baseUrl}${PLAY_RUNTIME_API_COMPAT_PATH}`;
791
+ return `${baseUrl}${PLAY_RUNTIME_API_CURRENT_PATH}`;
779
792
  }
780
793
 
781
794
  async function postAppRuntimeApi<TResponse>(
@@ -892,8 +905,13 @@ async function postAppRuntimeApi<TResponse>(
892
905
  );
893
906
  continue;
894
907
  }
908
+ const code = appRuntimeErrorCode(response, responseText);
909
+ const requestId = response.headers.get('x-deepline-request-id')?.trim();
895
910
  throw new Error(
896
- `App runtime API ${body.action} failed with status ${response.status}: ${summarizeAppRuntimeErrorBody(responseText)}`,
911
+ `App runtime API ${body.action} failed with status ${response.status}` +
912
+ `${code ? ` code=${code}` : ''}` +
913
+ `${requestId ? ` request_id=${requestId}` : ''}: ` +
914
+ summarizeAppRuntimeErrorBody(responseText),
897
915
  );
898
916
  }
899
917
 
@@ -47,10 +47,7 @@ import {
47
47
  RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER,
48
48
  SYNTHETIC_RUN_HEADER,
49
49
  } from './coordinator-headers';
50
- import {
51
- PLAY_RUNTIME_API_COMPAT_PATH,
52
- PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_COMPAT_PATH,
53
- } from './runtime-api-paths';
50
+ import { PLAY_RUNTIME_API_CURRENT_PATH } from './runtime-api-paths';
54
51
  import {
55
52
  createRootRunExecutionScope,
56
53
  deriveChildRunExecutionScope,
@@ -262,6 +259,15 @@ const rowContext = new AsyncLocalStorage<{
262
259
  rowKey?: string;
263
260
  mapScope?: MapExecutionScope;
264
261
  }>();
262
+ type InlineCompositionStore = {
263
+ context: PlayContextImpl;
264
+ executionScope: RunExecutionScope;
265
+ governor: PlayExecutionGovernor;
266
+ playName: string;
267
+ staticPipeline: ContextOptions['staticPipeline'];
268
+ };
269
+ const inlineCompositionContext =
270
+ new AsyncLocalStorage<InlineCompositionStore>();
265
271
  const toolExecutionOverrides = new AsyncLocalStorage<{ force: boolean }>();
266
272
  const PROGRESS_HEARTBEAT_INTERVAL_MS = 1_000;
267
273
  const PURE_JS_HEARTBEAT_ROW_INTERVAL = 250;
@@ -1270,12 +1276,6 @@ export class PlayContextImpl {
1270
1276
  */
1271
1277
  private readonly governor: PlayExecutionGovernor;
1272
1278
  private readonly resourceGovernor: RuntimeResourceGovernor;
1273
- /**
1274
- * Lineage identity for this context (run/play ids + ancestry). Seeded from the
1275
- * Governor snapshot; used only for receipt keys, durable-boundary scoping, and
1276
- * child run-id derivation. All policy lives in {@link governor}.
1277
- */
1278
- private readonly governance: GovernanceSnapshot;
1279
1279
  private readonly resolvedPlayExecutorCache = new Map<
1280
1280
  string,
1281
1281
  Promise<ResolvedPlayExecutor>
@@ -1443,7 +1443,6 @@ export class PlayContextImpl {
1443
1443
  this.resourceGovernor = createRuntimeResourceGovernor({
1444
1444
  executionGovernor: this.governor,
1445
1445
  });
1446
- this.governance = this.governor.snapshot();
1447
1446
  }
1448
1447
 
1449
1448
  private durableBoundaryId(localId: string): string {
@@ -1451,7 +1450,33 @@ export class PlayContextImpl {
1451
1450
  // Nested plays and concurrent child calls therefore need a stable run scope
1452
1451
  // in the key, otherwise two children can both produce e.g. "sleep-0-25"
1453
1452
  // and replay the wrong boundary or never observe completion.
1454
- return `${this.governance.currentRunId}:${localId}`;
1453
+ return `${this.currentGovernance.currentRunId}:${localId}`;
1454
+ }
1455
+
1456
+ private get activeInlineComposition(): InlineCompositionStore | null {
1457
+ const active = inlineCompositionContext.getStore();
1458
+ return active?.context === this ? active : null;
1459
+ }
1460
+
1461
+ private get currentExecutionScope(): RunExecutionScope {
1462
+ return this.activeInlineComposition?.executionScope ?? this.executionScope;
1463
+ }
1464
+
1465
+ private get currentExecutionGovernor(): PlayExecutionGovernor {
1466
+ return this.activeInlineComposition?.governor ?? this.governor;
1467
+ }
1468
+
1469
+ private get currentGovernance(): GovernanceSnapshot {
1470
+ return this.currentExecutionGovernor.snapshot();
1471
+ }
1472
+
1473
+ private get currentPlayName(): string | undefined {
1474
+ return this.activeInlineComposition?.playName ?? this.#options.playName;
1475
+ }
1476
+
1477
+ private get currentStaticPipeline(): ContextOptions['staticPipeline'] {
1478
+ const active = this.activeInlineComposition;
1479
+ return active ? active.staticPipeline : this.#options.staticPipeline;
1455
1480
  }
1456
1481
 
1457
1482
  private shouldLaunchChildPlaysThroughScheduler(): boolean {
@@ -1617,17 +1642,18 @@ export class PlayContextImpl {
1617
1642
  childInput: Record<string, unknown>;
1618
1643
  description?: string | null;
1619
1644
  }): Promise<void> {
1645
+ const parentGovernance = this.currentGovernance;
1620
1646
  const parentPlayName =
1621
- this.governance.currentPlayId ||
1622
- this.#options.playName ||
1647
+ parentGovernance.currentPlayId ||
1648
+ this.currentPlayName ||
1623
1649
  this.#options.playId ||
1624
1650
  'anonymous-play';
1625
1651
  const governance: PlayCallGovernanceSnapshot = {
1626
- rootRunId: this.governance.rootRunId,
1627
- parentRunId: this.governance.currentRunId,
1652
+ rootRunId: parentGovernance.rootRunId,
1653
+ parentRunId: parentGovernance.currentRunId,
1628
1654
  parentPlayName,
1629
1655
  key: input.key,
1630
- ancestryPlayIds: [...this.governance.ancestryPlayIds],
1656
+ ancestryPlayIds: [...parentGovernance.ancestryPlayIds],
1631
1657
  callDepth: input.childGovernance.callDepth,
1632
1658
  playCallCount: input.childGovernance.playCallCount,
1633
1659
  toolCallCount: input.childGovernance.toolCallCount,
@@ -1702,13 +1728,13 @@ export class PlayContextImpl {
1702
1728
  | null
1703
1729
  > {
1704
1730
  const response = await fetch(
1705
- `${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_COMPAT_PATH}`,
1731
+ `${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_CURRENT_PATH}`,
1706
1732
  {
1707
1733
  method: 'POST',
1708
1734
  headers: await this.runtimeApiHeaders(),
1709
1735
  body: JSON.stringify({
1710
1736
  action: 'read_child_run_terminal_snapshot',
1711
- parentRunId: this.governance.currentRunId,
1737
+ parentRunId: this.currentGovernance.currentRunId,
1712
1738
  childRunId: input.childRunId,
1713
1739
  childPlayName: input.childPlayName,
1714
1740
  }),
@@ -1817,7 +1843,7 @@ export class PlayContextImpl {
1817
1843
  this.#options.runId
1818
1844
  ) {
1819
1845
  const response = await fetch(
1820
- `${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`,
1846
+ `${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`,
1821
1847
  {
1822
1848
  method: 'POST',
1823
1849
  headers: {
@@ -2716,7 +2742,7 @@ export class PlayContextImpl {
2716
2742
  }): Promise<string> {
2717
2743
  return await this.durableToolCallCacheKeyForScope({
2718
2744
  ...input,
2719
- playLocalScope: this.governance.currentPlayId,
2745
+ playLocalScope: this.currentGovernance.currentPlayId,
2720
2746
  });
2721
2747
  }
2722
2748
 
@@ -2871,7 +2897,7 @@ export class PlayContextImpl {
2871
2897
  opts.receiptKey?.trim() ||
2872
2898
  durableCtxKey({
2873
2899
  orgId: this.#options.orgId,
2874
- playId: this.executionScope.receipt.namespace,
2900
+ playId: this.currentExecutionScope.receipt.namespace,
2875
2901
  operation,
2876
2902
  id,
2877
2903
  semanticKey: opts.semanticKey,
@@ -2905,15 +2931,15 @@ export class PlayContextImpl {
2905
2931
  }
2906
2932
 
2907
2933
  private get currentRunId(): string {
2908
- return this.executionScope.logical.runId;
2934
+ return this.currentExecutionScope.logical.runId;
2909
2935
  }
2910
2936
 
2911
2937
  private get currentReceiptOwnerRunId(): string {
2912
- return this.executionScope.receipt.ownerRunId;
2938
+ return this.currentExecutionScope.receipt.ownerRunId;
2913
2939
  }
2914
2940
 
2915
2941
  private get currentRunAttempt(): number {
2916
- return this.executionScope.receipt.ownerAttempt;
2942
+ return this.currentExecutionScope.receipt.ownerAttempt;
2917
2943
  }
2918
2944
 
2919
2945
  private emitScopedFieldMetaUpdate(input: {
@@ -3193,7 +3219,8 @@ export class PlayContextImpl {
3193
3219
  rowKey?: string;
3194
3220
  callId?: string;
3195
3221
  }): string {
3196
- const scope = this.governance.currentRunId || this.#options.runId || 'run';
3222
+ const scope =
3223
+ this.currentGovernance.currentRunId || this.#options.runId || 'run';
3197
3224
  if (input.callId?.trim()) {
3198
3225
  return `${scope}:${input.callId.trim()}`;
3199
3226
  }
@@ -3961,16 +3988,16 @@ export class PlayContextImpl {
3961
3988
  const flushStartedAt = Date.now();
3962
3989
  try {
3963
3990
  await this.#options.onMapRowsCompleted!({
3964
- playName: this.#options.playName,
3965
- playId: this.#options.playId,
3966
- runId: this.#options.runId,
3991
+ playName: this.currentPlayName,
3992
+ playId: this.currentExecutionScope.logical.playId,
3993
+ runId: this.currentRunId,
3967
3994
  executorToken: this.#options.executorToken,
3968
3995
  tableNamespace: resolvedTableNamespace,
3969
3996
  rows: chunk,
3970
3997
  outputFields: datasetColumnNames.filter((field) =>
3971
3998
  shouldPersistMapCellField(field),
3972
3999
  ),
3973
- staticPipeline: this.#options.staticPipeline ?? null,
4000
+ staticPipeline: this.currentStaticPipeline ?? null,
3974
4001
  });
3975
4002
  this.resourceGovernor.observe({
3976
4003
  sheetFlushBytes: chunkBytes,
@@ -4037,11 +4064,11 @@ export class PlayContextImpl {
4037
4064
  pageStartRows,
4038
4065
  resolvedTableNamespace,
4039
4066
  {
4040
- playName: this.#options.playName,
4041
- playId: this.#options.playId,
4042
- runId: this.#options.runId,
4067
+ playName: this.currentPlayName,
4068
+ playId: this.currentExecutionScope.logical.playId,
4069
+ runId: this.currentRunId,
4043
4070
  executorToken: this.#options.executorToken,
4044
- staticPipeline: this.#options.staticPipeline,
4071
+ staticPipeline: this.currentStaticPipeline,
4045
4072
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4046
4073
  inputOffset: page.offset,
4047
4074
  },
@@ -4472,11 +4499,11 @@ export class PlayContextImpl {
4472
4499
  mapStartRows,
4473
4500
  resolvedTableNamespace,
4474
4501
  {
4475
- playName: this.#options.playName,
4476
- playId: this.#options.playId,
4477
- runId: this.#options.runId,
4502
+ playName: this.currentPlayName,
4503
+ playId: this.currentExecutionScope.logical.playId,
4504
+ runId: this.currentRunId,
4478
4505
  executorToken: this.#options.executorToken,
4479
- staticPipeline: this.#options.staticPipeline,
4506
+ staticPipeline: this.currentStaticPipeline,
4480
4507
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4481
4508
  },
4482
4509
  );
@@ -4620,16 +4647,16 @@ export class PlayContextImpl {
4620
4647
  const flushStartedAt = Date.now();
4621
4648
  try {
4622
4649
  const writeResult = await this.#options.onMapRowsCompleted!({
4623
- playName: this.#options.playName,
4624
- playId: this.#options.playId,
4625
- runId: this.#options.runId,
4650
+ playName: this.currentPlayName,
4651
+ playId: this.currentExecutionScope.logical.playId,
4652
+ runId: this.currentRunId,
4626
4653
  executorToken: this.#options.executorToken,
4627
4654
  tableNamespace: resolvedTableNamespace,
4628
4655
  rows: chunk,
4629
4656
  outputFields: datasetColumnNames.filter((field) =>
4630
4657
  shouldPersistMapCellField(field),
4631
4658
  ),
4632
- staticPipeline: this.#options.staticPipeline ?? null,
4659
+ staticPipeline: this.currentStaticPipeline ?? null,
4633
4660
  });
4634
4661
  if (writeResult) {
4635
4662
  for (const key of writeResult.staleDroppedKeys ?? []) {
@@ -6158,6 +6185,7 @@ export class PlayContextImpl {
6158
6185
  input: Record<string, unknown>,
6159
6186
  options?: ToolCallOptions,
6160
6187
  ): Promise<unknown> {
6188
+ const executionScope = this.currentExecutionScope;
6161
6189
  const normalizedKey = this.normalizeContextKey(key, 'tool');
6162
6190
  const toolCachePolicy = this.effectiveToolCallCachePolicy(options);
6163
6191
  const toolRequestIdentity = deriveToolRequestIdentity({
@@ -6303,6 +6331,7 @@ export class PlayContextImpl {
6303
6331
  const rowId = store.rowId;
6304
6332
  const fieldName = store.fieldName;
6305
6333
  const callId = [
6334
+ executionScope.receipt.namespace,
6306
6335
  store.tableNamespace?.trim() || 'map',
6307
6336
  store.rowKey?.trim() || String(rowId),
6308
6337
  normalizedKey,
@@ -6669,10 +6698,10 @@ export class PlayContextImpl {
6669
6698
  });
6670
6699
 
6671
6700
  const executePlayCall = async (): Promise<TOutput> => {
6672
- // Reserve depth + per-parent + descendant/play-call budget on the parent
6673
- // and fork the lineage-global snapshot for the child. forkChild owns the
6674
- // cycle guard, depth cap, per-parent cap, and play/descendant charges, so
6675
- // a recovered receipt (which skips this body) never consumes budget.
6701
+ // Scheduled children reserve the parent's durable play/descendant
6702
+ // budgets. Inline scalar children are function composition: their
6703
+ // Governor view changes only local lineage for recursion/depth checks
6704
+ // and delegates tool admission, pacing, and tool budgets to the parent.
6676
6705
  //
6677
6706
  // The SCHEDULED substrate still allocates a durable child run id (through
6678
6707
  // the shared canonical builder so in-process and the workers_edge
@@ -6682,9 +6711,10 @@ export class PlayContextImpl {
6682
6711
  // settlement. Their only identity is a replay-stable, call-site scoped
6683
6712
  // composition namespace (`child:<playName>#<ordinal>`) used to scope tool
6684
6713
  // receipts under the PARENT run. See child-composition-namespace below.
6714
+ const parentGovernor = this.currentExecutionGovernor;
6685
6715
  const inlineChildGovernor = launchThroughScheduler
6686
6716
  ? null
6687
- : await this.governor.forkInlineChild({
6717
+ : await parentGovernor.forkInlineChild({
6688
6718
  childPlayName: resolvedName,
6689
6719
  childRunId: this.inlineChildCompositionNamespace(
6690
6720
  resolvedName,
@@ -6692,7 +6722,7 @@ export class PlayContextImpl {
6692
6722
  ),
6693
6723
  });
6694
6724
  const childGovernance = launchThroughScheduler
6695
- ? await this.governor.forkChild({
6725
+ ? await parentGovernor.forkChild({
6696
6726
  childPlayName: resolvedName,
6697
6727
  childRunId: buildChildRunId({
6698
6728
  childPlayName: resolvedName,
@@ -6706,7 +6736,7 @@ export class PlayContextImpl {
6706
6736
  })
6707
6737
  : inlineChildGovernor!.snapshot();
6708
6738
  const childPlaySlot = launchThroughScheduler
6709
- ? await this.governor.acquireChildSubmitSlot()
6739
+ ? await parentGovernor.acquireChildSubmitSlot()
6710
6740
  : null;
6711
6741
  const rowStore = rowContext.getStore();
6712
6742
  const producer = {
@@ -6840,7 +6870,7 @@ export class PlayContextImpl {
6840
6870
  // per-row inline-invocation sanity cap at creation).
6841
6871
  const compositionNamespace = childGovernance.currentRunId;
6842
6872
  const childExecutionScope = deriveChildRunExecutionScope(
6843
- this.executionScope,
6873
+ this.currentExecutionScope,
6844
6874
  {
6845
6875
  placement: 'inline',
6846
6876
  runId: compositionNamespace,
@@ -6849,49 +6879,22 @@ export class PlayContextImpl {
6849
6879
  },
6850
6880
  );
6851
6881
  this.inlineChildAggregates.total += 1;
6852
- const inlineScalarChild =
6853
- childExecutionDecision.reason === 'scalar_child';
6854
- const childContext = createPlayContext({
6855
- ...this.#options,
6856
- executionScope: childExecutionScope,
6857
- playId: resolvedName,
6858
- playName: resolvedName,
6859
- runId: compositionNamespace,
6860
- executorToken: inlineScalarChild
6861
- ? this.#options.executorToken
6862
- : await this.mintChildExecutorToken({
6863
- parentRunId: this.currentRunId,
6864
- parentPlayName: this.#options.playName,
6865
- childRunId: compositionNamespace,
6866
- childPlayName: resolvedName,
6867
- }),
6868
- staticPipeline: resolvedPlay.staticPipeline ?? null,
6869
- checkpoint: this.checkpoint,
6870
- governance: undefined,
6871
- executionGovernor: inlineChildGovernor!,
6872
- ...(inlineScalarChild
6873
- ? {}
6874
- : {
6875
- getRuntimeStepReceipt: undefined,
6876
- getRuntimeStepReceipts: undefined,
6877
- claimRuntimeStepReceipt: undefined,
6878
- claimRuntimeStepReceipts: undefined,
6879
- completeRuntimeStepReceipt: undefined,
6880
- completeRuntimeStepReceipts: undefined,
6881
- failRuntimeStepReceipt: undefined,
6882
- failRuntimeStepReceipts: undefined,
6883
- heartbeatRuntimeStepReceipts: undefined,
6884
- skipRuntimeStepReceipt: undefined,
6885
- }),
6886
- });
6887
6882
  try {
6888
- const childExecution = this.executeResolvedPlay(
6889
- resolvedPlay,
6890
- childContext,
6891
- input,
6883
+ // A scalar child is a normal function call on the parent's context.
6884
+ // Async-local identity changes receipt and recursion scope without
6885
+ // allocating another PlayContext, tool queue, drain loop, resource
6886
+ // governor, or receipt client. Calls from every concurrent child
6887
+ // therefore reach the same proven parent batching path.
6888
+ const result = await inlineCompositionContext.run(
6889
+ {
6890
+ context: this,
6891
+ executionScope: childExecutionScope,
6892
+ governor: inlineChildGovernor!,
6893
+ playName: resolvedName,
6894
+ staticPipeline: resolvedPlay.staticPipeline ?? null,
6895
+ },
6896
+ () => this.executeResolvedPlay(resolvedPlay, this, input),
6892
6897
  );
6893
- await childContext.drainQueuedWork([childExecution]);
6894
- const result = await childExecution;
6895
6898
  this.inlineChildAggregates.ok += 1;
6896
6899
  if (rowStore) {
6897
6900
  this.emitScopedFieldMetaUpdate({
@@ -6912,7 +6915,6 @@ export class PlayContextImpl {
6912
6915
  playId: resolvedName,
6913
6916
  execution: options?.execution,
6914
6917
  description: options?.description,
6915
- nestedSteps: childContext.getSteps(),
6916
6918
  });
6917
6919
  return result as TOutput;
6918
6920
  } catch (childError) {
@@ -6950,57 +6952,6 @@ export class PlayContextImpl {
6950
6952
  }
6951
6953
  }
6952
6954
 
6953
- private async mintChildExecutorToken(input: {
6954
- parentRunId: string;
6955
- parentPlayName?: string | null;
6956
- childRunId: string;
6957
- childPlayName: string;
6958
- }): Promise<string | undefined> {
6959
- if (!this.#options.executorToken || !this.#options.baseUrl) {
6960
- return this.#options.executorToken;
6961
- }
6962
- const parentPlayName = input.parentPlayName?.trim();
6963
- if (!parentPlayName) {
6964
- return this.#options.executorToken;
6965
- }
6966
- const response = await fetch(
6967
- `${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_COMPAT_PATH}`,
6968
- {
6969
- method: 'POST',
6970
- headers: {
6971
- Authorization: `Bearer ${this.#options.executorToken}`,
6972
- 'Content-Type': 'application/json',
6973
- ...(await this.vercelProtectionHeaders()),
6974
- },
6975
- body: JSON.stringify({
6976
- parentRunId: input.parentRunId,
6977
- parentPlayName,
6978
- childRunId: input.childRunId,
6979
- childPlayName: input.childPlayName,
6980
- }),
6981
- },
6982
- );
6983
- const body = (await response.json().catch(() => null)) as {
6984
- executorToken?: unknown;
6985
- error?: unknown;
6986
- } | null;
6987
- if (!response.ok) {
6988
- const message =
6989
- typeof body?.error === 'string' && body.error.trim()
6990
- ? body.error.trim()
6991
- : response.statusText;
6992
- throw new Error(
6993
- `ctx.runPlay failed to mint child executor token for "${input.childPlayName}": ${message} (status ${response.status}).`,
6994
- );
6995
- }
6996
- if (typeof body?.executorToken !== 'string' || !body.executorToken.trim()) {
6997
- throw new Error(
6998
- `ctx.runPlay failed to mint child executor token for "${input.childPlayName}": response did not include executorToken.`,
6999
- );
7000
- }
7001
- return body.executorToken.trim();
7002
- }
7003
-
7004
6955
  /**
7005
6956
  * Replay-stable composition namespace for one inline `ctx.runPlay`
7006
6957
  * invocation. Format `child:<playName>#<callKey>[@<rowScope>]`:
@@ -7358,7 +7309,7 @@ export class PlayContextImpl {
7358
7309
 
7359
7310
  const rowStore = rowContext.getStore();
7360
7311
  const scope = rowStore ? `row-${rowStore.rowId}` : 'workflow';
7361
- const callIndexKey = `${scope}:${normalizedKey}`;
7312
+ const callIndexKey = `${this.currentExecutionScope.receipt.namespace}:${scope}:${normalizedKey}`;
7362
7313
  const callIndex = this.stepCallIndexByKey.get(callIndexKey) ?? 0;
7363
7314
  this.stepCallIndexByKey.set(callIndexKey, callIndex + 1);
7364
7315
  const boundarySuffix = callIndex === 0 ? '' : `:${callIndex}`;
@@ -429,7 +429,11 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
429
429
  receipt: RuntimeStepReceipt,
430
430
  source: DurableReceiptRecoverySource = 'cache',
431
431
  ): Promise<T> => {
432
- input.log(`ctx.${input.operation}(${input.id}): reused completed work`);
432
+ input.log(
433
+ source === 'owner'
434
+ ? `ctx.${input.operation}(${input.id}): executed and converged through immutable publication`
435
+ : `ctx.${input.operation}(${input.id}): reused completed work`,
436
+ );
433
437
  if (receipt.output === undefined) {
434
438
  return receipt.output as T;
435
439
  }
@@ -489,8 +493,7 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
489
493
  const afterLock = await input.store.get(input.receiptKey);
490
494
  if (
491
495
  input.force !== true &&
492
- (afterLock?.status === 'completed' ||
493
- afterLock?.status === 'skipped')
496
+ (afterLock?.status === 'completed' || afterLock?.status === 'skipped')
494
497
  ) {
495
498
  await input.store.releaseExecutionLock({
496
499
  receiptKey: input.receiptKey,
@@ -566,6 +569,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
566
569
  completed?.status === 'completed' ||
567
570
  completed?.status === 'skipped'
568
571
  ) {
572
+ // Completion is a compact acknowledgement. This execution already
573
+ // owns the just-produced value; only a later receipt read needs the
574
+ // persisted payload for replay.
575
+ if (completed.output === undefined) return result;
569
576
  return await recoverCompletedReceipt(completed, 'owner');
570
577
  }
571
578
  const winner = await input.store.get(input.receiptKey);
@@ -828,6 +835,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
828
835
  (completed.status === 'completed' || completed.status === 'skipped')
829
836
  ) {
830
837
  logPhase('total', lifecycleStartedAt);
838
+ // Completion endpoints acknowledge ownership and status only. The caller
839
+ // already owns the live value; replay reads are the only path that needs
840
+ // the receipt payload returned over the transport.
841
+ if (completed.output === undefined) return result;
831
842
  return await recoverCompletedReceipt(completed, 'owner');
832
843
  }
833
844
  if (input.store.canPersistCompletion) {
@@ -540,7 +540,7 @@ export function createPlayExecutionGovernor(
540
540
  async forkInlineChild(childInput) {
541
541
  return createInlineChildGovernor(
542
542
  governor,
543
- await governor.forkChild(childInput),
543
+ deriveInlineChildSnapshot(state, childInput, policy),
544
544
  );
545
545
  },
546
546
 
@@ -593,6 +593,38 @@ export function createPlayExecutionGovernor(
593
593
  return governor;
594
594
  }
595
595
 
596
+ function deriveInlineChildSnapshot(
597
+ parent: GovernanceSnapshot,
598
+ input: { childPlayName: string; childRunId: string },
599
+ policy: ResolvedExecutionPolicy,
600
+ ): GovernanceSnapshot {
601
+ if (parent.ancestryPlayIds.includes(input.childPlayName)) {
602
+ throw new Error(
603
+ `Recursive play graph detected: ${[
604
+ ...parent.ancestryPlayIds,
605
+ input.childPlayName,
606
+ ].join(' -> ')}`,
607
+ );
608
+ }
609
+ const nextDepth = parent.callDepth + 1;
610
+ if (nextDepth > policy.budgets.maxPlayCallDepth) {
611
+ throw new GovernorBudgetError(
612
+ 'playDepth',
613
+ nextDepth,
614
+ policy.budgets.maxPlayCallDepth,
615
+ );
616
+ }
617
+ return {
618
+ ...parent,
619
+ currentRunId: input.childRunId,
620
+ currentPlayId: input.childPlayName,
621
+ ancestryPlayIds: [...parent.ancestryPlayIds, input.childPlayName],
622
+ ancestryRunIds: [...parent.ancestryRunIds, input.childRunId],
623
+ callDepth: nextDepth,
624
+ parentChildCalls: {},
625
+ };
626
+ }
627
+
596
628
  function createInlineChildGovernor(
597
629
  root: PlayExecutionGovernor,
598
630
  initial: GovernanceSnapshot,
@@ -667,7 +699,10 @@ function createInlineChildGovernor(
667
699
  };
668
700
  },
669
701
  async forkInlineChild(input) {
670
- return createInlineChildGovernor(child, await child.forkChild(input));
702
+ return createInlineChildGovernor(
703
+ child,
704
+ deriveInlineChildSnapshot(child.snapshot(), input, child.policy),
705
+ );
671
706
  },
672
707
  snapshot() {
673
708
  const counters = root.snapshot();
@@ -6,6 +6,7 @@ import {
6
6
  PLAY_RUNTIME_PROVIDERS,
7
7
  PLAY_RUNTIME_PROVIDER_IDS,
8
8
  defaultPlayRuntimeProvider,
9
+ resolveEnabledPlayRuntimeProvider,
9
10
  resolvePlayRuntimeProvider,
10
11
  type PlayRuntimeProviderId,
11
12
  } from './providers';
@@ -50,3 +51,24 @@ export function resolveExecutionProfile(
50
51
  throw error;
51
52
  }
52
53
  }
54
+
55
+ export function resolveEnabledExecutionProfile(
56
+ override?: string | null,
57
+ ): PlayExecutionProfile {
58
+ try {
59
+ return resolveEnabledPlayRuntimeProvider(override);
60
+ } catch (error) {
61
+ if (
62
+ override?.trim() &&
63
+ error instanceof Error &&
64
+ /Unsupported play runtime provider/.test(error.message)
65
+ ) {
66
+ throw new Error(
67
+ `Unknown execution profile "${override.trim()}". Expected one of: ${Object.keys(
68
+ PLAY_EXECUTION_PROFILES,
69
+ ).join(', ')}.`,
70
+ );
71
+ }
72
+ throw error;
73
+ }
74
+ }