deepline 0.1.215 → 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.
@@ -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
 
@@ -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.216',
110
110
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
111
111
  supportPolicy: {
112
- latest: '0.1.215',
112
+ latest: '0.1.216',
113
113
  minimumSupported: '0.1.53',
114
114
  deprecatedBelow: '0.1.53',
115
115
  commandMinimumSupported: [
@@ -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_CURRENT_PATH,
52
- PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_CURRENT_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,
@@ -1708,7 +1734,7 @@ export class PlayContextImpl {
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
  }),
@@ -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,8 +6736,8 @@ export class PlayContextImpl {
6706
6736
  })
6707
6737
  : inlineChildGovernor!.snapshot();
6708
6738
  const childPlaySlot = launchThroughScheduler
6709
- ? await this.governor.acquireChildSubmitSlot()
6710
- : await this.governor.acquireInlineChildSlot();
6739
+ ? await parentGovernor.acquireChildSubmitSlot()
6740
+ : null;
6711
6741
  const rowStore = rowContext.getStore();
6712
6742
  const producer = {
6713
6743
  kind: 'play' as const,
@@ -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_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
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}`;
@@ -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();
@@ -587,10 +587,10 @@ async function postRuntimeApi<TResponse>(
587
587
  ? parsed.retry_after_ms
588
588
  : RUNTIME_API_DEFAULT_RETRY_AFTER_MS;
589
589
  const details =
590
- typeof parsed?.details === 'string'
591
- ? parsed.details
592
- : typeof parsed?.detail === 'string'
593
- ? parsed.detail
590
+ typeof parsed?.detail === 'string'
591
+ ? parsed.detail
592
+ : typeof parsed?.details === 'string'
593
+ ? parsed.details
594
594
  : typeof parsed?.debug_error === 'string'
595
595
  ? parsed.debug_error
596
596
  : null;
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.216",
627
627
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
628
628
  supportPolicy: {
629
- latest: "0.1.215",
629
+ latest: "0.1.216",
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.216",
612
612
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
613
613
  supportPolicy: {
614
- latest: "0.1.215",
614
+ latest: "0.1.216",
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.216",
426
426
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
427
427
  supportPolicy: {
428
- latest: "0.1.215",
428
+ latest: "0.1.216",
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.216",
356
356
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
357
357
  supportPolicy: {
358
- latest: "0.1.215",
358
+ latest: "0.1.216",
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.216",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {