deepline 0.1.215 → 0.1.217

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.
@@ -100,6 +100,7 @@ export function createRunScopedChildManifestResolver(
100
100
  if (!state) {
101
101
  state = createResolutionState(input.preloaded);
102
102
  runResolutionStates.set(input.scopeKey, state);
103
+ pruneRunResolutionStates(now);
103
104
  }
104
105
  state.lastAccessedAt = now;
105
106
 
@@ -57,13 +57,9 @@ import {
57
57
  PLAY_RUNTIME_CONTRACT_HEADER,
58
58
  } from '../../../shared_libs/play-runtime/runtime-contract';
59
59
  import {
60
- PLAY_RUNTIME_API_CURRENT_PATH,
61
- PLAY_RUNTIME_TAIL_LOG_CURRENT_PATH,
60
+ PLAY_RUNTIME_API_COMPAT_PATH,
61
+ PLAY_RUNTIME_TAIL_LOG_COMPAT_PATH,
62
62
  } from '../../../shared_libs/play-runtime/runtime-api-paths';
63
- import {
64
- executorTokenFromAuthorizationHeader,
65
- isCoordinatorRuntimeProxyPath,
66
- } from './coordinator-runtime-proxy';
67
63
  import {
68
64
  decideWorkflowPlatformRetry,
69
65
  PLATFORM_DEPLOY_WORKFLOW_RETRY_LIMIT,
@@ -1335,7 +1331,7 @@ async function markWorkflowRuntimeFailure(input: {
1335
1331
  } satisfies PlayRunLedgerEvent,
1336
1332
  ],
1337
1333
  });
1338
- const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`;
1334
+ const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`;
1339
1335
  const backoffMs = [200, 500, 1500];
1340
1336
  let lastError: unknown = null;
1341
1337
  for (let attempt = 0; attempt <= backoffMs.length; attempt += 1) {
@@ -1424,7 +1420,7 @@ async function appendWorkflowChildCompletedRunEvent(input: {
1424
1420
  } satisfies PlayRunLedgerEvent,
1425
1421
  ],
1426
1422
  });
