deepline 0.1.214 → 0.1.216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/apps/play-runner-workers/src/child-manifest-resolver.ts +108 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +85 -10
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-runtime-proxy.ts +34 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +50 -36
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +12 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +4 -0
- package/dist/bundling-sources/sdk/src/client.ts +1 -2
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/sdk/src/types.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +21 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +95 -144
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +14 -3
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +37 -2
- package/dist/bundling-sources/shared_libs/play-runtime/profiles.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +32 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +13 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +5 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-session-execution.ts +66 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +18 -55
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +59 -27
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +3 -2
- package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +11 -1
- package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +10 -0
- package/dist/cli/index.js +72 -21
- package/dist/cli/index.mjs +72 -21
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +7 -6
- package/dist/index.mjs +7 -6
- package/package.json +1 -1
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
PLAY_RUNTIME_CONTRACT,
|
|
39
39
|
PLAY_RUNTIME_CONTRACT_HEADER,
|
|
40
40
|
} from '@shared_libs/play-runtime/runtime-contract';
|
|
41
|
-
import {
|
|
41
|
+
import { PLAY_RUNTIME_API_CURRENT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
|
|
42
42
|
import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '@shared_libs/play-runtime/test-runtime-seams';
|
|
43
43
|
import { vercelProtectionBypassHeaders } from '@shared_libs/play-runtime/vercel-protection';
|
|
44
44
|
|
|
@@ -569,6 +569,19 @@ function summarizeAppRuntimeErrorBody(body: string): string {
|
|
|
569
569
|
return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
function appRuntimeErrorCode(response: Response, body: string): string | null {
|
|
573
|
+
const header = response.headers.get('x-deepline-error-code')?.trim();
|
|
574
|
+
if (header) return header;
|
|
575
|
+
try {
|
|
576
|
+
const parsed = JSON.parse(body) as { code?: unknown };
|
|
577
|
+
return typeof parsed?.code === 'string' && parsed.code.trim()
|
|
578
|
+
? parsed.code.trim()
|
|
579
|
+
: null;
|
|
580
|
+
} catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
572
585
|
export function isTransientAppRuntimeFailureBody(body: string): boolean {
|
|
573
586
|
return /WorkerOverloaded|"\s*code\s*"\s*:\s*"\s*InternalServerError\s*"|Your request couldn't be completed\. Try again later\.|timeout exceeded when trying to connect|timed out|fetch failed|ECONNRESET|ECONNREFUSED|UND_ERR_CONNECT_TIMEOUT|tuple concurrently updated|RUNTIME_SCHEDULER_SATURATED|Runtime scheduler DB pool is saturated|Runtime scheduler DB circuit breaker is open|OptimisticConcurrencyControlFailure|changed while this mutation was being run|Documents read from or written to the .* table changed while this mutation/i.test(
|
|
574
587
|
body,
|
|
@@ -775,7 +788,7 @@ export class AppRuntimeApiTransportError extends Error {
|
|
|
775
788
|
|
|
776
789
|
function resolveAppRuntimeApiUrl(context: WorkerRuntimeApiContext): string {
|
|
777
790
|
const baseUrl = context.baseUrl.trim().replace(/\/$/, '');
|
|
778
|
-
return `${baseUrl}${
|
|
791
|
+
return `${baseUrl}${PLAY_RUNTIME_API_CURRENT_PATH}`;
|
|
779
792
|
}
|
|
780
793
|
|
|
781
794
|
async function postAppRuntimeApi<TResponse>(
|
|
@@ -892,8 +905,13 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
892
905
|
);
|
|
893
906
|
continue;
|
|
894
907
|
}
|
|
908
|
+
const code = appRuntimeErrorCode(response, responseText);
|
|
909
|
+
const requestId = response.headers.get('x-deepline-request-id')?.trim();
|
|
895
910
|
throw new Error(
|
|
896
|
-
`App runtime API ${body.action} failed with status ${response.status}
|
|
911
|
+
`App runtime API ${body.action} failed with status ${response.status}` +
|
|
912
|
+
`${code ? ` code=${code}` : ''}` +
|
|
913
|
+
`${requestId ? ` request_id=${requestId}` : ''}: ` +
|
|
914
|
+
summarizeAppRuntimeErrorBody(responseText),
|
|
897
915
|
);
|
|
898
916
|
}
|
|
899
917
|
|
|
@@ -47,10 +47,7 @@ import {
|
|
|
47
47
|
RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER,
|
|
48
48
|
SYNTHETIC_RUN_HEADER,
|
|
49
49
|
} from './coordinator-headers';
|
|
50
|
-
import {
|
|
51
|
-
PLAY_RUNTIME_API_COMPAT_PATH,
|
|
52
|
-
PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_COMPAT_PATH,
|
|
53
|
-
} from './runtime-api-paths';
|
|
50
|
+
import { PLAY_RUNTIME_API_CURRENT_PATH } from './runtime-api-paths';
|
|
54
51
|
import {
|
|
55
52
|
createRootRunExecutionScope,
|
|
56
53
|
deriveChildRunExecutionScope,
|
|
@@ -262,6 +259,15 @@ const rowContext = new AsyncLocalStorage<{
|
|
|
262
259
|
rowKey?: string;
|
|
263
260
|
mapScope?: MapExecutionScope;
|
|
264
261
|
}>();
|
|
262
|
+
type InlineCompositionStore = {
|
|
263
|
+
context: PlayContextImpl;
|
|
264
|
+
executionScope: RunExecutionScope;
|
|
265
|
+
governor: PlayExecutionGovernor;
|
|
266
|
+
playName: string;
|
|
267
|
+
staticPipeline: ContextOptions['staticPipeline'];
|
|
268
|
+
};
|
|
269
|
+
const inlineCompositionContext =
|
|
270
|
+
new AsyncLocalStorage<InlineCompositionStore>();
|
|
265
271
|
const toolExecutionOverrides = new AsyncLocalStorage<{ force: boolean }>();
|
|
266
272
|
const PROGRESS_HEARTBEAT_INTERVAL_MS = 1_000;
|
|
267
273
|
const PURE_JS_HEARTBEAT_ROW_INTERVAL = 250;
|
|
@@ -1270,12 +1276,6 @@ export class PlayContextImpl {
|
|
|
1270
1276
|
*/
|
|
1271
1277
|
private readonly governor: PlayExecutionGovernor;
|
|
1272
1278
|
private readonly resourceGovernor: RuntimeResourceGovernor;
|
|
1273
|
-
/**
|
|
1274
|
-
* Lineage identity for this context (run/play ids + ancestry). Seeded from the
|
|
1275
|
-
* Governor snapshot; used only for receipt keys, durable-boundary scoping, and
|
|
1276
|
-
* child run-id derivation. All policy lives in {@link governor}.
|
|
1277
|
-
*/
|
|
1278
|
-
private readonly governance: GovernanceSnapshot;
|
|
1279
1279
|
private readonly resolvedPlayExecutorCache = new Map<
|
|
1280
1280
|
string,
|
|
1281
1281
|
Promise<ResolvedPlayExecutor>
|
|
@@ -1443,7 +1443,6 @@ export class PlayContextImpl {
|
|
|
1443
1443
|
this.resourceGovernor = createRuntimeResourceGovernor({
|
|
1444
1444
|
executionGovernor: this.governor,
|
|
1445
1445
|
});
|
|
1446
|
-
this.governance = this.governor.snapshot();
|
|
1447
1446
|
}
|
|
1448
1447
|
|
|
1449
1448
|
private durableBoundaryId(localId: string): string {
|
|
@@ -1451,7 +1450,33 @@ export class PlayContextImpl {
|
|
|
1451
1450
|
// Nested plays and concurrent child calls therefore need a stable run scope
|
|
1452
1451
|
// in the key, otherwise two children can both produce e.g. "sleep-0-25"
|
|
1453
1452
|
// and replay the wrong boundary or never observe completion.
|
|
1454
|
-
return `${this.
|
|
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
|
-
|
|
1622
|
-
this
|
|
1647
|
+
parentGovernance.currentPlayId ||
|
|
1648
|
+
this.currentPlayName ||
|
|
1623
1649
|
this.#options.playId ||
|
|
1624
1650
|
'anonymous-play';
|
|
1625
1651
|
const governance: PlayCallGovernanceSnapshot = {
|
|
1626
|
-
rootRunId:
|
|
1627
|
-
parentRunId:
|
|
1652
|
+
rootRunId: parentGovernance.rootRunId,
|
|
1653
|
+
parentRunId: parentGovernance.currentRunId,
|
|
1628
1654
|
parentPlayName,
|
|
1629
1655
|
key: input.key,
|
|
1630
|
-
ancestryPlayIds: [...
|
|
1656
|
+
ancestryPlayIds: [...parentGovernance.ancestryPlayIds],
|
|
1631
1657
|
callDepth: input.childGovernance.callDepth,
|
|
1632
1658
|
playCallCount: input.childGovernance.playCallCount,
|
|
1633
1659
|
toolCallCount: input.childGovernance.toolCallCount,
|
|
@@ -1702,13 +1728,13 @@ export class PlayContextImpl {
|
|
|
1702
1728
|
| null
|
|
1703
1729
|
> {
|
|
1704
1730
|
const response = await fetch(
|
|
1705
|
-
`${this.runtimeApiBaseUrl()}${
|
|
1731
|
+
`${this.runtimeApiBaseUrl()}${PLAY_RUNTIME_API_CURRENT_PATH}`,
|
|
1706
1732
|
{
|
|
1707
1733
|
method: 'POST',
|
|
1708
1734
|
headers: await this.runtimeApiHeaders(),
|
|
1709
1735
|
body: JSON.stringify({
|
|
1710
1736
|
action: 'read_child_run_terminal_snapshot',
|
|
1711
|
-
parentRunId: this.
|
|
1737
|
+
parentRunId: this.currentGovernance.currentRunId,
|
|
1712
1738
|
childRunId: input.childRunId,
|
|
1713
1739
|
childPlayName: input.childPlayName,
|
|
1714
1740
|
}),
|
|
@@ -1817,7 +1843,7 @@ export class PlayContextImpl {
|
|
|
1817
1843
|
this.#options.runId
|
|
1818
1844
|
) {
|
|
1819
1845
|
const response = await fetch(
|
|
1820
|
-
`${this.#options.baseUrl.replace(/\/$/, '')}${
|
|
1846
|
+
`${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_API_CURRENT_PATH}`,
|
|
1821
1847
|
{
|
|
1822
1848
|
method: 'POST',
|
|
1823
1849
|
headers: {
|
|
@@ -2716,7 +2742,7 @@ export class PlayContextImpl {
|
|
|
2716
2742
|
}): Promise<string> {
|
|
2717
2743
|
return await this.durableToolCallCacheKeyForScope({
|
|
2718
2744
|
...input,
|
|
2719
|
-
playLocalScope: this.
|
|
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.
|
|
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.
|
|
2934
|
+
return this.currentExecutionScope.logical.runId;
|
|
2909
2935
|
}
|
|
2910
2936
|
|
|
2911
2937
|
private get currentReceiptOwnerRunId(): string {
|
|
2912
|
-
return this.
|
|
2938
|
+
return this.currentExecutionScope.receipt.ownerRunId;
|
|
2913
2939
|
}
|
|
2914
2940
|
|
|
2915
2941
|
private get currentRunAttempt(): number {
|
|
2916
|
-
return this.
|
|
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 =
|
|
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
|
|
3965
|
-
playId: this
|
|
3966
|
-
runId: this
|
|
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
|
|
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
|
|
4041
|
-
playId: this
|
|
4042
|
-
runId: this
|
|
4067
|
+
playName: this.currentPlayName,
|
|
4068
|
+
playId: this.currentExecutionScope.logical.playId,
|
|
4069
|
+
runId: this.currentRunId,
|
|
4043
4070
|
executorToken: this.#options.executorToken,
|
|
4044
|
-
staticPipeline: this
|
|
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
|
|
4476
|
-
playId: this
|
|
4477
|
-
runId: this
|
|
4502
|
+
playName: this.currentPlayName,
|
|
4503
|
+
playId: this.currentExecutionScope.logical.playId,
|
|
4504
|
+
runId: this.currentRunId,
|
|
4478
4505
|
executorToken: this.#options.executorToken,
|
|
4479
|
-
staticPipeline: this
|
|
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
|
|
4624
|
-
playId: this
|
|
4625
|
-
runId: this
|
|
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
|
|
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
|
-
//
|
|
6673
|
-
//
|
|
6674
|
-
//
|
|
6675
|
-
//
|
|
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
|
|
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
|
|
6725
|
+
? await parentGovernor.forkChild({
|
|
6696
6726
|
childPlayName: resolvedName,
|
|
6697
6727
|
childRunId: buildChildRunId({
|
|
6698
6728
|
childPlayName: resolvedName,
|
|
@@ -6706,7 +6736,7 @@ export class PlayContextImpl {
|
|
|
6706
6736
|
})
|
|
6707
6737
|
: inlineChildGovernor!.snapshot();
|
|
6708
6738
|
const childPlaySlot = launchThroughScheduler
|
|
6709
|
-
? await
|
|
6739
|
+
? await parentGovernor.acquireChildSubmitSlot()
|
|
6710
6740
|
: null;
|
|
6711
6741
|
const rowStore = rowContext.getStore();
|
|
6712
6742
|
const producer = {
|
|
@@ -6840,7 +6870,7 @@ export class PlayContextImpl {
|
|
|
6840
6870
|
// per-row inline-invocation sanity cap at creation).
|
|
6841
6871
|
const compositionNamespace = childGovernance.currentRunId;
|
|
6842
6872
|
const childExecutionScope = deriveChildRunExecutionScope(
|
|
6843
|
-
this.
|
|
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
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6883
|
+
// A scalar child is a normal function call on the parent's context.
|
|
6884
|
+
// Async-local identity changes receipt and recursion scope without
|
|
6885
|
+
// allocating another PlayContext, tool queue, drain loop, resource
|
|
6886
|
+
// governor, or receipt client. Calls from every concurrent child
|
|
6887
|
+
// therefore reach the same proven parent batching path.
|
|
6888
|
+
const result = await inlineCompositionContext.run(
|
|
6889
|
+
{
|
|
6890
|
+
context: this,
|
|
6891
|
+
executionScope: childExecutionScope,
|
|
6892
|
+
governor: inlineChildGovernor!,
|
|
6893
|
+
playName: resolvedName,
|
|
6894
|
+
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
6895
|
+
},
|
|
6896
|
+
() => this.executeResolvedPlay(resolvedPlay, this, input),
|
|
6892
6897
|
);
|
|
6893
|
-
await childContext.drainQueuedWork([childExecution]);
|
|
6894
|
-
const result = await childExecution;
|
|
6895
6898
|
this.inlineChildAggregates.ok += 1;
|
|
6896
6899
|
if (rowStore) {
|
|
6897
6900
|
this.emitScopedFieldMetaUpdate({
|
|
@@ -6912,7 +6915,6 @@ export class PlayContextImpl {
|
|
|
6912
6915
|
playId: resolvedName,
|
|
6913
6916
|
execution: options?.execution,
|
|
6914
6917
|
description: options?.description,
|
|
6915
|
-
nestedSteps: childContext.getSteps(),
|
|
6916
6918
|
});
|
|
6917
6919
|
return result as TOutput;
|
|
6918
6920
|
} catch (childError) {
|
|
@@ -6950,57 +6952,6 @@ export class PlayContextImpl {
|
|
|
6950
6952
|
}
|
|
6951
6953
|
}
|
|
6952
6954
|
|
|
6953
|
-
private async mintChildExecutorToken(input: {
|
|
6954
|
-
parentRunId: string;
|
|
6955
|
-
parentPlayName?: string | null;
|
|
6956
|
-
childRunId: string;
|
|
6957
|
-
childPlayName: string;
|
|
6958
|
-
}): Promise<string | undefined> {
|
|
6959
|
-
if (!this.#options.executorToken || !this.#options.baseUrl) {
|
|
6960
|
-
return this.#options.executorToken;
|
|
6961
|
-
}
|
|
6962
|
-
const parentPlayName = input.parentPlayName?.trim();
|
|
6963
|
-
if (!parentPlayName) {
|
|
6964
|
-
return this.#options.executorToken;
|
|
6965
|
-
}
|
|
6966
|
-
const response = await fetch(
|
|
6967
|
-
`${this.#options.baseUrl.replace(/\/$/, '')}${PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_COMPAT_PATH}`,
|
|
6968
|
-
{
|
|
6969
|
-
method: 'POST',
|
|
6970
|
-
headers: {
|
|
6971
|
-
Authorization: `Bearer ${this.#options.executorToken}`,
|
|
6972
|
-
'Content-Type': 'application/json',
|
|
6973
|
-
...(await this.vercelProtectionHeaders()),
|
|
6974
|
-
},
|
|
6975
|
-
body: JSON.stringify({
|
|
6976
|
-
parentRunId: input.parentRunId,
|
|
6977
|
-
parentPlayName,
|
|
6978
|
-
childRunId: input.childRunId,
|
|
6979
|
-
childPlayName: input.childPlayName,
|
|
6980
|
-
}),
|
|
6981
|
-
},
|
|
6982
|
-
);
|
|
6983
|
-
const body = (await response.json().catch(() => null)) as {
|
|
6984
|
-
executorToken?: unknown;
|
|
6985
|
-
error?: unknown;
|
|
6986
|
-
} | null;
|
|
6987
|
-
if (!response.ok) {
|
|
6988
|
-
const message =
|
|
6989
|
-
typeof body?.error === 'string' && body.error.trim()
|
|
6990
|
-
? body.error.trim()
|
|
6991
|
-
: response.statusText;
|
|
6992
|
-
throw new Error(
|
|
6993
|
-
`ctx.runPlay failed to mint child executor token for "${input.childPlayName}": ${message} (status ${response.status}).`,
|
|
6994
|
-
);
|
|
6995
|
-
}
|
|
6996
|
-
if (typeof body?.executorToken !== 'string' || !body.executorToken.trim()) {
|
|
6997
|
-
throw new Error(
|
|
6998
|
-
`ctx.runPlay failed to mint child executor token for "${input.childPlayName}": response did not include executorToken.`,
|
|
6999
|
-
);
|
|
7000
|
-
}
|
|
7001
|
-
return body.executorToken.trim();
|
|
7002
|
-
}
|
|
7003
|
-
|
|
7004
6955
|
/**
|
|
7005
6956
|
* Replay-stable composition namespace for one inline `ctx.runPlay`
|
|
7006
6957
|
* invocation. Format `child:<playName>#<callKey>[@<rowScope>]`:
|
|
@@ -7358,7 +7309,7 @@ export class PlayContextImpl {
|
|
|
7358
7309
|
|
|
7359
7310
|
const rowStore = rowContext.getStore();
|
|
7360
7311
|
const scope = rowStore ? `row-${rowStore.rowId}` : 'workflow';
|
|
7361
|
-
const callIndexKey = `${scope}:${normalizedKey}`;
|
|
7312
|
+
const callIndexKey = `${this.currentExecutionScope.receipt.namespace}:${scope}:${normalizedKey}`;
|
|
7362
7313
|
const callIndex = this.stepCallIndexByKey.get(callIndexKey) ?? 0;
|
|
7363
7314
|
this.stepCallIndexByKey.set(callIndexKey, callIndex + 1);
|
|
7364
7315
|
const boundarySuffix = callIndex === 0 ? '' : `:${callIndex}`;
|
|
@@ -429,7 +429,11 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
429
429
|
receipt: RuntimeStepReceipt,
|
|
430
430
|
source: DurableReceiptRecoverySource = 'cache',
|
|
431
431
|
): Promise<T> => {
|
|
432
|
-
input.log(
|
|
432
|
+
input.log(
|
|
433
|
+
source === 'owner'
|
|
434
|
+
? `ctx.${input.operation}(${input.id}): executed and converged through immutable publication`
|
|
435
|
+
: `ctx.${input.operation}(${input.id}): reused completed work`,
|
|
436
|
+
);
|
|
433
437
|
if (receipt.output === undefined) {
|
|
434
438
|
return receipt.output as T;
|
|
435
439
|
}
|
|
@@ -489,8 +493,7 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
489
493
|
const afterLock = await input.store.get(input.receiptKey);
|
|
490
494
|
if (
|
|
491
495
|
input.force !== true &&
|
|
492
|
-
(afterLock?.status === 'completed' ||
|
|
493
|
-
afterLock?.status === 'skipped')
|
|
496
|
+
(afterLock?.status === 'completed' || afterLock?.status === 'skipped')
|
|
494
497
|
) {
|
|
495
498
|
await input.store.releaseExecutionLock({
|
|
496
499
|
receiptKey: input.receiptKey,
|
|
@@ -566,6 +569,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
566
569
|
completed?.status === 'completed' ||
|
|
567
570
|
completed?.status === 'skipped'
|
|
568
571
|
) {
|
|
572
|
+
// Completion is a compact acknowledgement. This execution already
|
|
573
|
+
// owns the just-produced value; only a later receipt read needs the
|
|
574
|
+
// persisted payload for replay.
|
|
575
|
+
if (completed.output === undefined) return result;
|
|
569
576
|
return await recoverCompletedReceipt(completed, 'owner');
|
|
570
577
|
}
|
|
571
578
|
const winner = await input.store.get(input.receiptKey);
|
|
@@ -828,6 +835,10 @@ export async function executeWithDurableRuntimeReceipt<T>(input: {
|
|
|
828
835
|
(completed.status === 'completed' || completed.status === 'skipped')
|
|
829
836
|
) {
|
|
830
837
|
logPhase('total', lifecycleStartedAt);
|
|
838
|
+
// Completion endpoints acknowledge ownership and status only. The caller
|
|
839
|
+
// already owns the live value; replay reads are the only path that needs
|
|
840
|
+
// the receipt payload returned over the transport.
|
|
841
|
+
if (completed.output === undefined) return result;
|
|
831
842
|
return await recoverCompletedReceipt(completed, 'owner');
|
|
832
843
|
}
|
|
833
844
|
if (input.store.canPersistCompletion) {
|
|
@@ -540,7 +540,7 @@ export function createPlayExecutionGovernor(
|
|
|
540
540
|
async forkInlineChild(childInput) {
|
|
541
541
|
return createInlineChildGovernor(
|
|
542
542
|
governor,
|
|
543
|
-
|
|
543
|
+
deriveInlineChildSnapshot(state, childInput, policy),
|
|
544
544
|
);
|
|
545
545
|
},
|
|
546
546
|
|
|
@@ -593,6 +593,38 @@ export function createPlayExecutionGovernor(
|
|
|
593
593
|
return governor;
|
|
594
594
|
}
|
|
595
595
|
|
|
596
|
+
function deriveInlineChildSnapshot(
|
|
597
|
+
parent: GovernanceSnapshot,
|
|
598
|
+
input: { childPlayName: string; childRunId: string },
|
|
599
|
+
policy: ResolvedExecutionPolicy,
|
|
600
|
+
): GovernanceSnapshot {
|
|
601
|
+
if (parent.ancestryPlayIds.includes(input.childPlayName)) {
|
|
602
|
+
throw new Error(
|
|
603
|
+
`Recursive play graph detected: ${[
|
|
604
|
+
...parent.ancestryPlayIds,
|
|
605
|
+
input.childPlayName,
|
|
606
|
+
].join(' -> ')}`,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
const nextDepth = parent.callDepth + 1;
|
|
610
|
+
if (nextDepth > policy.budgets.maxPlayCallDepth) {
|
|
611
|
+
throw new GovernorBudgetError(
|
|
612
|
+
'playDepth',
|
|
613
|
+
nextDepth,
|
|
614
|
+
policy.budgets.maxPlayCallDepth,
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
...parent,
|
|
619
|
+
currentRunId: input.childRunId,
|
|
620
|
+
currentPlayId: input.childPlayName,
|
|
621
|
+
ancestryPlayIds: [...parent.ancestryPlayIds, input.childPlayName],
|
|
622
|
+
ancestryRunIds: [...parent.ancestryRunIds, input.childRunId],
|
|
623
|
+
callDepth: nextDepth,
|
|
624
|
+
parentChildCalls: {},
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
596
628
|
function createInlineChildGovernor(
|
|
597
629
|
root: PlayExecutionGovernor,
|
|
598
630
|
initial: GovernanceSnapshot,
|
|
@@ -667,7 +699,10 @@ function createInlineChildGovernor(
|
|
|
667
699
|
};
|
|
668
700
|
},
|
|
669
701
|
async forkInlineChild(input) {
|
|
670
|
-
return createInlineChildGovernor(
|
|
702
|
+
return createInlineChildGovernor(
|
|
703
|
+
child,
|
|
704
|
+
deriveInlineChildSnapshot(child.snapshot(), input, child.policy),
|
|
705
|
+
);
|
|
671
706
|
},
|
|
672
707
|
snapshot() {
|
|
673
708
|
const counters = root.snapshot();
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
PLAY_RUNTIME_PROVIDERS,
|
|
7
7
|
PLAY_RUNTIME_PROVIDER_IDS,
|
|
8
8
|
defaultPlayRuntimeProvider,
|
|
9
|
+
resolveEnabledPlayRuntimeProvider,
|
|
9
10
|
resolvePlayRuntimeProvider,
|
|
10
11
|
type PlayRuntimeProviderId,
|
|
11
12
|
} from './providers';
|
|
@@ -50,3 +51,24 @@ export function resolveExecutionProfile(
|
|
|
50
51
|
throw error;
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
export function resolveEnabledExecutionProfile(
|
|
56
|
+
override?: string | null,
|
|
57
|
+
): PlayExecutionProfile {
|
|
58
|
+
try {
|
|
59
|
+
return resolveEnabledPlayRuntimeProvider(override);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (
|
|
62
|
+
override?.trim() &&
|
|
63
|
+
error instanceof Error &&
|
|
64
|
+
/Unsupported play runtime provider/.test(error.message)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Unknown execution profile "${override.trim()}". Expected one of: ${Object.keys(
|
|
68
|
+
PLAY_EXECUTION_PROFILES,
|
|
69
|
+
).join(', ')}.`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|