deepline 0.1.228 → 0.1.229
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.
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +76 -1842
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +0 -113
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +81 -968
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +1 -50
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +67 -15
- package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +84 -79
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +152 -688
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +3 -10
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +12 -47
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +0 -2
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +12 -200
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +2 -17
- package/dist/bundling-sources/shared_libs/play-runtime/play-call-execution.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +28 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-execution-scope.ts +16 -64
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +0 -4
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +37 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +0 -36
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +3 -21
- package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +9 -1
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +2 -0
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/{compiler-manifest-BhgZ23A4.d.ts → compiler-manifest-CYcwzSOJ.d.mts} +2 -0
- package/dist/{compiler-manifest-BhgZ23A4.d.mts → compiler-manifest-CYcwzSOJ.d.ts} +2 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
- package/dist/bundling-sources/apps/play-runner-workers/src/child-manifest-resolver.ts +0 -108
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-await.ts +0 -374
- package/dist/bundling-sources/apps/play-runner-workers/src/child-play-submit.ts +0 -203
- package/dist/bundling-sources/shared_libs/play-runtime/child-execution-placement.ts +0 -14
- package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +0 -121
|
@@ -42,17 +42,7 @@ import {
|
|
|
42
42
|
mapRowOutcomeRuntimeFields,
|
|
43
43
|
resolveMapRowOutcomeKey,
|
|
44
44
|
} from './map-row-outcome';
|
|
45
|
-
import {
|
|
46
|
-
import type { PlayCallGovernanceSnapshot } from './scheduler-backend';
|
|
47
|
-
import {
|
|
48
|
-
ABSURD_RELEASE_OVERRIDE_HEADER,
|
|
49
|
-
RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER,
|
|
50
|
-
SYNTHETIC_RUN_HEADER,
|
|
51
|
-
} from './coordinator-headers';
|
|
52
|
-
import {
|
|
53
|
-
PLAY_RUNTIME_API_COMPAT_PATH,
|
|
54
|
-
PLAY_RUNTIME_API_CURRENT_PATH,
|
|
55
|
-
} from './runtime-api-paths';
|
|
45
|
+
import { PLAY_RUNTIME_API_COMPAT_PATH } from './runtime-api-paths';
|
|
56
46
|
import {
|
|
57
47
|
createRootRunExecutionScope,
|
|
58
48
|
deriveChildRunExecutionScope,
|
|
@@ -245,8 +235,8 @@ import {
|
|
|
245
235
|
} from './step-program-dataset-builder';
|
|
246
236
|
import { readRuntimeSheetDatasetRows } from './runtime-api';
|
|
247
237
|
import {
|
|
238
|
+
ctxRunPlayInlineOnlyMessage,
|
|
248
239
|
resolveChildExecutionStrategy,
|
|
249
|
-
inlineChildScheduledInRowMessage,
|
|
250
240
|
} from './child-execution-strategy';
|
|
251
241
|
|
|
252
242
|
type ResolvedPlayExecutor = (
|
|
@@ -267,6 +257,11 @@ const rowContext = new AsyncLocalStorage<{
|
|
|
267
257
|
tableNamespace?: string;
|
|
268
258
|
rowKey?: string;
|
|
269
259
|
mapScope?: MapExecutionScope;
|
|
260
|
+
/**
|
|
261
|
+
* Per-resolver inline-child fan-width guard. This stays in the async row
|
|
262
|
+
* scope, so it becomes collectible as soon as the resolver settles.
|
|
263
|
+
*/
|
|
264
|
+
inlineChildInvocationNamespaces?: Set<string>;
|
|
270
265
|
}>();
|
|
271
266
|
type InlineCompositionStore = {
|
|
272
267
|
context: PlayContextImpl;
|
|
@@ -307,19 +302,11 @@ const FETCH_TRANSPORT_RETRY_DELAY_MS = 100;
|
|
|
307
302
|
// provider 502; require both the branded page and our development tunnel host.
|
|
308
303
|
const DEEPLINE_DEVELOPER_TUNNEL_ORIGIN_502_PATTERN =
|
|
309
304
|
/\bdeeplinedeveloper\.com\s*\|\s*502\s*:\s*bad gateway\b/i;
|
|
310
|
-
const CHILD_PLAY_LAUNCH_WRITE_CONFLICT_RETRY_DELAYS_MS = [
|
|
311
|
-
50, 100, 200, 400, 800,
|
|
312
|
-
] as const;
|
|
313
|
-
const CHILD_PLAY_LAUNCH_ADMISSION_RETRY_DELAYS_MS = [
|
|
314
|
-
250, 500, 1_000, 2_000, 4_000, 8_000, 8_000, 8_000, 8_000, 8_000, 8_000,
|
|
315
|
-
8_000, 8_000, 8_000, 8_000,
|
|
316
|
-
] as const;
|
|
317
305
|
const NODE_RUNTIME_MAP_VISIBILITY_MAX_ATTEMPTS = 100;
|
|
318
306
|
const NODE_RUNTIME_MAP_VISIBILITY_RETRY_MS = 25;
|
|
319
307
|
// Per-row sanity cap on distinct inline child invocations. `maxPlayCallDepth`
|
|
320
|
-
// (governor) already bounds nesting; this bounds fan-WIDTH from
|
|
321
|
-
//
|
|
322
|
-
// composition namespaces. Well above any legitimate authored per-row fan-out.
|
|
308
|
+
// (governor) already bounds nesting; this bounds fan-WIDTH from one active row
|
|
309
|
+
// resolver. The dedupe set is row-local, never retained for the full run.
|
|
323
310
|
const MAX_INLINE_CHILD_INVOCATIONS_PER_ROW = 512;
|
|
324
311
|
// Bound the retained failure detail carried in parent aggregates so a fully
|
|
325
312
|
// failing large map cannot grow the progress event without limit.
|
|
@@ -354,44 +341,6 @@ export function chunkArrayForReceiptRequest<T>(items: readonly T[]): T[][] {
|
|
|
354
341
|
type SafeFetchModule = typeof import('@shared_libs/security/safe-fetch');
|
|
355
342
|
let safeFetchModule: Promise<SafeFetchModule> | null = null;
|
|
356
343
|
|
|
357
|
-
function sleepTransientRuntimeMs(ms: number): Promise<void> {
|
|
358
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function isConvexWriteConflictMessage(message: string): boolean {
|
|
362
|
-
return (
|
|
363
|
-
message.includes('OptimisticConcurrencyControlFailure') ||
|
|
364
|
-
/changed while this mutation was being run/i.test(message) ||
|
|
365
|
-
/write conflict/i.test(message)
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function isChildPlayLaunchAdmissionMessage(message: string): boolean {
|
|
370
|
-
return /too many play runs (?:are )?(?:starting|active)/i.test(message);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function childPlayLaunchRetryDelayMs(input: {
|
|
374
|
-
status: number;
|
|
375
|
-
message: string;
|
|
376
|
-
attempt: number;
|
|
377
|
-
}): number | null {
|
|
378
|
-
if (
|
|
379
|
-
input.status === 429 &&
|
|
380
|
-
isChildPlayLaunchAdmissionMessage(input.message)
|
|
381
|
-
) {
|
|
382
|
-
return CHILD_PLAY_LAUNCH_ADMISSION_RETRY_DELAYS_MS[input.attempt] ?? null;
|
|
383
|
-
}
|
|
384
|
-
if (
|
|
385
|
-
![409, 429, 500, 502, 503, 504].includes(input.status) ||
|
|
386
|
-
!isConvexWriteConflictMessage(input.message)
|
|
387
|
-
) {
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
return (
|
|
391
|
-
CHILD_PLAY_LAUNCH_WRITE_CONFLICT_RETRY_DELAYS_MS[input.attempt] ?? null
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
344
|
export async function waitForNodeRuntimeMapRowsVisible(input: {
|
|
396
345
|
mapName: string;
|
|
397
346
|
tableNamespace: string;
|
|
@@ -1241,13 +1190,6 @@ export class PlayContextImpl {
|
|
|
1241
1190
|
eventKey: string;
|
|
1242
1191
|
timeoutMs: number;
|
|
1243
1192
|
}> = [];
|
|
1244
|
-
private pendingChildPlayBoundaries: Array<{
|
|
1245
|
-
boundaryId: string;
|
|
1246
|
-
childRunId: string;
|
|
1247
|
-
childPlayName: string;
|
|
1248
|
-
}> = [];
|
|
1249
|
-
private activeScheduledChildPlayCalls = 0;
|
|
1250
|
-
private scheduledChildPlayQuiescenceWaiters: Array<() => void> = [];
|
|
1251
1193
|
private processedRowCount = 0;
|
|
1252
1194
|
private sleepBoundaryIndex = 0;
|
|
1253
1195
|
private fetchCallIndex = 0;
|
|
@@ -1255,19 +1197,6 @@ export class PlayContextImpl {
|
|
|
1255
1197
|
createSecretRedactionContext();
|
|
1256
1198
|
private mapInvocationIndex = 0;
|
|
1257
1199
|
private readonly stepCallIndexByKey = new Map<string, number>();
|
|
1258
|
-
/**
|
|
1259
|
-
* Registry of distinct inline child invocation namespaces seen by this
|
|
1260
|
-
* context. Inline child composition is a function call, not a run; each
|
|
1261
|
-
* invocation's namespace (`child:<playName>#<callKey>[@<rowScope>]`) scopes
|
|
1262
|
-
* its receipts under the parent run. See ADR 0013.
|
|
1263
|
-
*/
|
|
1264
|
-
private readonly inlineChildInvocationOrdinals = new Map<string, number>();
|
|
1265
|
-
/**
|
|
1266
|
-
* Distinct inline child invocations per row scope (top-level counts as one
|
|
1267
|
-
* scope). Backs the per-row fan-width sanity cap so a resolver looping
|
|
1268
|
-
* `ctx.runPlay` unbounded fails loudly instead of leaking namespaces.
|
|
1269
|
-
*/
|
|
1270
|
-
private readonly inlineChildInvocationsPerRow = new Map<string, number>();
|
|
1271
1200
|
/**
|
|
1272
1201
|
* Parent-level inline-child aggregates. Maintained only by the single-writer
|
|
1273
1202
|
* progress path (never per child) so concurrent fan-out cannot contend. See
|
|
@@ -1478,6 +1407,15 @@ export class PlayContextImpl {
|
|
|
1478
1407
|
return active?.context === this ? active : null;
|
|
1479
1408
|
}
|
|
1480
1409
|
|
|
1410
|
+
/** Enforce the scalar inline-child contract at dynamic API boundaries. */
|
|
1411
|
+
private assertInlineChildContract(
|
|
1412
|
+
reason: 'dataset_child' | 'suspending_child',
|
|
1413
|
+
): void {
|
|
1414
|
+
const composition = this.activeInlineComposition;
|
|
1415
|
+
if (!composition) return;
|
|
1416
|
+
throw new Error(ctxRunPlayInlineOnlyMessage(composition.playName, reason));
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1481
1419
|
private get currentExecutionScope(): RunExecutionScope {
|
|
1482
1420
|
return this.activeInlineComposition?.executionScope ?? this.executionScope;
|
|
1483
1421
|
}
|
|
@@ -1499,100 +1437,9 @@ export class PlayContextImpl {
|
|
|
1499
1437
|
return active ? active.staticPipeline : this.#options.staticPipeline;
|
|
1500
1438
|
}
|
|
1501
1439
|
|
|
1502
|
-
private shouldLaunchChildPlaysThroughScheduler(): boolean {
|
|
1503
|
-
return (
|
|
1504
|
-
this.#options.requireSharedRateState === true &&
|
|
1505
|
-
this.#options.durableBoundaries === true
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
private childPlayBoundaryId(childRunId: string): string {
|
|
1510
|
-
return this.durableBoundaryId(`runPlay:${childRunId}`);
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
private recordPendingChildPlayBoundary(input: {
|
|
1514
|
-
boundaryId: string;
|
|
1515
|
-
childRunId: string;
|
|
1516
|
-
childPlayName: string;
|
|
1517
|
-
}): void {
|
|
1518
|
-
this.pendingChildPlayBoundaries.push(input);
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
private beginScheduledChildPlayCall(): { release: () => void } {
|
|
1522
|
-
this.activeScheduledChildPlayCalls += 1;
|
|
1523
|
-
let released = false;
|
|
1524
|
-
return {
|
|
1525
|
-
release: () => {
|
|
1526
|
-
if (released) return;
|
|
1527
|
-
released = true;
|
|
1528
|
-
this.activeScheduledChildPlayCalls = Math.max(
|
|
1529
|
-
0,
|
|
1530
|
-
this.activeScheduledChildPlayCalls - 1,
|
|
1531
|
-
);
|
|
1532
|
-
if (this.activeScheduledChildPlayCalls !== 0) return;
|
|
1533
|
-
for (const resolve of this.scheduledChildPlayQuiescenceWaiters.splice(
|
|
1534
|
-
0,
|
|
1535
|
-
)) {
|
|
1536
|
-
resolve();
|
|
1537
|
-
}
|
|
1538
|
-
},
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
private async waitForScheduledChildPlayQuiescence(): Promise<void> {
|
|
1543
|
-
if (this.activeScheduledChildPlayCalls === 0) return;
|
|
1544
|
-
await new Promise<void>((resolve) => {
|
|
1545
|
-
this.scheduledChildPlayQuiescenceWaiters.push(resolve);
|
|
1546
|
-
});
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
private consumePendingChildPlaySuspension(): PlayExecutionSuspension | null {
|
|
1550
|
-
if (this.pendingChildPlayBoundaries.length === 0) return null;
|
|
1551
|
-
const boundaries = [
|
|
1552
|
-
...new Map(
|
|
1553
|
-
this.pendingChildPlayBoundaries.map((boundary) => [
|
|
1554
|
-
boundary.boundaryId,
|
|
1555
|
-
boundary,
|
|
1556
|
-
]),
|
|
1557
|
-
).values(),
|
|
1558
|
-
];
|
|
1559
|
-
this.pendingChildPlayBoundaries = [];
|
|
1560
|
-
if (boundaries.length === 1) {
|
|
1561
|
-
const boundary = boundaries[0]!;
|
|
1562
|
-
return {
|
|
1563
|
-
kind: 'child_play',
|
|
1564
|
-
boundaryId: boundary.boundaryId,
|
|
1565
|
-
childRunId: boundary.childRunId,
|
|
1566
|
-
childPlayName: boundary.childPlayName,
|
|
1567
|
-
};
|
|
1568
|
-
}
|
|
1569
|
-
return {
|
|
1570
|
-
kind: 'child_play_batch',
|
|
1571
|
-
boundaries,
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
private childPlayBoundaryOutput<TOutput>(input: {
|
|
1576
|
-
boundaryId: string;
|
|
1577
|
-
childRunId: string;
|
|
1578
|
-
childPlayName: string;
|
|
1579
|
-
}): { found: true; output: TOutput } | { found: false } {
|
|
1580
|
-
const existing = this.checkpoint.resolvedBoundaries?.[input.boundaryId];
|
|
1581
|
-
if (!existing || existing.kind !== 'child_play') return { found: false };
|
|
1582
|
-
if (existing.childRunId !== input.childRunId) return { found: false };
|
|
1583
|
-
if (existing.status === 'completed') {
|
|
1584
|
-
return { found: true, output: existing.output as TOutput };
|
|
1585
|
-
}
|
|
1586
|
-
const message =
|
|
1587
|
-
typeof existing.error === 'string' && existing.error.trim()
|
|
1588
|
-
? existing.error.trim()
|
|
1589
|
-
: `Child play ${input.childPlayName} (${input.childRunId}) ${existing.status}.`;
|
|
1590
|
-
throw new Error(message);
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
1440
|
private recordPlayCallStep(input: {
|
|
1594
1441
|
playId: string;
|
|
1595
|
-
execution?: 'inline'
|
|
1442
|
+
execution?: 'inline';
|
|
1596
1443
|
description?: string | null;
|
|
1597
1444
|
nestedSteps?: PlayStep[];
|
|
1598
1445
|
}): void {
|
|
@@ -1616,186 +1463,6 @@ export class PlayContextImpl {
|
|
|
1616
1463
|
);
|
|
1617
1464
|
}
|
|
1618
1465
|
|
|
1619
|
-
private async runtimeApiHeaders(): Promise<Record<string, string>> {
|
|
1620
|
-
if (!this.#options.executorToken?.trim()) {
|
|
1621
|
-
throw new Error(
|
|
1622
|
-
'ctx.runPlay scheduled child launch requires an executor token.',
|
|
1623
|
-
);
|
|
1624
|
-
}
|
|
1625
|
-
return {
|
|
1626
|
-
Authorization: `Bearer ${this.#options.executorToken.trim()}`,
|
|
1627
|
-
'Content-Type': 'application/json',
|
|
1628
|
-
...(this.#options.runtimeSchedulerSchema?.trim()
|
|
1629
|
-
? {
|
|
1630
|
-
[RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER]:
|
|
1631
|
-
this.#options.runtimeSchedulerSchema.trim(),
|
|
1632
|
-
[SYNTHETIC_RUN_HEADER]: '1',
|
|
1633
|
-
}
|
|
1634
|
-
: {}),
|
|
1635
|
-
// Defense-in-depth: pin the child to the parent worker's release lane so
|
|
1636
|
-
// a child always finishes on the release that launched its parent, even
|
|
1637
|
-
// if the resolver default (schema-derived) would drift. The run route
|
|
1638
|
-
// honors this only for internal ctx.runPlay child launches.
|
|
1639
|
-
...(this.#options.absurdReleaseId?.trim()
|
|
1640
|
-
? {
|
|
1641
|
-
[ABSURD_RELEASE_OVERRIDE_HEADER]:
|
|
1642
|
-
this.#options.absurdReleaseId.trim(),
|
|
1643
|
-
}
|
|
1644
|
-
: {}),
|
|
1645
|
-
...(await this.vercelProtectionHeaders()),
|
|
1646
|
-
};
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
private runtimeApiBaseUrl(): string {
|
|
1650
|
-
const baseUrl = this.#options.baseUrl?.trim();
|
|
1651
|
-
if (!baseUrl) {
|
|
1652
|
-
throw new Error('ctx.runPlay scheduled child launch requires baseUrl.');
|
|
1653
|
-
}
|
|
1654
|
-
return baseUrl.replace(/\/$/, '');
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
private async launchScheduledChildPlay(input: {
|
|
1658
|
-
key: string;
|
|
1659
|
-
childRunId: string;
|
|
1660
|
-
childPlayName: string;
|
|
1661
|
-
childGovernance: GovernanceSnapshot;
|
|
1662
|
-
childInput: Record<string, unknown>;
|
|
1663
|
-
description?: string | null;
|
|
1664
|
-
}): Promise<void> {
|
|
1665
|
-
const parentGovernance = this.currentGovernance;
|
|
1666
|
-
const parentPlayName =
|
|
1667
|
-
parentGovernance.currentPlayId ||
|
|
1668
|
-
this.currentPlayName ||
|
|
1669
|
-
this.#options.playId ||
|
|
1670
|
-
'anonymous-play';
|
|
1671
|
-
const governance: PlayCallGovernanceSnapshot = {
|
|
1672
|
-
rootRunId: parentGovernance.rootRunId,
|
|
1673
|
-
parentRunId: parentGovernance.currentRunId,
|
|
1674
|
-
parentPlayName,
|
|
1675
|
-
key: input.key,
|
|
1676
|
-
ancestryPlayIds: [...parentGovernance.ancestryPlayIds],
|
|
1677
|
-
callDepth: input.childGovernance.callDepth,
|
|
1678
|
-
playCallCount: input.childGovernance.playCallCount,
|
|
1679
|
-
toolCallCount: input.childGovernance.toolCallCount,
|
|
1680
|
-
retryCount: input.childGovernance.retryCount,
|
|
1681
|
-
descendantCount: input.childGovernance.descendantCount,
|
|
1682
|
-
waterfallStepExecutions: input.childGovernance.waterfallStepExecutions,
|
|
1683
|
-
};
|
|
1684
|
-
const requestBody = JSON.stringify({
|
|
1685
|
-
name: input.childPlayName,
|
|
1686
|
-
workflowId: input.childRunId,
|
|
1687
|
-
input: input.childInput,
|
|
1688
|
-
// Inherit the parent's scheduler profile so an `absurd` parent launches
|
|
1689
|
-
// its child on the same scheduler. Absent threading defaults to absurd.
|
|
1690
|
-
profile: this.#options.childRunProfile?.trim() || 'absurd',
|
|
1691
|
-
waitForCompletionMs: 0,
|
|
1692
|
-
internalRunPlay: {
|
|
1693
|
-
...governance,
|
|
1694
|
-
description: input.description ?? null,
|
|
1695
|
-
},
|
|
1696
|
-
});
|
|
1697
|
-
for (let attempt = 0; ; attempt += 1) {
|
|
1698
|
-
const response = await fetch(
|
|
1699
|
-
`${this.runtimeApiBaseUrl()}/api/v2/plays/run`,
|
|
1700
|
-
{
|
|
1701
|
-
method: 'POST',
|
|
1702
|
-
headers: await this.runtimeApiHeaders(),
|
|
1703
|
-
body: requestBody,
|
|
1704
|
-
},
|
|
1705
|
-
);
|
|
1706
|
-
if (response.ok) {
|
|
1707
|
-
if (attempt > 0) {
|
|
1708
|
-
this.log(
|
|
1709
|
-
`ctx.runPlay(${input.key}): child launch recovered after ${attempt} retry(s)`,
|
|
1710
|
-
);
|
|
1711
|
-
}
|
|
1712
|
-
return;
|
|
1713
|
-
}
|
|
1714
|
-
const body = (await response.json().catch(() => null)) as {
|
|
1715
|
-
error?: unknown;
|
|
1716
|
-
} | null;
|
|
1717
|
-
const message =
|
|
1718
|
-
typeof body?.error === 'string' && body.error.trim()
|
|
1719
|
-
? body.error.trim()
|
|
1720
|
-
: response.statusText;
|
|
1721
|
-
const retryDelayMs = childPlayLaunchRetryDelayMs({
|
|
1722
|
-
status: response.status,
|
|
1723
|
-
message,
|
|
1724
|
-
attempt,
|
|
1725
|
-
});
|
|
1726
|
-
if (retryDelayMs === null) {
|
|
1727
|
-
throw new Error(
|
|
1728
|
-
`ctx.runPlay(${input.key}) failed to launch child play "${input.childPlayName}": ${message} (status ${response.status}).`,
|
|
1729
|
-
);
|
|
1730
|
-
}
|
|
1731
|
-
const retryReason =
|
|
1732
|
-
response.status === 429 && isChildPlayLaunchAdmissionMessage(message)
|
|
1733
|
-
? 'start admission backpressure'
|
|
1734
|
-
: 'transient write conflict';
|
|
1735
|
-
this.log(
|
|
1736
|
-
`ctx.runPlay(${input.key}): retrying child launch after ${retryReason} (attempt ${attempt + 1})`,
|
|
1737
|
-
);
|
|
1738
|
-
await sleepTransientRuntimeMs(retryDelayMs);
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
private async readScheduledChildTerminal<TOutput>(input: {
|
|
1743
|
-
childRunId: string;
|
|
1744
|
-
childPlayName: string;
|
|
1745
|
-
}): Promise<
|
|
1746
|
-
| { status: 'completed'; output: TOutput }
|
|
1747
|
-
| { status: 'failed' | 'cancelled'; error: string }
|
|
1748
|
-
| null
|
|
1749
|
-
> {
|
|
1750
|
-
const response = await fetch(
|
|
1751
|
-
`${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_COMPAT_PATH}`,
|
|
1752
|
-
{
|
|
1753
|
-
method: 'POST',
|
|
1754
|
-
headers: await this.runtimeApiHeaders(),
|
|
1755
|
-
body: JSON.stringify({
|
|
1756
|
-
action: 'read_child_run_terminal_snapshot',
|
|
1757
|
-
parentRunId: this.currentGovernance.currentRunId,
|
|
1758
|
-
childRunId: input.childRunId,
|
|
1759
|
-
childPlayName: input.childPlayName,
|
|
1760
|
-
}),
|
|
1761
|
-
},
|
|
1762
|
-
);
|
|
1763
|
-
const body = (await response.json().catch(() => null)) as {
|
|
1764
|
-
state?: {
|
|
1765
|
-
data?: {
|
|
1766
|
-
status?: unknown;
|
|
1767
|
-
result?: unknown;
|
|
1768
|
-
error?: unknown;
|
|
1769
|
-
};
|
|
1770
|
-
};
|
|
1771
|
-
error?: unknown;
|
|
1772
|
-
} | null;
|
|
1773
|
-
if (!response.ok) {
|
|
1774
|
-
const message =
|
|
1775
|
-
typeof body?.error === 'string' && body.error.trim()
|
|
1776
|
-
? body.error.trim()
|
|
1777
|
-
: response.statusText;
|
|
1778
|
-
throw new Error(
|
|
1779
|
-
`ctx.runPlay failed to read child terminal snapshot for "${input.childPlayName}": ${message} (status ${response.status}).`,
|
|
1780
|
-
);
|
|
1781
|
-
}
|
|
1782
|
-
const data = body?.state?.data;
|
|
1783
|
-
const status = String(data?.status ?? '').toLowerCase();
|
|
1784
|
-
if (status === 'completed') {
|
|
1785
|
-
return { status, output: data?.result as TOutput };
|
|
1786
|
-
}
|
|
1787
|
-
if (status === 'failed' || status === 'cancelled') {
|
|
1788
|
-
return {
|
|
1789
|
-
status,
|
|
1790
|
-
error:
|
|
1791
|
-
typeof data?.error === 'string' && data.error.trim()
|
|
1792
|
-
? data.error.trim()
|
|
1793
|
-
: `Child play ${input.childPlayName} (${input.childRunId}) ${status}.`,
|
|
1794
|
-
};
|
|
1795
|
-
}
|
|
1796
|
-
return null;
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
1466
|
private emitScopedRowUpdate(
|
|
1800
1467
|
key: string | null,
|
|
1801
1468
|
tableNamespace: string | null,
|
|
@@ -3703,6 +3370,7 @@ export class PlayContextImpl {
|
|
|
3703
3370
|
path: string,
|
|
3704
3371
|
_options?: CsvOptions,
|
|
3705
3372
|
): Promise<PlayDataset<Record<string, unknown>>> {
|
|
3373
|
+
this.assertInlineChildContract('dataset_child');
|
|
3706
3374
|
void _options;
|
|
3707
3375
|
// In cloud mode, CSV data is passed in — path is just a label
|
|
3708
3376
|
// The activity loads the actual data before creating the ctx
|
|
@@ -3731,6 +3399,7 @@ export class PlayContextImpl {
|
|
|
3731
3399
|
input?: MapFieldDefinition<T, TColumns> | RuntimeStepProgram,
|
|
3732
3400
|
options?: DatasetOptions<T>,
|
|
3733
3401
|
): RuntimeDatasetBuilder<T> | Promise<PlayDataset<Record<string, unknown>>> {
|
|
3402
|
+
this.assertInlineChildContract('dataset_child');
|
|
3734
3403
|
if (rowContext.getStore()) {
|
|
3735
3404
|
throw new Error(
|
|
3736
3405
|
'Nested ctx.dataset() is not supported. Flatten your fields into one dataset, or keep custom per-row logic inside a single column.',
|
|
@@ -6319,13 +5988,6 @@ export class PlayContextImpl {
|
|
|
6319
5988
|
this.toolCallQueue.length === 0 &&
|
|
6320
5989
|
inFlightToolExecutions.size === 0
|
|
6321
5990
|
) {
|
|
6322
|
-
if (!this.activeDatasetStep) {
|
|
6323
|
-
const childPlaySuspension =
|
|
6324
|
-
this.consumePendingChildPlaySuspension();
|
|
6325
|
-
if (childPlaySuspension) {
|
|
6326
|
-
throw new PlayExecutionSuspendedError(childPlaySuspension);
|
|
6327
|
-
}
|
|
6328
|
-
}
|
|
6329
5991
|
break;
|
|
6330
5992
|
}
|
|
6331
5993
|
|
|
@@ -6727,6 +6389,7 @@ export class PlayContextImpl {
|
|
|
6727
6389
|
input: Record<string, unknown>,
|
|
6728
6390
|
handler: IntegrationEventWaitHandler,
|
|
6729
6391
|
): Promise<unknown> {
|
|
6392
|
+
this.assertInlineChildContract('suspending_child');
|
|
6730
6393
|
if (!this.#options.durableBoundaries) {
|
|
6731
6394
|
throw new Error(`${toolId} requires durable play boundaries.`);
|
|
6732
6395
|
}
|
|
@@ -6845,328 +6508,136 @@ export class PlayContextImpl {
|
|
|
6845
6508
|
if (!resolvedName.trim()) {
|
|
6846
6509
|
throw new Error('ctx.runPlay(...) requires a resolvable play name.');
|
|
6847
6510
|
}
|
|
6848
|
-
|
|
6511
|
+
if (!this.#options.resolvePlay) {
|
|
6512
|
+
throw new Error(
|
|
6513
|
+
'ctx.runPlay(...) is unavailable because no play resolver was configured.',
|
|
6514
|
+
);
|
|
6515
|
+
}
|
|
6516
|
+
const resolvedPlay = await this.#options.resolvePlay(resolvedName);
|
|
6517
|
+
if (!resolvedPlay) {
|
|
6518
|
+
throw new Error(
|
|
6519
|
+
`Unable to resolve play "${resolvedName}" for ctx.runPlay(...).`,
|
|
6520
|
+
);
|
|
6521
|
+
}
|
|
6522
|
+
const childExecutionDecision = resolveChildExecutionStrategy({
|
|
6523
|
+
pipeline: resolvedPlay.staticPipeline,
|
|
6524
|
+
timeoutMs: options?.timeoutMs,
|
|
6525
|
+
hasExplicitTimeout:
|
|
6526
|
+
options != null && Object.hasOwn(options, 'timeoutMs'),
|
|
6527
|
+
execution: options?.execution,
|
|
6528
|
+
childPlayName: resolvedName,
|
|
6529
|
+
});
|
|
6530
|
+
this.log(
|
|
6531
|
+
`ctx.runPlay(${normalizedKey}): ${childExecutionDecision.strategy} (${childExecutionDecision.reason})`,
|
|
6532
|
+
);
|
|
6849
6533
|
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
const resolvePlay = this.#options.resolvePlay;
|
|
6857
|
-
const resolvedPlay = await resolvePlay(resolvedName);
|
|
6858
|
-
if (!resolvedPlay) {
|
|
6859
|
-
throw new Error(
|
|
6860
|
-
`Unable to resolve play "${resolvedName}" for ctx.runPlay(...).`,
|
|
6861
|
-
);
|
|
6862
|
-
}
|
|
6863
|
-
const childExecutionDecision = resolveChildExecutionStrategy({
|
|
6864
|
-
pipeline: resolvedPlay.staticPipeline,
|
|
6865
|
-
execution: options?.execution,
|
|
6534
|
+
const compositionNamespace = this.inlineChildCompositionNamespace(
|
|
6535
|
+
resolvedName,
|
|
6536
|
+
normalizedKey,
|
|
6537
|
+
);
|
|
6538
|
+
const inlineChildGovernor =
|
|
6539
|
+
await this.currentExecutionGovernor.forkInlineChild({
|
|
6866
6540
|
childPlayName: resolvedName,
|
|
6541
|
+
childRunId: compositionNamespace,
|
|
6867
6542
|
});
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
childExecutionDecision.strategy === 'scheduled' &&
|
|
6892
|
-
childExecutionDecision.reason !== 'missing_static_contract';
|
|
6893
|
-
if (
|
|
6894
|
-
runPlayRowScope &&
|
|
6895
|
-
(provablySchedulingRequired || launchThroughScheduler)
|
|
6896
|
-
) {
|
|
6897
|
-
throw new Error(
|
|
6898
|
-
inlineChildScheduledInRowMessage(
|
|
6899
|
-
resolvedName,
|
|
6900
|
-
runPlayRowScope.tableNamespace ??
|
|
6901
|
-
runPlayRowScope.fieldName ??
|
|
6902
|
-
'dataset',
|
|
6903
|
-
),
|
|
6904
|
-
);
|
|
6543
|
+
const rowStore = rowContext.getStore();
|
|
6544
|
+
const producer = {
|
|
6545
|
+
kind: 'play' as const,
|
|
6546
|
+
id: normalizedKey,
|
|
6547
|
+
playId: resolvedName,
|
|
6548
|
+
displayName: displayNameFromProducerId(resolvedName),
|
|
6549
|
+
runId: compositionNamespace,
|
|
6550
|
+
};
|
|
6551
|
+
try {
|
|
6552
|
+
if (rowStore) {
|
|
6553
|
+
this.emitScopedFieldMetaUpdate({
|
|
6554
|
+
rowId: rowStore.rowId,
|
|
6555
|
+
key: rowStore.rowKey ?? null,
|
|
6556
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
6557
|
+
fieldName: rowStore.fieldName,
|
|
6558
|
+
status: 'running',
|
|
6559
|
+
rowStatus: 'running',
|
|
6560
|
+
stage: resolvedName,
|
|
6561
|
+
provider: 'deepline_native',
|
|
6562
|
+
error: null,
|
|
6563
|
+
producer,
|
|
6564
|
+
dataPatch: {},
|
|
6565
|
+
});
|
|
6905
6566
|
}
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
tableNamespace: runPlayRowScope.tableNamespace ?? null,
|
|
6914
|
-
}
|
|
6915
|
-
: null,
|
|
6916
|
-
});
|
|
6917
|
-
|
|
6918
|
-
const executePlayCall = async (): Promise<TOutput> => {
|
|
6919
|
-
// Scheduled children reserve the parent's durable play/descendant
|
|
6920
|
-
// budgets. Inline scalar children are function composition: their
|
|
6921
|
-
// Governor view changes only local lineage for recursion/depth checks
|
|
6922
|
-
// and delegates tool admission, pacing, and tool budgets to the parent.
|
|
6923
|
-
//
|
|
6924
|
-
// The SCHEDULED substrate still allocates a durable child run id (through
|
|
6925
|
-
// the shared canonical builder so in-process and the workers_edge
|
|
6926
|
-
// coordinator produce the same FORMAT `play/<slug>/run/child-<digest>`).
|
|
6927
|
-
// INLINE scalar children are pure function composition (ADR 0013): they
|
|
6928
|
-
// carry NO durable run identity — no child run id, no playRuns doc, no
|
|
6929
|
-
// settlement. Their only identity is a replay-stable, call-site scoped
|
|
6930
|
-
// composition namespace (`child:<playName>#<ordinal>`) used to scope tool
|
|
6931
|
-
// receipts under the PARENT run. See child-composition-namespace below.
|
|
6932
|
-
const parentGovernor = this.currentExecutionGovernor;
|
|
6933
|
-
const inlineChildGovernor = launchThroughScheduler
|
|
6934
|
-
? null
|
|
6935
|
-
: await parentGovernor.forkInlineChild({
|
|
6936
|
-
childPlayName: resolvedName,
|
|
6937
|
-
childRunId: this.inlineChildCompositionNamespace(
|
|
6938
|
-
resolvedName,
|
|
6939
|
-
normalizedKey,
|
|
6940
|
-
),
|
|
6941
|
-
});
|
|
6942
|
-
const childGovernance = launchThroughScheduler
|
|
6943
|
-
? await parentGovernor.forkChild({
|
|
6944
|
-
childPlayName: resolvedName,
|
|
6945
|
-
childRunId: buildChildRunId({
|
|
6946
|
-
childPlayName: resolvedName,
|
|
6947
|
-
parentRunId: this.currentRunId,
|
|
6948
|
-
parentPlayName: this.#options.playName,
|
|
6949
|
-
key: normalizedKey,
|
|
6950
|
-
runPlaySemanticKey: runPlayInvocationScope,
|
|
6951
|
-
input,
|
|
6952
|
-
graphHash: null,
|
|
6953
|
-
}),
|
|
6954
|
-
})
|
|
6955
|
-
: inlineChildGovernor!.snapshot();
|
|
6956
|
-
const childPlaySlot = launchThroughScheduler
|
|
6957
|
-
? await parentGovernor.acquireChildSubmitSlot()
|
|
6958
|
-
: null;
|
|
6959
|
-
const rowStore = rowContext.getStore();
|
|
6960
|
-
const producer = {
|
|
6961
|
-
kind: 'play' as const,
|
|
6962
|
-
id: normalizedKey,
|
|
6567
|
+
// Inline composition has no child run, launch request, terminal poll, or
|
|
6568
|
+
// scheduler slot. It shares the caller's execution and tool governors.
|
|
6569
|
+
const childExecutionScope = deriveChildRunExecutionScope(
|
|
6570
|
+
this.currentExecutionScope,
|
|
6571
|
+
{
|
|
6572
|
+
placement: 'inline',
|
|
6573
|
+
runId: compositionNamespace,
|
|
6963
6574
|
playId: resolvedName,
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
tableNamespace: rowStore.tableNamespace ?? null,
|
|
7000
|
-
fieldName: rowStore.fieldName,
|
|
7001
|
-
status: 'completed',
|
|
7002
|
-
rowStatus: 'running',
|
|
7003
|
-
stage: resolvedName,
|
|
7004
|
-
provider: 'deepline_native',
|
|
7005
|
-
error: null,
|
|
7006
|
-
producer,
|
|
7007
|
-
dataPatch: {},
|
|
7008
|
-
});
|
|
7009
|
-
}
|
|
7010
|
-
this.recordPlayCallStep({
|
|
7011
|
-
playId: resolvedName,
|
|
7012
|
-
execution: options?.execution,
|
|
7013
|
-
description: options?.description,
|
|
7014
|
-
});
|
|
7015
|
-
return checkpointResult.output;
|
|
7016
|
-
}
|
|
7017
|
-
|
|
7018
|
-
await this.launchScheduledChildPlay({
|
|
7019
|
-
key: normalizedKey,
|
|
7020
|
-
childRunId: childGovernance.currentRunId,
|
|
7021
|
-
childPlayName: resolvedName,
|
|
7022
|
-
childGovernance,
|
|
7023
|
-
childInput: input,
|
|
7024
|
-
description: options?.description ?? null,
|
|
7025
|
-
});
|
|
7026
|
-
childPlaySlot?.release();
|
|
7027
|
-
const terminal = await this.readScheduledChildTerminal<TOutput>({
|
|
7028
|
-
childRunId: childGovernance.currentRunId,
|
|
7029
|
-
childPlayName: resolvedName,
|
|
7030
|
-
});
|
|
7031
|
-
if (terminal?.status === 'completed') {
|
|
7032
|
-
this.checkpoint.resolvedBoundaries = {
|
|
7033
|
-
...(this.checkpoint.resolvedBoundaries ?? {}),
|
|
7034
|
-
[boundaryId]: {
|
|
7035
|
-
kind: 'child_play',
|
|
7036
|
-
childRunId: childGovernance.currentRunId,
|
|
7037
|
-
childPlayName: resolvedName,
|
|
7038
|
-
status: 'completed',
|
|
7039
|
-
output: terminal.output,
|
|
7040
|
-
completedAt: Date.now(),
|
|
7041
|
-
},
|
|
7042
|
-
};
|
|
7043
|
-
if (rowStore) {
|
|
7044
|
-
this.emitScopedFieldMetaUpdate({
|
|
7045
|
-
rowId: rowStore.rowId,
|
|
7046
|
-
key: rowStore.rowKey ?? null,
|
|
7047
|
-
tableNamespace: rowStore.tableNamespace ?? null,
|
|
7048
|
-
fieldName: rowStore.fieldName,
|
|
7049
|
-
status: 'completed',
|
|
7050
|
-
rowStatus: 'running',
|
|
7051
|
-
stage: resolvedName,
|
|
7052
|
-
provider: 'deepline_native',
|
|
7053
|
-
error: null,
|
|
7054
|
-
producer,
|
|
7055
|
-
dataPatch: {},
|
|
7056
|
-
});
|
|
7057
|
-
}
|
|
7058
|
-
this.recordPlayCallStep({
|
|
7059
|
-
playId: resolvedName,
|
|
7060
|
-
execution: options?.execution,
|
|
7061
|
-
description: options?.description,
|
|
7062
|
-
});
|
|
7063
|
-
return terminal.output;
|
|
7064
|
-
}
|
|
7065
|
-
if (terminal) {
|
|
7066
|
-
throw new Error(terminal.error);
|
|
7067
|
-
}
|
|
7068
|
-
|
|
7069
|
-
const suspension = {
|
|
7070
|
-
kind: 'child_play',
|
|
7071
|
-
boundaryId,
|
|
7072
|
-
childRunId: childGovernance.currentRunId,
|
|
7073
|
-
childPlayName: resolvedName,
|
|
7074
|
-
} as const;
|
|
7075
|
-
this.recordPendingChildPlayBoundary(suspension);
|
|
7076
|
-
scheduledChildPlayCall?.release();
|
|
7077
|
-
await this.waitForScheduledChildPlayQuiescence();
|
|
7078
|
-
throw new PlayExecutionSuspendedError(
|
|
7079
|
-
this.consumePendingChildPlaySuspension() ?? suspension,
|
|
7080
|
-
);
|
|
7081
|
-
}
|
|
7082
|
-
// Inline child = pure function composition (ADR 0013). No durable
|
|
7083
|
-
// child run: no playRuns doc, no start/settle round-trip, no
|
|
7084
|
-
// OptimisticConcurrencyControlFailure fan-out. The child's tool
|
|
7085
|
-
// receipts still land durably under the PARENT run (the inline
|
|
7086
|
-
// execution scope keeps the parent's receipt.ownerRunId) scoped by the
|
|
7087
|
-
// replay-stable composition namespace (which also enforces the
|
|
7088
|
-
// per-row inline-invocation sanity cap at creation).
|
|
7089
|
-
const compositionNamespace = childGovernance.currentRunId;
|
|
7090
|
-
const childExecutionScope = deriveChildRunExecutionScope(
|
|
7091
|
-
this.currentExecutionScope,
|
|
7092
|
-
{
|
|
7093
|
-
placement: 'inline',
|
|
7094
|
-
runId: compositionNamespace,
|
|
7095
|
-
playId: resolvedName,
|
|
7096
|
-
receiptNamespace: compositionNamespace,
|
|
7097
|
-
},
|
|
7098
|
-
);
|
|
7099
|
-
this.inlineChildAggregates.total += 1;
|
|
7100
|
-
try {
|
|
7101
|
-
// A scalar child is a normal function call on the parent's context.
|
|
7102
|
-
// Async-local identity changes receipt and recursion scope without
|
|
7103
|
-
// allocating another PlayContext, tool queue, drain loop, resource
|
|
7104
|
-
// governor, or receipt client. Calls from every concurrent child
|
|
7105
|
-
// therefore reach the same proven parent batching path.
|
|
7106
|
-
const result = await inlineCompositionContext.run(
|
|
7107
|
-
{
|
|
7108
|
-
context: this,
|
|
7109
|
-
executionScope: childExecutionScope,
|
|
7110
|
-
governor: inlineChildGovernor!,
|
|
7111
|
-
playName: resolvedName,
|
|
7112
|
-
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
7113
|
-
},
|
|
7114
|
-
() => this.executeResolvedPlay(resolvedPlay, this, input),
|
|
7115
|
-
);
|
|
7116
|
-
this.inlineChildAggregates.ok += 1;
|
|
7117
|
-
if (rowStore) {
|
|
7118
|
-
this.emitScopedFieldMetaUpdate({
|
|
7119
|
-
rowId: rowStore.rowId,
|
|
7120
|
-
key: rowStore.rowKey ?? null,
|
|
7121
|
-
tableNamespace: rowStore.tableNamespace ?? null,
|
|
7122
|
-
fieldName: rowStore.fieldName,
|
|
7123
|
-
status: 'completed',
|
|
7124
|
-
rowStatus: 'running',
|
|
7125
|
-
stage: resolvedName,
|
|
7126
|
-
provider: 'deepline_native',
|
|
7127
|
-
error: null,
|
|
7128
|
-
producer,
|
|
7129
|
-
dataPatch: {},
|
|
7130
|
-
});
|
|
7131
|
-
}
|
|
7132
|
-
this.recordPlayCallStep({
|
|
7133
|
-
playId: resolvedName,
|
|
7134
|
-
execution: options?.execution,
|
|
7135
|
-
description: options?.description,
|
|
7136
|
-
});
|
|
7137
|
-
return result as TOutput;
|
|
7138
|
-
} catch (childError) {
|
|
7139
|
-
this.recordInlineChildFailure(resolvedName, childError);
|
|
7140
|
-
throw childError;
|
|
7141
|
-
}
|
|
7142
|
-
} catch (error) {
|
|
7143
|
-
if (isPlayExecutionSuspendedError(error)) {
|
|
7144
|
-
throw error;
|
|
7145
|
-
}
|
|
7146
|
-
if (rowStore) {
|
|
7147
|
-
this.emitScopedFieldMetaUpdate({
|
|
7148
|
-
rowId: rowStore.rowId,
|
|
7149
|
-
key: rowStore.rowKey ?? null,
|
|
7150
|
-
tableNamespace: rowStore.tableNamespace ?? null,
|
|
7151
|
-
fieldName: rowStore.fieldName,
|
|
7152
|
-
status: 'failed',
|
|
7153
|
-
rowStatus: 'running',
|
|
7154
|
-
stage: resolvedName,
|
|
7155
|
-
provider: 'deepline_native',
|
|
7156
|
-
error: this.formatRuntimeError(error),
|
|
7157
|
-
producer,
|
|
7158
|
-
dataPatch: {},
|
|
7159
|
-
});
|
|
7160
|
-
}
|
|
7161
|
-
throw error;
|
|
7162
|
-
} finally {
|
|
7163
|
-
childPlaySlot?.release();
|
|
6575
|
+
receiptNamespace: compositionNamespace,
|
|
6576
|
+
},
|
|
6577
|
+
);
|
|
6578
|
+
this.inlineChildAggregates.total += 1;
|
|
6579
|
+
try {
|
|
6580
|
+
// A scalar child is a normal function call on the parent's context.
|
|
6581
|
+
// Async-local identity changes receipt and recursion scope without
|
|
6582
|
+
// allocating another PlayContext, tool queue, drain loop, resource
|
|
6583
|
+
// governor, or receipt client. Calls from every concurrent child
|
|
6584
|
+
// therefore reach the same proven parent batching path.
|
|
6585
|
+
const result = await inlineCompositionContext.run(
|
|
6586
|
+
{
|
|
6587
|
+
context: this,
|
|
6588
|
+
executionScope: childExecutionScope,
|
|
6589
|
+
governor: inlineChildGovernor,
|
|
6590
|
+
playName: resolvedName,
|
|
6591
|
+
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
6592
|
+
},
|
|
6593
|
+
() => this.executeResolvedPlay(resolvedPlay, this, input),
|
|
6594
|
+
);
|
|
6595
|
+
this.inlineChildAggregates.ok += 1;
|
|
6596
|
+
if (rowStore) {
|
|
6597
|
+
this.emitScopedFieldMetaUpdate({
|
|
6598
|
+
rowId: rowStore.rowId,
|
|
6599
|
+
key: rowStore.rowKey ?? null,
|
|
6600
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
6601
|
+
fieldName: rowStore.fieldName,
|
|
6602
|
+
status: 'completed',
|
|
6603
|
+
rowStatus: 'running',
|
|
6604
|
+
stage: resolvedName,
|
|
6605
|
+
provider: 'deepline_native',
|
|
6606
|
+
error: null,
|
|
6607
|
+
producer,
|
|
6608
|
+
dataPatch: {},
|
|
6609
|
+
});
|
|
7164
6610
|
}
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
6611
|
+
this.recordPlayCallStep({
|
|
6612
|
+
playId: resolvedName,
|
|
6613
|
+
execution: options?.execution === 'inline' ? 'inline' : undefined,
|
|
6614
|
+
description: options?.description,
|
|
6615
|
+
});
|
|
6616
|
+
return result as TOutput;
|
|
6617
|
+
} catch (childError) {
|
|
6618
|
+
this.recordInlineChildFailure(resolvedName, childError);
|
|
6619
|
+
throw childError;
|
|
6620
|
+
}
|
|
6621
|
+
} catch (error) {
|
|
6622
|
+
if (isPlayExecutionSuspendedError(error)) {
|
|
6623
|
+
throw error;
|
|
6624
|
+
}
|
|
6625
|
+
if (rowStore) {
|
|
6626
|
+
this.emitScopedFieldMetaUpdate({
|
|
6627
|
+
rowId: rowStore.rowId,
|
|
6628
|
+
key: rowStore.rowKey ?? null,
|
|
6629
|
+
tableNamespace: rowStore.tableNamespace ?? null,
|
|
6630
|
+
fieldName: rowStore.fieldName,
|
|
6631
|
+
status: 'failed',
|
|
6632
|
+
rowStatus: 'running',
|
|
6633
|
+
stage: resolvedName,
|
|
6634
|
+
provider: 'deepline_native',
|
|
6635
|
+
error: this.formatRuntimeError(error),
|
|
6636
|
+
producer,
|
|
6637
|
+
dataPatch: {},
|
|
6638
|
+
});
|
|
6639
|
+
}
|
|
6640
|
+
throw error;
|
|
7170
6641
|
}
|
|
7171
6642
|
}
|
|
7172
6643
|
|
|
@@ -7200,25 +6671,17 @@ export class PlayContextImpl {
|
|
|
7200
6671
|
const namespace = `child:${childPlayName}#${normalizedKey}${
|
|
7201
6672
|
rowScopeKey ? `@${rowScopeKey}` : ''
|
|
7202
6673
|
}`;
|
|
7203
|
-
if (
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
const rowInvocationCount =
|
|
7209
|
-
(this.inlineChildInvocationsPerRow.get(rowScopeKey) ?? 0) + 1;
|
|
7210
|
-
if (rowInvocationCount > MAX_INLINE_CHILD_INVOCATIONS_PER_ROW) {
|
|
6674
|
+
if (rowScope) {
|
|
6675
|
+
const namespaces =
|
|
6676
|
+
rowScope.inlineChildInvocationNamespaces ??= new Set<string>();
|
|
6677
|
+
namespaces.add(namespace);
|
|
6678
|
+
if (namespaces.size > MAX_INLINE_CHILD_INVOCATIONS_PER_ROW) {
|
|
7211
6679
|
throw new Error(
|
|
7212
6680
|
`ctx.runPlay("${childPlayName}") exceeded the inline child invocation cap ` +
|
|
7213
6681
|
`(${MAX_INLINE_CHILD_INVOCATIONS_PER_ROW} per row). A resolver is calling ` +
|
|
7214
6682
|
'runPlay in an unbounded loop; give the batch ONE dataset child at play level instead.',
|
|
7215
6683
|
);
|
|
7216
6684
|
}
|
|
7217
|
-
this.inlineChildInvocationsPerRow.set(rowScopeKey, rowInvocationCount);
|
|
7218
|
-
this.inlineChildInvocationOrdinals.set(
|
|
7219
|
-
namespace,
|
|
7220
|
-
this.inlineChildInvocationOrdinals.size,
|
|
7221
|
-
);
|
|
7222
6685
|
}
|
|
7223
6686
|
return namespace;
|
|
7224
6687
|
}
|
|
@@ -7306,6 +6769,7 @@ export class PlayContextImpl {
|
|
|
7306
6769
|
}
|
|
7307
6770
|
|
|
7308
6771
|
async sleep(ms: number): Promise<void> {
|
|
6772
|
+
this.assertInlineChildContract('suspending_child');
|
|
7309
6773
|
if (this.#options.durableBoundaries) {
|
|
7310
6774
|
const delayMs = Math.max(0, Math.round(ms));
|
|
7311
6775
|
const boundaryId = this.durableBoundaryId(
|