1427
- const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`;
1423
+ const url = `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`;
1428
1424
  const response = await fetch(url, { method: 'POST', headers, body });
1429
1425
  if (!response.ok) {
1430
1426
  throw new Error(
@@ -1844,7 +1840,7 @@ async function markRegisteredChildRunFailed(input: {
1844
1840
  contract: PLAY_RUNTIME_CONTRACT,
1845
1841
  executorToken: input.childExecutorToken,
1846
1842
  baseUrl: input.baseUrl,
1847
- path: PLAY_RUNTIME_API_CURRENT_PATH,
1843
+ path: PLAY_RUNTIME_API_COMPAT_PATH,
1848
1844
  headers: {
1849
1845
  'x-deepline-request-id': crypto.randomUUID(),
1850
1846
  [PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
@@ -1901,7 +1897,7 @@ async function appendRegisteredChildRunTerminal(input: {
1901
1897
  contract: PLAY_RUNTIME_CONTRACT,
1902
1898
  executorToken: input.childExecutorToken,
1903
1899
  baseUrl: input.baseUrl,
1904
- path: PLAY_RUNTIME_API_CURRENT_PATH,
1900
+ path: PLAY_RUNTIME_API_COMPAT_PATH,
1905
1901
  headers: {
1906
1902
  'x-deepline-request-id': crypto.randomUUID(),
1907
1903
  [PLAY_RUNTIME_CONTRACT_HEADER]: String(PLAY_RUNTIME_CONTRACT),
@@ -1983,7 +1979,7 @@ async function callRuntimeApiFromCoordinator(input: {
1983
1979
  contract: PLAY_RUNTIME_CONTRACT,
1984
1980
  executorToken: input.executorToken,
1985
1981
  baseUrl: input.baseUrl,
1986
- path: PLAY_RUNTIME_API_CURRENT_PATH,
1982
+ path: PLAY_RUNTIME_API_COMPAT_PATH,
1987
1983
  body,
1988
1984
  headers: {
1989
1985
  'x-deepline-request-id': requestId,
@@ -3122,8 +3118,7 @@ async function submitChildWorkflowThroughCoordinator(input: {
3122
3118
  * Workflows serializes the dynamic Worker's `env` map when it persists
3123
3119
  * workflow state. WorkerEntrypoint stubs ARE cloneable.
3124
3120
  *
3125
- * Path allowlist: the current runtime endpoint plus `/api/v2/plays/*` and
3126
- * `/api/v2/integrations/*`.
3121
+ * Path allowlist: only `/api/v2/plays/*` and `/api/v2/integrations/*`.
3127
3122
  * Anything else is a sandbox-escape attempt and gets a loud 403 — the
3128
3123
  * coordinator must NOT proxy a play's request to internal admin routes
3129
3124
  * even if the play tries to construct such a URL.
@@ -3132,7 +3127,6 @@ export class RuntimeApi extends WorkerEntrypoint<CoordinatorEnv, undefined> {
3132
3127
  async fetch(request: Request): Promise<Response> {
3133
3128
  const incoming = new URL(request.url);
3134
3129
  const allowed =
3135
- incoming.pathname === PLAY_RUNTIME_API_CURRENT_PATH ||
3136
3130
  incoming.pathname.startsWith('/api/v2/plays/') ||
3137
3131
  incoming.pathname.startsWith('/api/v2/integrations/');
3138
3132
  if (!allowed) {
@@ -3765,62 +3759,6 @@ const coordinatorEntrypoint = {
3765
3759
 
3766
3760
  export default coordinatorEntrypoint;
3767
3761
 
3768
- const COORDINATOR_RUNTIME_PROXY_MAX_BODY_BYTES = 16 * 1024 * 1024;
3769
-
3770
- async function proxyCoordinatorRuntimeApiRequest(input: {
3771
- request: Request;
3772
- env: CoordinatorEnv;
3773
- url: URL;
3774
- }): Promise<Response> {
3775
- const { request, env, url } = input;
3776
- if (request.method !== 'GET' && request.method !== 'POST') {
3777
- return new Response('method not allowed', { status: 405 });
3778
- }
3779
- const executorToken = executorTokenFromAuthorizationHeader(
3780
- request.headers.get('authorization'),
3781
- );
3782
- if (!executorToken) {
3783
- return Response.json(
3784
- { error: 'Runtime gateway requires a bearer executor token.' },
3785
- { status: 401 },
3786
- );
3787
- }
3788
- const contentLength = Number(request.headers.get('content-length') ?? '0');
3789
- if (
3790
- Number.isFinite(contentLength) &&
3791
- contentLength > COORDINATOR_RUNTIME_PROXY_MAX_BODY_BYTES
3792
- ) {
3793
- return Response.json(
3794
- { error: 'Runtime gateway request body exceeds the 16 MiB limit.' },
3795
- { status: 413 },
3796
- );
3797
- }
3798
- let body: unknown = {};
3799
- if (request.method === 'POST') {
3800
- try {
3801
- body = await request.json();
3802
- } catch {
3803
- return Response.json(
3804
- { error: 'Runtime gateway requires a JSON request body.' },
3805
- { status: 400 },
3806
- );
3807
- }
3808
- }
3809
- const upstream = await env.HARNESS.runtimeApiCall({
3810
- contract: PLAY_RUNTIME_CONTRACT,
3811
- executorToken,
3812
- method: request.method,
3813
- path: `${url.pathname}${url.search}`,
3814
- body,
3815
- headers: Object.fromEntries(request.headers.entries()),
3816
- });
3817
- const headers = new Headers(upstream.headers);
3818
- // The harness materializes the upstream response body as a string. Let this
3819
- // Response calculate its own byte length instead of forwarding a stale one.
3820
- headers.delete('content-length');
3821
- return new Response(upstream.body, { status: upstream.status, headers });
3822
- }
3823
-
3824
3762
  /**
3825
3763
  * Route dispatcher for the coordinator fetch handler. Extracted as a standalone
3826
3764
  * function so the outer {@link coordinatorEntrypoint.fetch} try/catch backstop
@@ -3849,19 +3787,6 @@ async function coordinatorRouteFetch(
3849
3787
  }
3850
3788
  return new Response('ok', { status: 200, headers });
3851
3789
  }
3852
- if (isCoordinatorRuntimeProxyPath(url.pathname)) {
3853
- try {
3854
- return await proxyCoordinatorRuntimeApiRequest({ request, env, url });
3855
- } catch (error) {
3856
- return coordinatorRouteErrorResponse({
3857
- logTag: '[coordinator.runtime_proxy.error]',
3858
- code: 'COORDINATOR_RUNTIME_PROXY_FAILED',
3859
- phase: 'coordinator.runtime_proxy',
3860
- runId: null,
3861
- error,
3862
- });
3863
- }
3864
- }
3865
3790
  if (url.pathname === '/internal-token/probe') {
3866
3791
  const authError = authorizeCoordinatorControlRequest({ request, env });
3867
3792
  if (authError) return authError;
@@ -4110,7 +4035,7 @@ async function flushTailRunLogs(
4110
4035
  return;
4111
4036
  }
4112
4037
  await fetch(
4113
- `${env.DEEPLINE_API_BASE_URL}${PLAY_RUNTIME_TAIL_LOG_CURRENT_PATH}`,
4038
+ `${env.DEEPLINE_API_BASE_URL}${PLAY_RUNTIME_TAIL_LOG_COMPAT_PATH}`,
4114
4039
  {
4115
4040
  method: 'POST',
4116
4041
  headers: {
@@ -5530,7 +5455,7 @@ export class TenantWorkflow extends WorkflowEntrypoint {
5530
5455
  const runId = payload && typeof payload.runId === "string" ? payload.runId : "warmup";
5531
5456
  const startedAt = Date.now();
5532
5457
  if (this.env.RUNTIME_API) {
5533
- await this.env.RUNTIME_API.fetch(new Request("https://deepline.runtime.internal${PLAY_RUNTIME_API_CURRENT_PATH}", {
5458
+ await this.env.RUNTIME_API.fetch(new Request("https://deepline.runtime.internal${PLAY_RUNTIME_API_COMPAT_PATH}", {
5534
5459
  method: "POST",
5535
5460
  headers: { "content-type": "application/json" },
5536
5461
  body: "{}"
@@ -185,8 +185,8 @@ import {
185
185
  PLAY_RUNTIME_CONTRACT_HEADER,
186
186
  } from '../../../shared_libs/play-runtime/runtime-contract';
187
187
  import {
188
- PLAY_RUNTIME_API_CURRENT_PATH,
189
- PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
188
+ PLAY_RUNTIME_API_COMPAT_PATH,
189
+ PLAY_RUNTIME_EGRESS_FETCH_COMPAT_PATH,
190
190
  } from '../../../shared_libs/play-runtime/runtime-api-paths';
191
191
  import { DYNAMIC_PLAY_WORKER_ARTIFACT_VERSION } from '../../../shared_libs/play-runtime/dynamic-worker-version';
192
192
  import {
@@ -860,7 +860,7 @@ async function fetchRuntimeApi(
860
860
  ? Math.max(1, Math.ceil(options.timeoutMsOverride))
861
861
  : path === '/api/v2/plays/run'
862
862
  ? RUNTIME_API_PLAY_RUN_TIMEOUT_MS
863
- : path === PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH
863
+ : path === PLAY_RUNTIME_EGRESS_FETCH_COMPAT_PATH
864
864
  ? RUNTIME_API_EGRESS_FETCH_TIMEOUT_MS
865
865
  : /^\/api\/v2\/integrations\/[^/]+\/execute$/.test(path)
866
866
  ? RUNTIME_API_INTEGRATION_EXECUTE_TIMEOUT_MS
@@ -1170,6 +1170,8 @@ function recordRunnerPerfTrace(input: {
1170
1170
  ts: Date.now(),
1171
1171
  source: 'dynamic_worker' as const,
1172
1172
  runId: input.req.runId,
1173
+ playName: input.req.playName,
1174
+ orgId: input.req.orgId,
1173
1175
  phase,
1174
1176
  ms: input.ms ?? 0,
1175
1177
  ...(input.extra ?? {}),
@@ -1316,7 +1318,7 @@ async function postRuntimeApi<T>(
1316
1318
  try {
1317
1319
  res = await fetchRuntimeApi(
1318
1320
  baseUrl,
1319
- PLAY_RUNTIME_API_CURRENT_PATH,
1321
+ PLAY_RUNTIME_API_COMPAT_PATH,
1320
1322
  {
1321
1323
  method: 'POST',
1322
1324
  headers: {
@@ -2890,7 +2892,7 @@ async function postRuntimeEgressFetch(
2890
2892
  ) {
2891
2893
  const response = await fetchRuntimeApi(
2892
2894
  req.baseUrl,
2893
- PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
2895
+ PLAY_RUNTIME_EGRESS_FETCH_COMPAT_PATH,
2894
2896
  {
2895
2897
  method: 'POST',
2896
2898
  headers: {
@@ -4263,7 +4265,7 @@ function createMinimalWorkerCtx(
4263
4265
  if (!auth) return {};
4264
4266
  const response = await fetchRuntimeApi(
4265
4267
  req.baseUrl,
4266
- PLAY_RUNTIME_API_CURRENT_PATH,
4268
+ PLAY_RUNTIME_API_COMPAT_PATH,
4267
4269
  {
4268
4270
  method: 'POST',
4269
4271
  headers: {
@@ -8942,7 +8944,7 @@ export class TenantWorkflow extends WorkflowEntrypoint<
8942
8944
  try {
8943
8945
  const response = await fetchRuntimeApi(
8944
8946
  req.baseUrl,
8945
- PLAY_RUNTIME_API_CURRENT_PATH,
8947
+ PLAY_RUNTIME_API_COMPAT_PATH,
8946
8948
  {
8947
8949
  method: 'POST',
8948
8950
  headers: {
@@ -106,10 +106,10 @@ export const SDK_RELEASE = {
106
106
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
107
107
  // fields shipped in 0.1.153.
108
108
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
109
- version: '0.1.215',
109
+ version: '0.1.217',
110
110
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
111
111
  supportPolicy: {
112
- latest: '0.1.215',
112
+ latest: '0.1.217',
113
113
  minimumSupported: '0.1.53',
114
114
  deprecatedBelow: '0.1.53',
115
115
  commandMinimumSupported: [
@@ -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_CURRENT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
41
+ import { PLAY_RUNTIME_API_COMPAT_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
 
@@ -788,7 +788,7 @@ export class AppRuntimeApiTransportError extends Error {
788
788
 
789
789
  function resolveAppRuntimeApiUrl(context: WorkerRuntimeApiContext): string {
790
790
  const baseUrl = context.baseUrl.trim().replace(/\/$/, '');
791
- return `${baseUrl}${PLAY_RUNTIME_API_CURRENT_PATH}`;
791
+ return `${baseUrl}${PLAY_RUNTIME_API_COMPAT_PATH}`;
792
792
  }
793
793
 
794
794
  async function postAppRuntimeApi<TResponse>(
@@ -48,8 +48,8 @@ import {
48
48
  SYNTHETIC_RUN_HEADER,
49
49
  } from './coordinator-headers';
50
50
  import {
51
+ PLAY_RUNTIME_API_COMPAT_PATH,
51
52
  PLAY_RUNTIME_API_CURRENT_PATH,
52
- PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_PATH,
53
53
  } from './runtime-api-paths';
54
54
  import {
55
55
  createRootRunExecutionScope,
@@ -262,6 +262,15 @@ const rowContext = new AsyncLocalStorage<{
262
262
  rowKey?: string;
263
263
  mapScope?: MapExecutionScope;
264
264
  }>();
265
+ type InlineCompositionStore = {
266
+ context: PlayContextImpl;
267
+ executionScope: RunExecutionScope;
268
+ governor: PlayExecutionGovernor;
269
+ playName: string;
270
+ staticPipeline: ContextOptions['staticPipeline'];
271
+ };
272
+ const inlineCompositionContext =
273
+ new AsyncLocalStorage<InlineCompositionStore>();
265
274
  const toolExecutionOverrides = new AsyncLocalStorage<{ force: boolean }>();
266
275
  const PROGRESS_HEARTBEAT_INTERVAL_MS = 1_000;
267
276
  const PURE_JS_HEARTBEAT_ROW_INTERVAL = 250;
@@ -1270,12 +1279,6 @@ export class PlayContextImpl {
1270
1279
  */
1271
1280
  private readonly governor: PlayExecutionGovernor;
1272
1281
  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
1282
  private readonly resolvedPlayExecutorCache = new Map<
1280
1283
  string,
1281
1284
  Promise<ResolvedPlayExecutor>
@@ -1443,7 +1446,6 @@ export class PlayContextImpl {
1443
1446
  this.resourceGovernor = createRuntimeResourceGovernor({
1444
1447
  executionGovernor: this.governor,
1445
1448
  });
1446
- this.governance = this.governor.snapshot();
1447
1449
  }
1448
1450
 
1449
1451
  private durableBoundaryId(localId: string): string {
@@ -1451,7 +1453,33 @@ export class PlayContextImpl {
1451
1453
  // Nested plays and concurrent child calls therefore need a stable run scope
1452
1454
  // in the key, otherwise two children can both produce e.g. "sleep-0-25"
1453
1455
  // and replay the wrong boundary or never observe completion.
1454
- return `${this.governance.currentRunId}:${localId}`;
1456
+ return `${this.currentGovernance.currentRunId}:${localId}`;
1457
+ }
1458
+
1459
+ private get activeInlineComposition(): InlineCompositionStore | null {
1460
+ const active = inlineCompositionContext.getStore();
1461
+ return active?.context === this ? active : null;
1462
+ }
1463
+
1464
+ private get currentExecutionScope(): RunExecutionScope {
1465
+ return this.activeInlineComposition?.executionScope ?? this.executionScope;
1466
+ }
1467
+
1468
+ private get currentExecutionGovernor(): PlayExecutionGovernor {
1469
+ return this.activeInlineComposition?.governor ?? this.governor;
1470
+ }
1471
+
1472
+ private get currentGovernance(): GovernanceSnapshot {
1473
+ return this.currentExecutionGovernor.snapshot();
1474
+ }
1475
+
1476
+ private get currentPlayName(): string | undefined {
1477
+ return this.activeInlineComposition?.playName ?? this.#options.playName;
1478
+ }
1479
+
1480
+ private get currentStaticPipeline(): ContextOptions['staticPipeline'] {
1481
+ const active = this.activeInlineComposition;
1482
+ return active ? active.staticPipeline : this.#options.staticPipeline;
1455
1483
  }
1456
1484
 
1457
1485
  private shouldLaunchChildPlaysThroughScheduler(): boolean {
@@ -1617,17 +1645,18 @@ export class PlayContextImpl {
1617
1645
  childInput: Record<string, unknown>;
1618
1646
  description?: string | null;
1619
1647
  }): Promise<void> {
1648
+ const parentGovernance = this.currentGovernance;
1620
1649
  const parentPlayName =
1621
- this.governance.currentPlayId ||
1622
- this.#options.playName ||
1650
+ parentGovernance.currentPlayId ||
1651
+ this.currentPlayName ||
1623
1652
  this.#options.playId ||
1624
1653
  'anonymous-play';
1625
1654
  const governance: PlayCallGovernanceSnapshot = {
1626
- rootRunId: this.governance.rootRunId,
1627
- parentRunId: this.governance.currentRunId,
1655
+ rootRunId: parentGovernance.rootRunId,
1656
+ parentRunId: parentGovernance.currentRunId,
1628
1657
  parentPlayName,
1629
1658
  key: input.key,
1630
- ancestryPlayIds: [...this.governance.ancestryPlayIds],
1659
+ ancestryPlayIds: [...parentGovernance.ancestryPlayIds],
1631
1660
  callDepth: input.childGovernance.callDepth,
1632
1661
  playCallCount: input.childGovernance.playCallCount,
1633
1662
  toolCallCount: input.childGovernance.toolCallCount,
@@ -1702,13 +1731,13 @@ export class PlayContextImpl {
1702
1731
  | null
1703
1732
  > {
1704
1733
  const response = await fetch(
1705
- `${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_CURRENT_PATH}`,
1734
+ `${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_COMPAT_PATH}`,
1706
1735
  {
1707
1736
  method: 'POST',
1708
1737
  headers: await this.runtimeApiHeaders(),
1709
1738
  body: JSON.stringify({
1710
1739
  action: 'read_child_run_terminal_snapshot',
1711
- parentRunId: this.governance.currentRunId,
1740
+ parentRunId: this.currentGovernance.currentRunId,
1712
1741
  childRunId: input.childRunId,
1713
1742
  childPlayName: input.childPlayName,
1714
1743
  }),
@@ -1817,7 +1846,7 @@ export class PlayContextImpl {
1817
1846
  this.#options.runId
1818
1847
  ) {
1819
1848
  const response = await fetch(
1820
- `${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`,
1849
+ `${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`,
1821
1850
  {
1822
1851
  method: 'POST',
1823
1852
  headers: {
@@ -2716,7 +2745,7 @@ export class PlayContextImpl {
2716
2745
  }): Promise<string> {
2717
2746
  return await this.durableToolCallCacheKeyForScope({
2718
2747
  ...input,
2719
- playLocalScope: this.governance.currentPlayId,
2748
+ playLocalScope: this.currentGovernance.currentPlayId,
2720
2749
  });
2721
2750
  }
2722
2751
 
@@ -2871,7 +2900,7 @@ export class PlayContextImpl {
2871
2900
  opts.receiptKey?.trim() ||
2872
2901
  durableCtxKey({
2873
2902
  orgId: this.#options.orgId,
2874
- playId: this.executionScope.receipt.namespace,
2903
+ playId: this.currentExecutionScope.receipt.namespace,
2875
2904
  operation,
2876
2905
  id,
2877
2906
  semanticKey: opts.semanticKey,
@@ -2905,15 +2934,15 @@ export class PlayContextImpl {
2905
2934
  }
2906
2935
 
2907
2936
  private get currentRunId(): string {
2908
- return this.executionScope.logical.runId;
2937
+ return this.currentExecutionScope.logical.runId;
2909
2938
  }
2910
2939
 
2911
2940
  private get currentReceiptOwnerRunId(): string {
2912
- return this.executionScope.receipt.ownerRunId;
2941
+ return this.currentExecutionScope.receipt.ownerRunId;
2913
2942
  }
2914
2943
 
2915
2944
  private get currentRunAttempt(): number {
2916
- return this.executionScope.receipt.ownerAttempt;
2945
+ return this.currentExecutionScope.receipt.ownerAttempt;
2917
2946
  }
2918
2947
 
2919
2948
  private emitScopedFieldMetaUpdate(input: {
@@ -3193,7 +3222,8 @@ export class PlayContextImpl {
3193
3222
  rowKey?: string;
3194
3223
  callId?: string;
3195
3224
  }): string {
3196
- const scope = this.governance.currentRunId || this.#options.runId || 'run';
3225
+ const scope =
3226
+ this.currentGovernance.currentRunId || this.#options.runId || 'run';
3197
3227
  if (input.callId?.trim()) {
3198
3228
  return `${scope}:${input.callId.trim()}`;
3199
3229
  }
@@ -3961,16 +3991,16 @@ export class PlayContextImpl {
3961
3991
  const flushStartedAt = Date.now();
3962
3992
  try {
3963
3993
  await this.#options.onMapRowsCompleted!({
3964
- playName: this.#options.playName,
3965
- playId: this.#options.playId,
3966
- runId: this.#options.runId,
3994
+ playName: this.currentPlayName,
3995
+ playId: this.currentExecutionScope.logical.playId,
3996
+ runId: this.currentRunId,
3967
3997
  executorToken: this.#options.executorToken,
3968
3998
  tableNamespace: resolvedTableNamespace,
3969
3999
  rows: chunk,
3970
4000
  outputFields: datasetColumnNames.filter((field) =>
3971
4001
  shouldPersistMapCellField(field),
3972
4002
  ),
3973
- staticPipeline: this.#options.staticPipeline ?? null,
4003
+ staticPipeline: this.currentStaticPipeline ?? null,
3974
4004
  });
3975
4005
  this.resourceGovernor.observe({
3976
4006
  sheetFlushBytes: chunkBytes,
@@ -4037,11 +4067,11 @@ export class PlayContextImpl {
4037
4067
  pageStartRows,
4038
4068
  resolvedTableNamespace,
4039
4069
  {
4040
- playName: this.#options.playName,
4041
- playId: this.#options.playId,
4042
- runId: this.#options.runId,
4070
+ playName: this.currentPlayName,
4071
+ playId: this.currentExecutionScope.logical.playId,
4072
+ runId: this.currentRunId,
4043
4073
  executorToken: this.#options.executorToken,
4044
- staticPipeline: this.#options.staticPipeline,
4074
+ staticPipeline: this.currentStaticPipeline,
4045
4075
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4046
4076
  inputOffset: page.offset,
4047
4077
  },
@@ -4472,11 +4502,11 @@ export class PlayContextImpl {
4472
4502
  mapStartRows,
4473
4503
  resolvedTableNamespace,
4474
4504
  {
4475
- playName: this.#options.playName,
4476
- playId: this.#options.playId,
4477
- runId: this.#options.runId,
4505
+ playName: this.currentPlayName,
4506
+ playId: this.currentExecutionScope.logical.playId,
4507
+ runId: this.currentRunId,
4478
4508
  executorToken: this.#options.executorToken,
4479
- staticPipeline: this.#options.staticPipeline,
4509
+ staticPipeline: this.currentStaticPipeline,
4480
4510
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4481
4511
  },
4482
4512
  );
@@ -4620,16 +4650,16 @@ export class PlayContextImpl {
4620
4650
  const flushStartedAt = Date.now();
4621
4651
  try {
4622
4652
  const writeResult = await this.#options.onMapRowsCompleted!({
4623
- playName: this.#options.playName,
4624
- playId: this.#options.playId,
4625
- runId: this.#options.runId,
4653
+ playName: this.currentPlayName,
4654
+ playId: this.currentExecutionScope.logical.playId,
4655
+ runId: this.currentRunId,
4626
4656
  executorToken: this.#options.executorToken,
4627
4657
  tableNamespace: resolvedTableNamespace,
4628
4658
  rows: chunk,
4629
4659
  outputFields: datasetColumnNames.filter((field) =>
4630
4660
  shouldPersistMapCellField(field),
4631
4661
  ),
4632
- staticPipeline: this.#options.staticPipeline ?? null,
4662
+ staticPipeline: this.currentStaticPipeline ?? null,
4633
4663
  });
4634
4664
  if (writeResult) {
4635
4665
  for (const key of writeResult.staleDroppedKeys ?? []) {
@@ -6158,6 +6188,7 @@ export class PlayContextImpl {
6158
6188
  input: Record<string, unknown>,
6159
6189
  options?: ToolCallOptions,
6160
6190
  ): Promise<unknown> {
6191
+ const executionScope = this.currentExecutionScope;
6161
6192
  const normalizedKey = this.normalizeContextKey(key, 'tool');
6162
6193
  const toolCachePolicy = this.effectiveToolCallCachePolicy(options);
6163
6194
  const toolRequestIdentity = deriveToolRequestIdentity({
@@ -6303,6 +6334,7 @@ export class PlayContextImpl {
6303
6334
  const rowId = store.rowId;
6304
6335
  const fieldName = store.fieldName;
6305
6336
  const callId = [
6337
+ executionScope.receipt.namespace,
6306
6338
  store.tableNamespace?.trim() || 'map',
6307
6339
  store.rowKey?.trim() || String(rowId),
6308
6340
  normalizedKey,
@@ -6669,10 +6701,10 @@ export class PlayContextImpl {
6669
6701
  });
6670
6702
 
6671
6703
  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.
6704
+ // Scheduled children reserve the parent's durable play/descendant
6705
+ // budgets. Inline scalar children are function composition: their
6706
+ // Governor view changes only local lineage for recursion/depth checks
6707
+ // and delegates tool admission, pacing, and tool budgets to the parent.
6676
6708
  //
6677
6709
  // The SCHEDULED substrate still allocates a durable child run id (through
6678
6710
  // the shared canonical builder so in-process and the workers_edge
@@ -6682,9 +6714,10 @@ export class PlayContextImpl {
6682
6714
  // settlement. Their only identity is a replay-stable, call-site scoped
6683
6715
  // composition namespace (`child:<playName>#<ordinal>`) used to scope tool
6684
6716
  // receipts under the PARENT run. See child-composition-namespace below.
6717
+ const parentGovernor = this.currentExecutionGovernor;
6685
6718
  const inlineChildGovernor = launchThroughScheduler
6686
6719
  ? null
6687
- : await this.governor.forkInlineChild({
6720
+ : await parentGovernor.forkInlineChild({
6688
6721
  childPlayName: resolvedName,
6689
6722
  childRunId: this.inlineChildCompositionNamespace(
6690
6723
  resolvedName,
@@ -6692,7 +6725,7 @@ export class PlayContextImpl {
6692
6725
  ),
6693
6726
  });
6694
6727
  const childGovernance = launchThroughScheduler
6695
- ? await this.governor.forkChild({
6728
+ ? await parentGovernor.forkChild({
6696
6729
  childPlayName: resolvedName,
6697
6730
  childRunId: buildChildRunId({
6698
6731
  childPlayName: resolvedName,
@@ -6706,8 +6739,8 @@ export class PlayContextImpl {
6706
6739
  })
6707
6740
  : inlineChildGovernor!.snapshot();
6708
6741
  const childPlaySlot = launchThroughScheduler
6709
- ? await this.governor.acquireChildSubmitSlot()
6710
- : await this.governor.acquireInlineChildSlot();
6742
+ ? await parentGovernor.acquireChildSubmitSlot()
6743
+ : null;
6711
6744
  const rowStore = rowContext.getStore();
6712
6745
  const producer = {
6713
6746
  kind: 'play' as const,
@@ -6840,7 +6873,7 @@ export class PlayContextImpl {
6840
6873
  // per-row inline-invocation sanity cap at creation).
6841
6874
  const compositionNamespace = childGovernance.currentRunId;
6842
6875
  const childExecutionScope = deriveChildRunExecutionScope(
6843
- this.executionScope,
6876
+ this.currentExecutionScope,
6844
6877
  {
6845
6878
  placement: 'inline',
6846
6879
  runId: compositionNamespace,
@@ -6849,49 +6882,22 @@ export class PlayContextImpl {
6849
6882
  },
6850
6883
  );
6851
6884
  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
6885
  try {
6888
- const childExecution = this.executeResolvedPlay(
6889
- resolvedPlay,
6890
- childContext,
6891
- input,
6886
+ // A scalar child is a normal function call on the parent's context.
6887
+ // Async-local identity changes receipt and recursion scope without
6888
+ // allocating another PlayContext, tool queue, drain loop, resource
6889
+ // governor, or receipt client. Calls from every concurrent child
6890
+ // therefore reach the same proven parent batching path.
6891
+ const result = await inlineCompositionContext.run(
6892
+ {
6893
+ context: this,
6894
+ executionScope: childExecutionScope,
6895
+ governor: inlineChildGovernor!,
6896
+ playName: resolvedName,
6897
+ staticPipeline: resolvedPlay.staticPipeline ?? null,
6898
+ },
6899
+ () => this.executeResolvedPlay(resolvedPlay, this, input),
6892
6900
  );
6893
- await childContext.drainQueuedWork([childExecution]);
6894
- const result = await childExecution;
6895
6901
  this.inlineChildAggregates.ok += 1;
6896
6902
  if (rowStore) {
6897
6903
  this.emitScopedFieldMetaUpdate({
@@ -6912,7 +6918,6 @@ export class PlayContextImpl {
6912
6918
  playId: resolvedName,
6913
6919
  execution: options?.execution,
6914
6920
  description: options?.description,
6915
- nestedSteps: childContext.getSteps(),
6916
6921
  });
6917
6922
  return result as TOutput;
6918
6923
  } catch (childError) {
@@ -6950,57 +6955,6 @@ export class PlayContextImpl {
6950
6955
  }
6951
6956
  }
6952
6957
 
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_CURRENT_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
6958
  /**
7005
6959
  * Replay-stable composition namespace for one inline `ctx.runPlay`
7006
6960
  * invocation. Format `child:<playName>#<callKey>[@<rowScope>]`:
@@ -7358,7 +7312,7 @@ export class PlayContextImpl {
7358
7312
 
7359
7313
  const rowStore = rowContext.getStore();
7360
7314
  const scope = rowStore ? `row-${rowStore.rowId}` : 'workflow';
7361
- const callIndexKey = `${scope}:${normalizedKey}`;
7315
+ const callIndexKey = `${this.currentExecutionScope.receipt.namespace}:${scope}:${normalizedKey}`;
7362
7316
  const callIndex = this.stepCallIndexByKey.get(callIndexKey) ?? 0;
7363
7317
  this.stepCallIndexByKey.set(callIndexKey, callIndex + 1);
7364
7318
  const boundarySuffix = callIndex === 0 ? '' : `:${callIndex}`;
@@ -106,13 +106,6 @@ export interface PlayExecutionGovernor {
106
106
  acquireRowSlot(opts?: { signal?: AbortSignal }): Promise<WorkLease>;
107
107
  /** Block until a child-play submit slot is free. Released after submit, not terminal. */
108
108
  acquireChildSubmitSlot(opts?: { signal?: AbortSignal }): Promise<WorkLease>;
109
- /**
110
- * Block until an in-process child-composition slot is free. Unlike a
111
- * scheduled child's submit slot, this lease is held through child terminal
112
- * execution so a large row fan-out cannot materialize thousands of child
113
- * contexts and receipt closures at once.
114
- */
115
- acquireInlineChildSlot(opts?: { signal?: AbortSignal }): Promise<WorkLease>;
116
109
  /**
117
110
  * Block until a global tool-concurrency slot AND the per-(org,provider) pacer
118
111
  * permit are free, then charge the tool-call budget and return a lease. Order:
@@ -404,7 +397,6 @@ export function createPlayExecutionGovernor(
404
397
 
405
398
  acquireRowSlot: (opts) => rowSlots.acquire(opts?.signal),
406
399
  acquireChildSubmitSlot: (opts) => childPlaySlots.acquire(opts?.signal),
407
- acquireInlineChildSlot: (opts) => childPlaySlots.acquire(opts?.signal),
408
400
 
409
401
  async acquireToolSlot(toolId, opts) {
410
402
  // 1. global tool-concurrency slot.
@@ -548,7 +540,7 @@ export function createPlayExecutionGovernor(
548
540
  async forkInlineChild(childInput) {
549
541
  return createInlineChildGovernor(
550
542
  governor,
551
- await governor.forkChild(childInput),
543
+ deriveInlineChildSnapshot(state, childInput, policy),
552
544
  );
553
545
  },
554
546
 
@@ -601,6 +593,38 @@ export function createPlayExecutionGovernor(
601
593
  return governor;
602
594
  }
603
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
+
604
628
  function createInlineChildGovernor(
605
629
  root: PlayExecutionGovernor,
606
630
  initial: GovernanceSnapshot,
@@ -620,17 +644,6 @@ function createInlineChildGovernor(
620
644
  policy: root.policy,
621
645
  acquireRowSlot: (opts) => root.acquireRowSlot(opts),
622
646
  acquireChildSubmitSlot: (opts) => root.acquireChildSubmitSlot(opts),
623
- async acquireInlineChildSlot(opts) {
624
- if (opts?.signal?.aborted) {
625
- throw opts.signal.reason instanceof Error
626
- ? opts.signal.reason
627
- : new Error('Slot acquire aborted.');
628
- }
629
- // This governor already runs inside a root inline-child lease. Descendant
630
- // composition shares that admitted subtree slot; reacquiring the root
631
- // semaphore here can deadlock when every admitted parent awaits a child.
632
- return { release: () => {} };
633
- },
634
647
  acquireToolSlot: (toolId, opts) => root.acquireToolSlot(toolId, opts),
635
648
  suggestedParallelism: (toolId, fallback) =>
636
649
  root.suggestedParallelism(toolId, fallback),
@@ -686,7 +699,10 @@ function createInlineChildGovernor(
686
699
  };
687
700
  },
688
701
  async forkInlineChild(input) {
689
- return createInlineChildGovernor(child, await child.forkChild(input));
702
+ return createInlineChildGovernor(
703
+ child,
704
+ deriveInlineChildSnapshot(child.snapshot(), input, child.policy),
705
+ );
690
706
  },
691
707
  snapshot() {
692
708
  const counters = root.snapshot();
@@ -95,7 +95,7 @@ import {
95
95
  PLAY_RUNTIME_CONTRACT,
96
96
  PLAY_RUNTIME_CONTRACT_HEADER,
97
97
  } from './runtime-contract';
98
- import { PLAY_RUNTIME_API_CURRENT_PATH } from './runtime-api-paths';
98
+ import { PLAY_RUNTIME_API_COMPAT_PATH } from './runtime-api-paths';
99
99
  import {
100
100
  PLAY_RUNTIME_SHEET_ATTEMPT_LEASE_TTL_MS,
101
101
  PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
@@ -298,7 +298,6 @@ const RUNTIME_WORK_RECEIPT_LEASE_COLUMNS = [
298
298
  'lease_expires_at',
299
299
  ] as const;
300
300
  const RUNTIME_WORK_RECEIPT_FAILURE_KIND_COLUMN = 'failure_kind';
301
- const RUNTIME_WORK_RECEIPT_KEY_INDEX = 'idx_deepline_step_receipts_k_unique';
302
301
  const RUNTIME_WORK_RECEIPT_SELF_HEAL_COLUMNS = [
303
302
  ...RUNTIME_WORK_RECEIPT_LEASE_COLUMNS,
304
303
  RUNTIME_WORK_RECEIPT_FAILURE_KIND_COLUMN,
@@ -505,7 +504,7 @@ function resolveRuntimeApiUrl(context: RuntimeApiContext): string {
505
504
  if (!baseUrl) {
506
505
  throw new Error('Runner runtime API requires a baseUrl.');
507
506
  }
508
- return `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`;
507
+ return `${baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_COMPAT_PATH}`;
509
508
  }
510
509
 
511
510
  function resolveRuntimeApiHeaders(
@@ -587,10 +586,10 @@ async function postRuntimeApi<TResponse>(
587
586
  ? parsed.retry_after_ms
588
587
  : RUNTIME_API_DEFAULT_RETRY_AFTER_MS;
589
588
  const details =
590
- typeof parsed?.details === 'string'
591
- ? parsed.details
592
- : typeof parsed?.detail === 'string'
593
- ? parsed.detail
589
+ typeof parsed?.detail === 'string'
590
+ ? parsed.detail
591
+ : typeof parsed?.details === 'string'
592
+ ? parsed.details
594
593
  : typeof parsed?.debug_error === 'string'
595
594
  ? parsed.debug_error
596
595
  : null;
@@ -2043,22 +2042,6 @@ function isMissingRuntimeWorkReceiptSelfHealColumnError(
2043
2042
  );
2044
2043
  }
2045
2044
 
2046
- function isMissingRuntimeWorkReceiptKeyConstraintError(
2047
- error: unknown,
2048
- ): boolean {
2049
- if (!error || typeof error !== 'object') {
2050
- return false;
2051
- }
2052
- const code = 'code' in error ? String(error.code) : '';
2053
- const message = 'message' in error ? String(error.message) : '';
2054
- return (
2055
- code === '42P10' ||
2056
- /no unique or exclusion constraint matching the ON CONFLICT specification/i.test(
2057
- message,
2058
- )
2059
- );
2060
- }
2061
-
2062
2045
  function runtimeWorkReceiptEnsureCacheKey(
2063
2046
  session: RuntimePostgresSession,
2064
2047
  ): string {
@@ -2138,29 +2121,22 @@ async function ensureRuntimeWorkReceiptTable(
2138
2121
  session,
2139
2122
  client,
2140
2123
  );
2141
- if (missingColumns.length > 0) {
2142
- await client.query(`
2143
- ALTER TABLE ${workReceiptTable(session)}
2144
- ${missingColumns
2145
- .map((column) => {
2146
- const type =
2147
- column === 'lease_expires_at'
2148
- ? 'timestamptz'
2149
- : column === 'lease_owner_attempt'
2150
- ? 'integer'
2151
- : column === RUNTIME_WORK_RECEIPT_FAILURE_KIND_COLUMN
2152
- ? 'smallint NOT NULL DEFAULT 0'
2153
- : 'text';
2154
- return `ADD COLUMN IF NOT EXISTS ${column} ${type}`;
2155
- })
2156
- .join(',\n ')}
2157
- `);
2158
- }
2124
+ if (missingColumns.length === 0) return;
2159
2125
  await client.query(`
2160
- CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdentifier(
2161
- RUNTIME_WORK_RECEIPT_KEY_INDEX,
2162
- )}
2163
- ON ${workReceiptTable(session)} (k)
2126
+ ALTER TABLE ${workReceiptTable(session)}
2127
+ ${missingColumns
2128
+ .map((column) => {
2129
+ const type =
2130
+ column === 'lease_expires_at'
2131
+ ? 'timestamptz'
2132
+ : column === 'lease_owner_attempt'
2133
+ ? 'integer'
2134
+ : column === RUNTIME_WORK_RECEIPT_FAILURE_KIND_COLUMN
2135
+ ? 'smallint NOT NULL DEFAULT 0'
2136
+ : 'text';
2137
+ return `ADD COLUMN IF NOT EXISTS ${column} ${type}`;
2138
+ })
2139
+ .join(',\n ')}
2164
2140
  `);
2165
2141
  })
2166
2142
  .then(() => undefined);
@@ -2264,8 +2240,7 @@ async function withRuntimeWorkReceiptClient<T>(
2264
2240
  } catch (error) {
2265
2241
  if (
2266
2242
  isMissingRelationError(error) ||
2267
- isMissingRuntimeWorkReceiptSelfHealColumnError(error) ||
2268
- isMissingRuntimeWorkReceiptKeyConstraintError(error)
2243
+ isMissingRuntimeWorkReceiptSelfHealColumnError(error)
2269
2244
  ) {
2270
2245
  runtimeWorkReceiptEnsureCache.delete(
2271
2246
  runtimeWorkReceiptEnsureCacheKey(session),
package/dist/cli/index.js CHANGED
@@ -623,10 +623,10 @@ var SDK_RELEASE = {
623
623
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
624
624
  // fields shipped in 0.1.153.
625
625
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
626
- version: "0.1.215",
626
+ version: "0.1.217",
627
627
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
628
628
  supportPolicy: {
629
- latest: "0.1.215",
629
+ latest: "0.1.217",
630
630
  minimumSupported: "0.1.53",
631
631
  deprecatedBelow: "0.1.53",
632
632
  commandMinimumSupported: [
@@ -13348,7 +13348,9 @@ function normalizeProgressForEnvelope(status, rowsInfo) {
13348
13348
  const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
13349
13349
  return {
13350
13350
  total,
13351
+ totalRows: total,
13351
13352
  completed,
13353
+ completedRows: completed,
13352
13354
  pending,
13353
13355
  failed,
13354
13356
  executed: getNumericField(progress, "executed"),
@@ -15483,7 +15485,7 @@ async function handleRunsList(args) {
15483
15485
  return 0;
15484
15486
  }
15485
15487
  async function handleRunTail(args) {
15486
- const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]";
15488
+ const usage = "Usage: deepline runs tail <run-id> [--json | --jsonl] [--compact]";
15487
15489
  let runId;
15488
15490
  try {
15489
15491
  runId = parseRunIdPositional(args, usage);
@@ -15499,13 +15501,18 @@ async function handleRunTail(args) {
15499
15501
  );
15500
15502
  return 1;
15501
15503
  }
15502
- if (arg.startsWith("--") && arg !== "--json" && arg !== "--compact") {
15504
+ if (arg.startsWith("--") && arg !== "--json" && arg !== "--jsonl" && arg !== "--compact") {
15503
15505
  console.error(`${arg} is not supported by deepline runs tail.`);
15504
15506
  return 1;
15505
15507
  }
15506
15508
  }
15509
+ if (args.includes("--json") && args.includes("--jsonl")) {
15510
+ console.error("--json and --jsonl cannot be used together.");
15511
+ return 1;
15512
+ }
15507
15513
  const client2 = new DeeplineClient();
15508
- const jsonOutput = argsWantJson(args);
15514
+ const jsonLines = args.includes("--jsonl");
15515
+ const jsonOutput = !jsonLines && argsWantJson(args);
15509
15516
  const compact = args.includes("--compact");
15510
15517
  const compactState = {
15511
15518
  lastLogIndex: 0,
@@ -15515,7 +15522,16 @@ async function handleRunTail(args) {
15515
15522
  lastStatusHeartbeatAt: 0
15516
15523
  };
15517
15524
  const status = await client2.runs.tail(runId, {
15518
- onEvent: compact && !jsonOutput ? (event) => {
15525
+ onEvent: jsonLines ? (event) => {
15526
+ process.stdout.write(
15527
+ `${JSON.stringify({
15528
+ schemaVersion: 1,
15529
+ kind: "play_run_event",
15530
+ event
15531
+ })}
15532
+ `
15533
+ );
15534
+ } : compact && !jsonOutput ? (event) => {
15519
15535
  const transition = getStepTransitionLineFromLiveEvent(
15520
15536
  event,
15521
15537
  compactState
@@ -15545,7 +15561,12 @@ async function handleRunTail(args) {
15545
15561
  );
15546
15562
  }
15547
15563
  });
15548
- writePlayResult(status, jsonOutput);
15564
+ if (jsonLines) {
15565
+ process.stdout.write(`${JSON.stringify(compactPlayStatus(status))}
15566
+ `);
15567
+ } else {
15568
+ writePlayResult(status, jsonOutput);
15569
+ }
15549
15570
  return status.status === "failed" ? 1 : 0;
15550
15571
  }
15551
15572
  async function handleRunLogs(args) {
@@ -16911,19 +16932,26 @@ Examples:
16911
16932
  Notes:
16912
16933
  Streams live run events until the stream ends. Use get for current status and
16913
16934
  logs for persisted log history. In human output, --compact prints deduplicated
16914
- step transitions and progress instead of the full event stream.
16935
+ step transitions and progress instead of the full event stream. --json emits
16936
+ one terminal package. --jsonl emits canonical live events as JSON Lines and
16937
+ ends with the same compact package shape as runs get --json.
16915
16938
 
16916
16939
  Examples:
16917
16940
  deepline runs tail play/my-play/run/20260501t000000-000
16918
- deepline runs tail play/my-play/run/20260501t000000-000 --compact --json
16941
+ deepline runs tail play/my-play/run/20260501t000000-000 --compact
16942
+ deepline runs tail play/my-play/run/20260501t000000-000 --jsonl
16919
16943
  `
16920
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
16944
+ ).option("--json", "Emit one terminal JSON package after the run completes").option(
16945
+ "--jsonl",
16946
+ "Stream live events as JSON Lines, then the terminal package"
16947
+ ).option(
16921
16948
  "--compact",
16922
- "Show deduplicated step transitions and progress; compact JSON output"
16949
+ "Show deduplicated human step transitions and progress"
16923
16950
  ).action(async (runId, options) => {
16924
16951
  process.exitCode = await handleRunTail([
16925
16952
  runId,
16926
16953
  ...options.json ? ["--json"] : [],
16954
+ ...options.jsonl ? ["--jsonl"] : [],
16927
16955
  ...options.compact ? ["--compact"] : []
16928
16956
  ]);
16929
16957
  });
@@ -608,10 +608,10 @@ var SDK_RELEASE = {
608
608
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
609
609
  // fields shipped in 0.1.153.
610
610
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
- version: "0.1.215",
611
+ version: "0.1.217",
612
612
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
613
613
  supportPolicy: {
614
- latest: "0.1.215",
614
+ latest: "0.1.217",
615
615
  minimumSupported: "0.1.53",
616
616
  deprecatedBelow: "0.1.53",
617
617
  commandMinimumSupported: [
@@ -13377,7 +13377,9 @@ function normalizeProgressForEnvelope(status, rowsInfo) {
13377
13377
  const pending = getNumericField(progress, "pending") ?? (typeof total === "number" && typeof completed === "number" && typeof failed === "number" ? Math.max(0, total - completed - failed) : null);
13378
13378
  return {
13379
13379
  total,
13380
+ totalRows: total,
13380
13381
  completed,
13382
+ completedRows: completed,
13381
13383
  pending,
13382
13384
  failed,
13383
13385
  executed: getNumericField(progress, "executed"),
@@ -15512,7 +15514,7 @@ async function handleRunsList(args) {
15512
15514
  return 0;
15513
15515
  }
15514
15516
  async function handleRunTail(args) {
15515
- const usage = "Usage: deepline runs tail <run-id> [--json] [--compact]";
15517
+ const usage = "Usage: deepline runs tail <run-id> [--json | --jsonl] [--compact]";
15516
15518
  let runId;
15517
15519
  try {
15518
15520
  runId = parseRunIdPositional(args, usage);
@@ -15528,13 +15530,18 @@ async function handleRunTail(args) {
15528
15530
  );
15529
15531
  return 1;
15530
15532
  }
15531
- if (arg.startsWith("--") && arg !== "--json" && arg !== "--compact") {
15533
+ if (arg.startsWith("--") && arg !== "--json" && arg !== "--jsonl" && arg !== "--compact") {
15532
15534
  console.error(`${arg} is not supported by deepline runs tail.`);
15533
15535
  return 1;
15534
15536
  }
15535
15537
  }
15538
+ if (args.includes("--json") && args.includes("--jsonl")) {
15539
+ console.error("--json and --jsonl cannot be used together.");
15540
+ return 1;
15541
+ }
15536
15542
  const client2 = new DeeplineClient();
15537
- const jsonOutput = argsWantJson(args);
15543
+ const jsonLines = args.includes("--jsonl");
15544
+ const jsonOutput = !jsonLines && argsWantJson(args);
15538
15545
  const compact = args.includes("--compact");
15539
15546
  const compactState = {
15540
15547
  lastLogIndex: 0,
@@ -15544,7 +15551,16 @@ async function handleRunTail(args) {
15544
15551
  lastStatusHeartbeatAt: 0
15545
15552
  };
15546
15553
  const status = await client2.runs.tail(runId, {
15547
- onEvent: compact && !jsonOutput ? (event) => {
15554
+ onEvent: jsonLines ? (event) => {
15555
+ process.stdout.write(
15556
+ `${JSON.stringify({
15557
+ schemaVersion: 1,
15558
+ kind: "play_run_event",
15559
+ event
15560
+ })}
15561
+ `
15562
+ );
15563
+ } : compact && !jsonOutput ? (event) => {
15548
15564
  const transition = getStepTransitionLineFromLiveEvent(
15549
15565
  event,
15550
15566
  compactState
@@ -15574,7 +15590,12 @@ async function handleRunTail(args) {
15574
15590
  );
15575
15591
  }
15576
15592
  });
15577
- writePlayResult(status, jsonOutput);
15593
+ if (jsonLines) {
15594
+ process.stdout.write(`${JSON.stringify(compactPlayStatus(status))}
15595
+ `);
15596
+ } else {
15597
+ writePlayResult(status, jsonOutput);
15598
+ }
15578
15599
  return status.status === "failed" ? 1 : 0;
15579
15600
  }
15580
15601
  async function handleRunLogs(args) {
@@ -16940,19 +16961,26 @@ Examples:
16940
16961
  Notes:
16941
16962
  Streams live run events until the stream ends. Use get for current status and
16942
16963
  logs for persisted log history. In human output, --compact prints deduplicated
16943
- step transitions and progress instead of the full event stream.
16964
+ step transitions and progress instead of the full event stream. --json emits
16965
+ one terminal package. --jsonl emits canonical live events as JSON Lines and
16966
+ ends with the same compact package shape as runs get --json.
16944
16967
 
16945
16968
  Examples:
16946
16969
  deepline runs tail play/my-play/run/20260501t000000-000
16947
- deepline runs tail play/my-play/run/20260501t000000-000 --compact --json
16970
+ deepline runs tail play/my-play/run/20260501t000000-000 --compact
16971
+ deepline runs tail play/my-play/run/20260501t000000-000 --jsonl
16948
16972
  `
16949
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
16973
+ ).option("--json", "Emit one terminal JSON package after the run completes").option(
16974
+ "--jsonl",
16975
+ "Stream live events as JSON Lines, then the terminal package"
16976
+ ).option(
16950
16977
  "--compact",
16951
- "Show deduplicated step transitions and progress; compact JSON output"
16978
+ "Show deduplicated human step transitions and progress"
16952
16979
  ).action(async (runId, options) => {
16953
16980
  process.exitCode = await handleRunTail([
16954
16981
  runId,
16955
16982
  ...options.json ? ["--json"] : [],
16983
+ ...options.jsonl ? ["--jsonl"] : [],
16956
16984
  ...options.compact ? ["--compact"] : []
16957
16985
  ]);
16958
16986
  });
package/dist/index.js CHANGED
@@ -422,10 +422,10 @@ var SDK_RELEASE = {
422
422
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
423
423
  // fields shipped in 0.1.153.
424
424
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
425
- version: "0.1.215",
425
+ version: "0.1.217",
426
426
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
427
427
  supportPolicy: {
428
- latest: "0.1.215",
428
+ latest: "0.1.217",
429
429
  minimumSupported: "0.1.53",
430
430
  deprecatedBelow: "0.1.53",
431
431
  commandMinimumSupported: [
package/dist/index.mjs CHANGED
@@ -352,10 +352,10 @@ var SDK_RELEASE = {
352
352
  // 0.1.154 removes the short-lived generated enrich StepOptions recompute
353
353
  // fields shipped in 0.1.153.
354
354
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
355
- version: "0.1.215",
355
+ version: "0.1.217",
356
356
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
357
357
  supportPolicy: {
358
- latest: "0.1.215",
358
+ latest: "0.1.217",
359
359
  minimumSupported: "0.1.53",
360
360
  deprecatedBelow: "0.1.53",
361
361
  commandMinimumSupported: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.215",
3
+ "version": "0.1.217",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,34 +0,0 @@
1
- import {
2
- PLAY_RUNTIME_API_CURRENT_PATH,
3
- PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_PATH,
4
- PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH,
5
- } from '../../../shared_libs/play-runtime/runtime-api-paths';
6
-
7
- /**
8
- * Paths a credential-isolated Daytona sandbox may send through the
9
- * coordinator. The sandbox receives the coordinator as its runtime origin so
10
- * it never holds the Vercel protection-bypass credential; the harness owns the
11
- * protected upstream hop. Keep this deliberately narrower than the harness
12
- * allowlist: a coordinator URL is public, while the harness is a service
13
- * binding.
14
- */
15
- export function isCoordinatorRuntimeProxyPath(pathname: string): boolean {
16
- return (
17
- pathname === PLAY_RUNTIME_API_CURRENT_PATH ||
18
- pathname === PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_PATH ||
19
- pathname === PLAY_RUNTIME_EGRESS_FETCH_CURRENT_PATH ||
20
- pathname === '/api/v2/plays/run' ||
21
- /^\/api\/v2\/plays\/runtime-tools\/[^/]+(?:\/(?:auth-scope|event-wait))?$/.test(
22
- pathname,
23
- ) ||
24
- /^\/api\/v2\/integrations\/[^/]+\/(?:execute|stream)$/.test(pathname)
25
- );
26
- }
27
-
28
- export function executorTokenFromAuthorizationHeader(
29
- authorization: string | null,
30
- ): string | null {
31
- const match = /^Bearer\s+(.+)$/i.exec(authorization?.trim() ?? '');
32
- const token = match?.[1]?.trim();
33
- return token || null;
34
- }