deepline 0.1.209 → 0.1.210
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 +0 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +102 -138
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +12 -4
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +25 -92
- package/dist/bundling-sources/sdk/src/play.ts +2 -2
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/child-run-id.ts +4 -5
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5 -26
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +1 -2
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +2 -4
- package/dist/cli/index.js +3 -6
- package/dist/cli/index.mjs +3 -6
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -2445,8 +2445,6 @@ function runRequestFromPlayWorkflowParams(
|
|
|
2445
2445
|
preloadedDbSessions: params.preloadedDbSessions ?? null,
|
|
2446
2446
|
runtimeTestFaultHeader: params.runtimeTestFaultHeader ?? null,
|
|
2447
2447
|
testPolicyOverrides: params.testPolicyOverrides ?? null,
|
|
2448
|
-
inlineChildRunRegistered:
|
|
2449
|
-
params.runtimeBackend === 'cf_workflows_dynamic_worker_inline_child',
|
|
2450
2448
|
coordinatorUrl: params.coordinatorUrl ?? null,
|
|
2451
2449
|
coordinatorInternalToken: params.coordinatorInternalToken ?? null,
|
|
2452
2450
|
totalRows: params.totalRows,
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type WorkflowEvent,
|
|
33
33
|
type WorkflowStep,
|
|
34
34
|
} from 'cloudflare:workers';
|
|
35
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
35
36
|
import {
|
|
36
37
|
deterministicMapChunkStepName,
|
|
37
38
|
type ExecutionPlan,
|
|
@@ -40,7 +41,6 @@ import {
|
|
|
40
41
|
STANDARD_PLAY_RUNTIME_LIMIT_LABEL,
|
|
41
42
|
STANDARD_PLAY_RUNTIME_LIMIT_SECONDS,
|
|
42
43
|
} from '../../../shared_libs/play-runtime/runtime-constants';
|
|
43
|
-
import { childSubmitIdempotencyKey } from '../../../shared_libs/play-runtime/child-run-id';
|
|
44
44
|
import {
|
|
45
45
|
createPlayExecutionGovernor,
|
|
46
46
|
type GovernanceSnapshot,
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
import { createDeferredPlayDataset } from '../../../shared_libs/plays/dataset';
|
|
105
105
|
import {
|
|
106
106
|
buildDurableCtxCallCacheKey,
|
|
107
|
-
|
|
107
|
+
buildDurableRunPlayInvocationScope,
|
|
108
108
|
} from '../../../shared_libs/play-runtime/durable-call-cache';
|
|
109
109
|
import {
|
|
110
110
|
resolveRuntimeToolReceiptWaitMaxAttempts,
|
|
@@ -368,8 +368,6 @@ type RunRequest = {
|
|
|
368
368
|
/** Internal ctx.runPlay lineage. Public SDK/users never see this. */
|
|
369
369
|
playCallGovernance?: PlayCallGovernanceSnapshot | null;
|
|
370
370
|
preloadedDbSessions?: PreloadedRuntimeDbSession[] | null;
|
|
371
|
-
/** Coordinator already created the child run row before invoking /run-inline. */
|
|
372
|
-
inlineChildRunRegistered?: boolean | null;
|
|
373
371
|
/** Cloudflare coordinator URL for direct Workflow control-plane signals. */
|
|
374
372
|
coordinatorUrl?: string | null;
|
|
375
373
|
/** Request-scoped coordinator auth token for preview/dev direct control calls. */
|
|
@@ -1148,11 +1146,29 @@ async function drainRunnerPerfTraces(req: RunRequest): Promise<void> {
|
|
|
1148
1146
|
|
|
1149
1147
|
let workerRequestSequence = 0;
|
|
1150
1148
|
|
|
1149
|
+
const workerRunPlayRowContext = new AsyncLocalStorage<{
|
|
1150
|
+
tableNamespace: string;
|
|
1151
|
+
rowKey: string;
|
|
1152
|
+
fieldName: string;
|
|
1153
|
+
}>();
|
|
1154
|
+
|
|
1151
1155
|
function makeRequestId(): string {
|
|
1152
1156
|
workerRequestSequence = (workerRequestSequence + 1) % Number.MAX_SAFE_INTEGER;
|
|
1153
1157
|
return `worker-request-${workerRequestSequence}`;
|
|
1154
1158
|
}
|
|
1155
1159
|
|
|
1160
|
+
async function executeFreshChildInvocation<T>(
|
|
1161
|
+
input: {
|
|
1162
|
+
parentRunId: string;
|
|
1163
|
+
key: string;
|
|
1164
|
+
invocationScope: string;
|
|
1165
|
+
},
|
|
1166
|
+
execute: (invocationId: string) => Promise<T>,
|
|
1167
|
+
): Promise<T> {
|
|
1168
|
+
const invocationId = `${input.parentRunId}:runPlay:${input.key}:${input.invocationScope}`;
|
|
1169
|
+
return await execute(invocationId);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1156
1172
|
function makeRuntimeSheetAttempt(input: {
|
|
1157
1173
|
runId: string;
|
|
1158
1174
|
runAttempt?: number | null;
|
|
@@ -1665,27 +1681,6 @@ async function hashChildPlayEventKey(input: unknown): Promise<string> {
|
|
|
1665
1681
|
return (await hashJson(input)).slice(0, 32);
|
|
1666
1682
|
}
|
|
1667
1683
|
|
|
1668
|
-
function workerChildRevisionFingerprint(input: {
|
|
1669
|
-
playName: string;
|
|
1670
|
-
manifest: PlayRuntimeManifest | null | undefined;
|
|
1671
|
-
}): string | null {
|
|
1672
|
-
const manifest = input.manifest;
|
|
1673
|
-
if (!manifest) return null;
|
|
1674
|
-
return sha256Hex(
|
|
1675
|
-
stableStringify({
|
|
1676
|
-
playId: manifest.playName?.trim() || input.playName,
|
|
1677
|
-
codeFormat: 'cjs_module',
|
|
1678
|
-
artifactHash: manifest.artifactHash ?? null,
|
|
1679
|
-
graphHash: manifest.graphHash ?? null,
|
|
1680
|
-
sourceHash: null,
|
|
1681
|
-
sourceCodeHash:
|
|
1682
|
-
typeof manifest.sourceCode === 'string'
|
|
1683
|
-
? sha256Hex(manifest.sourceCode)
|
|
1684
|
-
: null,
|
|
1685
|
-
}),
|
|
1686
|
-
);
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
1684
|
type RuntimeResolvedPlayResponse = {
|
|
1690
1685
|
manifest?: PlayRuntimeManifest | null;
|
|
1691
1686
|
};
|
|
@@ -5580,37 +5575,49 @@ function createMinimalWorkerCtx(
|
|
|
5580
5575
|
})
|
|
5581
5576
|
: null,
|
|
5582
5577
|
});
|
|
5583
|
-
const resolved = await
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
status
|
|
5607
|
-
|
|
5608
|
-
|
|
5578
|
+
const resolved = await workerRunPlayRowContext.run(
|
|
5579
|
+
{
|
|
5580
|
+
tableNamespace: name,
|
|
5581
|
+
rowKey: entry.rowKey,
|
|
5582
|
+
fieldName: key,
|
|
5583
|
+
},
|
|
5584
|
+
() =>
|
|
5585
|
+
executeWorkerStepResolver(
|
|
5586
|
+
value,
|
|
5587
|
+
enriched,
|
|
5588
|
+
rowCtx,
|
|
5589
|
+
absoluteIndex,
|
|
5590
|
+
previousCell,
|
|
5591
|
+
isWorkerStepProgram(value)
|
|
5592
|
+
? {
|
|
5593
|
+
parentField: key,
|
|
5594
|
+
path: [],
|
|
5595
|
+
outputs: stepProgramOutputs,
|
|
5596
|
+
onOutput: async (stepOutput) => {
|
|
5597
|
+
generatedOutputFields.add(
|
|
5598
|
+
stepOutput.columnName,
|
|
5599
|
+
);
|
|
5600
|
+
const status =
|
|
5601
|
+
stepOutput.status ?? 'completed';
|
|
5602
|
+
if (status === 'skipped')
|
|
5603
|
+
stepCellsSkipped += 1;
|
|
5604
|
+
else stepCellsCompleted += 1;
|
|
5605
|
+
await enqueueLiveRowUpdate({
|
|
5606
|
+
key: entry.rowKey,
|
|
5607
|
+
dataPatch: {
|
|
5608
|
+
[stepOutput.columnName]: stepOutput.value,
|
|
5609
|
+
},
|
|
5610
|
+
cellMetaPatch: {
|
|
5611
|
+
[stepOutput.columnName]: {
|
|
5612
|
+
status,
|
|
5613
|
+
stage: stepOutput.stepId,
|
|
5614
|
+
},
|
|
5615
|
+
},
|
|
5616
|
+
});
|
|
5609
5617
|
},
|
|
5610
|
-
}
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
: undefined,
|
|
5618
|
+
}
|
|
5619
|
+
: undefined,
|
|
5620
|
+
),
|
|
5614
5621
|
);
|
|
5615
5622
|
enriched[key] = resolved.value;
|
|
5616
5623
|
fieldOutputs[key] = resolved.value;
|
|
@@ -6933,7 +6940,6 @@ function createMinimalWorkerCtx(
|
|
|
6933
6940
|
options?: {
|
|
6934
6941
|
description?: string;
|
|
6935
6942
|
timeoutMs?: number;
|
|
6936
|
-
staleAfterSeconds?: number;
|
|
6937
6943
|
},
|
|
6938
6944
|
): Promise<unknown> {
|
|
6939
6945
|
const normalizedKey = normalizeContextKey(key, 'runPlay');
|
|
@@ -6947,34 +6953,49 @@ function createMinimalWorkerCtx(
|
|
|
6947
6953
|
playRef: resolvedName,
|
|
6948
6954
|
cache: childManifestCache,
|
|
6949
6955
|
});
|
|
6950
|
-
const
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
input,
|
|
6962
|
-
}),
|
|
6963
|
-
staleAfterSeconds: options?.staleAfterSeconds,
|
|
6956
|
+
const rowScope = workerRunPlayRowContext.getStore();
|
|
6957
|
+
const invocationScope = buildDurableRunPlayInvocationScope({
|
|
6958
|
+
childPlayName: resolvedName,
|
|
6959
|
+
input,
|
|
6960
|
+
rowScope: rowScope
|
|
6961
|
+
? {
|
|
6962
|
+
fieldName: rowScope.fieldName,
|
|
6963
|
+
rowKey: rowScope.rowKey,
|
|
6964
|
+
tableNamespace: rowScope.tableNamespace,
|
|
6965
|
+
}
|
|
6966
|
+
: null,
|
|
6964
6967
|
});
|
|
6965
|
-
return await
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
+
return await executeFreshChildInvocation(
|
|
6969
|
+
{
|
|
6970
|
+
parentRunId: req.runId,
|
|
6971
|
+
key: normalizedKey,
|
|
6972
|
+
invocationScope,
|
|
6973
|
+
},
|
|
6974
|
+
async (childInvocationId) => {
|
|
6968
6975
|
// The Governor owns the play-call lineage: forkChild does the cycle
|
|
6969
6976
|
// guard, depth/per-parent/playCall/descendant budget charges, and
|
|
6970
6977
|
// returns the snapshot to thread into the child so budgets accumulate
|
|
6971
|
-
// across isolates.
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
+
// across isolates. Every ctx.runPlay call is a fresh child invocation.
|
|
6979
|
+
const childRunId = `${req.runId}:child:${childInvocationId}`;
|
|
6980
|
+
// Checkpoint governance admission separately from the child result.
|
|
6981
|
+
// Workflow replay reuses this admission and the coordinator's stable
|
|
6982
|
+
// child launch identity, but ctx.runPlay never caches child output.
|
|
6983
|
+
const forkChild = () =>
|
|
6984
|
+
governor.forkChild({
|
|
6985
|
+
childPlayName: resolvedName,
|
|
6986
|
+
childRunId,
|
|
6987
|
+
});
|
|
6988
|
+
const childGovernance = workflowStep
|
|
6989
|
+
? await (
|
|
6990
|
+
workflowStep.do as unknown as <T>(
|
|
6991
|
+
name: string,
|
|
6992
|
+
callback: () => Promise<T>,
|
|
6993
|
+
) => Promise<T>
|
|
6994
|
+
)(
|
|
6995
|
+
`runPlay-governance:${sha256Hex(childInvocationId).slice(0, 24)}`,
|
|
6996
|
+
forkChild,
|
|
6997
|
+
)
|
|
6998
|
+
: await forkChild();
|
|
6978
6999
|
const nextDepth = childGovernance.callDepth;
|
|
6979
7000
|
const nextParentCalls =
|
|
6980
7001
|
governor.snapshot().parentChildCalls[req.playName] ?? 0;
|
|
@@ -7047,11 +7068,7 @@ function createMinimalWorkerCtx(
|
|
|
7047
7068
|
options?.timeoutMs == null && !childNeedsWorkflowScheduler,
|
|
7048
7069
|
body: {
|
|
7049
7070
|
name: resolvedName,
|
|
7050
|
-
childIdempotencyKey:
|
|
7051
|
-
receiptKey,
|
|
7052
|
-
wasFailed,
|
|
7053
|
-
leaseId: receiptContext.leaseId,
|
|
7054
|
-
}),
|
|
7071
|
+
childIdempotencyKey: childInvocationId,
|
|
7055
7072
|
input: isRecord(input) ? input : {},
|
|
7056
7073
|
orgId: req.orgId,
|
|
7057
7074
|
callbackUrl: req.callbackUrl,
|
|
@@ -7329,12 +7346,6 @@ function createMinimalWorkerCtx(
|
|
|
7329
7346
|
childPlaySlot?.release();
|
|
7330
7347
|
}
|
|
7331
7348
|
},
|
|
7332
|
-
{
|
|
7333
|
-
forceFailedRefresh: true,
|
|
7334
|
-
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
7335
|
-
runningReceiptWaitMaxAttempts:
|
|
7336
|
-
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
7337
|
-
},
|
|
7338
7349
|
);
|
|
7339
7350
|
},
|
|
7340
7351
|
async fetch(
|
|
@@ -7615,13 +7626,6 @@ async function handleRunInline(
|
|
|
7615
7626
|
const probeStartedAt = nowMs();
|
|
7616
7627
|
await probeHarnessOnce(env, runPrefix);
|
|
7617
7628
|
traceInline('inline.probe_harness', probeStartedAt);
|
|
7618
|
-
if (!req.inlineChildRunRegistered) {
|
|
7619
|
-
const registerStartedAt = nowMs();
|
|
7620
|
-
await registerInlineChildRun(req);
|
|
7621
|
-
traceInline('inline.register_child_run', registerStartedAt);
|
|
7622
|
-
} else {
|
|
7623
|
-
traceInline('inline.register_child_run', nowMs(), { skipped: true });
|
|
7624
|
-
}
|
|
7625
7629
|
const executeStartedAt = nowMs();
|
|
7626
7630
|
const output = await executeRunRequest(
|
|
7627
7631
|
req,
|
|
@@ -7667,46 +7671,6 @@ async function handleRunInline(
|
|
|
7667
7671
|
}
|
|
7668
7672
|
}
|
|
7669
7673
|
|
|
7670
|
-
async function registerInlineChildRun(req: RunRequest): Promise<void> {
|
|
7671
|
-
const snapshot = isRecord(req.contractSnapshot) ? req.contractSnapshot : {};
|
|
7672
|
-
const artifactMetadata = isRecord(snapshot.artifactMetadata)
|
|
7673
|
-
? snapshot.artifactMetadata
|
|
7674
|
-
: {};
|
|
7675
|
-
const governance = req.playCallGovernance;
|
|
7676
|
-
await postRuntimeApi(req.baseUrl, req.executorToken, {
|
|
7677
|
-
action: 'start_inline_child_run',
|
|
7678
|
-
playName: req.playName,
|
|
7679
|
-
runId: req.runId,
|
|
7680
|
-
parentRunId: governance?.parentRunId,
|
|
7681
|
-
rootRunId: governance?.rootRunId,
|
|
7682
|
-
workflowFamilyKey:
|
|
7683
|
-
governance?.rootRunId ?? governance?.parentRunId ?? req.runId,
|
|
7684
|
-
artifactStorageKey:
|
|
7685
|
-
typeof artifactMetadata.storageKey === 'string'
|
|
7686
|
-
? artifactMetadata.storageKey
|
|
7687
|
-
: undefined,
|
|
7688
|
-
artifactHash:
|
|
7689
|
-
typeof artifactMetadata.artifactHash === 'string'
|
|
7690
|
-
? artifactMetadata.artifactHash
|
|
7691
|
-
: undefined,
|
|
7692
|
-
graphHash:
|
|
7693
|
-
typeof artifactMetadata.graphHash === 'string'
|
|
7694
|
-
? artifactMetadata.graphHash
|
|
7695
|
-
: undefined,
|
|
7696
|
-
runtimeBackend: 'workers_edge',
|
|
7697
|
-
schedulerBackend: 'inline_child',
|
|
7698
|
-
executionProfile: 'workers_edge',
|
|
7699
|
-
maxCreditsPerRun: extractMaxCreditsPerRun(req.contractSnapshot),
|
|
7700
|
-
staticPipeline: snapshot.staticPipeline ?? null,
|
|
7701
|
-
source:
|
|
7702
|
-
snapshot.source === 'published' ||
|
|
7703
|
-
snapshot.source === 'ad_hoc' ||
|
|
7704
|
-
snapshot.source === 'draft'
|
|
7705
|
-
? snapshot.source
|
|
7706
|
-
: 'published',
|
|
7707
|
-
});
|
|
7708
|
-
}
|
|
7709
|
-
|
|
7710
7674
|
/** Cap on run log lines retained in the terminal output compatibility shape. */
|
|
7711
7675
|
const RUN_LOG_BUFFER_LIMIT = 500;
|
|
7712
7676
|
/** Min wall-clock interval between live run-ledger flushes during a run. */
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
markWorkerToolReceiptResultCached,
|
|
55
55
|
markWorkerToolReceiptResultExecution,
|
|
56
56
|
planWorkerToolReceiptGroups,
|
|
57
|
+
resolveRunningReceiptWaitMaxAttempts,
|
|
57
58
|
resolveWorkerToolReceiptGroupWaitMaxAttempts,
|
|
58
59
|
resolveWorkerToolRuntimeTimeoutMs,
|
|
59
60
|
} from './tool-receipts';
|
|
@@ -1232,10 +1233,17 @@ export class WorkerToolBatchScheduler {
|
|
|
1232
1233
|
try {
|
|
1233
1234
|
const result = await this.waitForDurableToolReceipt({
|
|
1234
1235
|
receiptKey,
|
|
1235
|
-
maxAttempts:
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1236
|
+
maxAttempts: resolveRunningReceiptWaitMaxAttempts({
|
|
1237
|
+
ownerRunId: claim.receipt.runId,
|
|
1238
|
+
currentRunId: this.options.req.runId,
|
|
1239
|
+
updatedAt: claim.receipt.updatedAt,
|
|
1240
|
+
requestedMaxAttempts:
|
|
1241
|
+
resolveWorkerToolReceiptGroupWaitMaxAttempts(
|
|
1242
|
+
group,
|
|
1243
|
+
(request) => request.receiptWaitMaxAttempts,
|
|
1244
|
+
),
|
|
1245
|
+
nowMs: this.options.nowMs(),
|
|
1246
|
+
}),
|
|
1239
1247
|
});
|
|
1240
1248
|
if (groupState.forceDurableRefresh) {
|
|
1241
1249
|
return await this.claimForcedDurableToolReceiptGroupAfterWait(
|
|
@@ -39,106 +39,39 @@ export function canReclaimTimedOutWorkerToolReceipt(input: {
|
|
|
39
39
|
* conservative constant, not a heartbeat multiple.
|
|
40
40
|
*/
|
|
41
41
|
export const RUNNING_RECEIPT_DEAD_OWNER_STALENESS_MS = 60_000;
|
|
42
|
+
export const RUNNING_RECEIPT_POLL_INTERVAL_MS = 250;
|
|
42
43
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
* the completion poll.
|
|
46
|
-
*
|
|
47
|
-
* The poll (`waitForCompletedRuntimeReceipt`) issues one service-binding
|
|
48
|
-
* subrequest per 250ms tick, up to the receipt's wait budget (~1320 ticks for a
|
|
49
|
-
* 5.5-minute default). On resume, a map can carry many receipts left `running`
|
|
50
|
-
* by a DEAD prior run; polling each one burns its full tick budget waiting on a
|
|
51
|
-
* receipt no live worker will complete, which blows Cloudflare's ~1000
|
|
52
|
-
* subrequests-per-invocation budget and kills the resume with
|
|
53
|
-
* "Too many subrequests by single Worker invocation" (prod resume deaths
|
|
54
|
-
* 20260703t230209 / 20260703t232104).
|
|
55
|
-
*
|
|
56
|
-
* A receipt is a dead-owner orphan when it is owned by a DIFFERENT run (a
|
|
57
|
-
* same-run `running` is this run replaying — its owner may still be live) and
|
|
58
|
-
* its last write is stale (or missing entirely — a live owner would carry a
|
|
59
|
-
* recent claim timestamp). Those reclaim immediately, matching the dispatch
|
|
60
|
-
* code's own contract ("on resume it reclaims and re-executes"). A live or
|
|
61
|
-
* freshly-claimed owner keeps the poll.
|
|
62
|
-
*/
|
|
63
|
-
export function shouldReclaimRunningReceiptWithoutPolling(input: {
|
|
44
|
+
/** Bound a follower poll by when a different-run owner becomes reclaimable. */
|
|
45
|
+
export function resolveRunningReceiptWaitMaxAttempts(input: {
|
|
64
46
|
ownerRunId?: string | null;
|
|
65
47
|
currentRunId: string;
|
|
66
48
|
updatedAt?: string | null;
|
|
49
|
+
requestedMaxAttempts: number;
|
|
67
50
|
nowMs?: number;
|
|
68
51
|
stalenessFloorMs?: number;
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
pollIntervalMs?: number;
|
|
53
|
+
}): number {
|
|
54
|
+
const requested = Math.max(1, Math.floor(input.requestedMaxAttempts));
|
|
71
55
|
const ownerRunId = input.ownerRunId?.trim() ?? '';
|
|
72
|
-
|
|
73
|
-
if (!
|
|
74
|
-
|
|
75
|
-
input.stalenessFloorMs ?? RUNNING_RECEIPT_DEAD_OWNER_STALENESS_MS;
|
|
76
|
-
// No freshness signal at all: treat as stale. A live owner would carry a
|
|
77
|
-
// recent claim timestamp; an orphan from a dead run may carry none.
|
|
78
|
-
if (!input.updatedAt) return true;
|
|
79
|
-
const updatedAtMs = Date.parse(input.updatedAt);
|
|
80
|
-
if (!Number.isFinite(updatedAtMs)) return true;
|
|
81
|
-
const now = input.nowMs ?? Date.now();
|
|
82
|
-
return now - updatedAtMs >= floorMs;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Metadata a `running` receipt carries about its owner and last write. */
|
|
86
|
-
export interface RunningReceiptOwnershipInput {
|
|
87
|
-
ownerRunId?: string | null;
|
|
88
|
-
currentRunId: string;
|
|
89
|
-
updatedAt?: string | null;
|
|
90
|
-
nowMs?: number;
|
|
91
|
-
stalenessFloorMs?: number;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Outcome of polling a `running` receipt for completion. */
|
|
95
|
-
export type RunningReceiptPollOutcome =
|
|
96
|
-
/** The poll saw the receipt complete and already resolved the group. */
|
|
97
|
-
| { kind: 'completed' }
|
|
98
|
-
/** The poll timed out; the receipt is still `running`. */
|
|
99
|
-
| { kind: 'timed_out'; error: unknown }
|
|
100
|
-
/** The poll failed for a non-timeout reason (e.g. the receipt failed). */
|
|
101
|
-
| { kind: 'errored'; error: unknown };
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Orchestrate the resume-time handling of a `running` receipt: reclaim it
|
|
105
|
-
* immediately when it is a dead-owner orphan, otherwise poll for completion and
|
|
106
|
-
* only reclaim after the poll times out. Kept as a pure, dependency-injected
|
|
107
|
-
* function (the poll and reclaim side effects are passed in) so the
|
|
108
|
-
* "skip the poll for a dead owner" decision is unit-testable with a poll counter
|
|
109
|
-
* — the Worker scheduler that owns the real effects imports `cloudflare:workers`
|
|
110
|
-
* and cannot be loaded under vitest.
|
|
111
|
-
*
|
|
112
|
-
* Behavior (must match the scheduler's prior inline logic exactly):
|
|
113
|
-
* - dead-owner + reclaim-eligible → `reclaim` now, never polling;
|
|
114
|
-
* - otherwise `poll`; on `completed` the group is already resolved (return []);
|
|
115
|
-
* on a non-timeout error `reject` and stop; on timeout, `reclaim` if still
|
|
116
|
-
* eligible else `reject` with the timeout error.
|
|
117
|
-
*/
|
|
118
|
-
export async function reclaimRunningReceiptGroupAfterWait<TClaimed>(input: {
|
|
119
|
-
ownership: RunningReceiptOwnershipInput;
|
|
120
|
-
timeoutError: unknown;
|
|
121
|
-
poll: () => Promise<RunningReceiptPollOutcome>;
|
|
122
|
-
reclaim: (rejectionOnFallthrough: unknown) => Promise<TClaimed[]>;
|
|
123
|
-
reject: (error: unknown) => void;
|
|
124
|
-
}): Promise<TClaimed[]> {
|
|
125
|
-
if (
|
|
126
|
-
shouldReclaimRunningReceiptWithoutPolling(input.ownership) &&
|
|
127
|
-
canReclaimTimedOutWorkerToolReceipt(input.ownership)
|
|
128
|
-
) {
|
|
129
|
-
return await input.reclaim(input.timeoutError);
|
|
130
|
-
}
|
|
131
|
-
const outcome = await input.poll();
|
|
132
|
-
if (outcome.kind === 'completed') return [];
|
|
133
|
-
if (outcome.kind === 'errored') {
|
|
134
|
-
input.reject(outcome.error);
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
137
|
-
if (!canReclaimTimedOutWorkerToolReceipt(input.ownership)) {
|
|
138
|
-
input.reject(outcome.error);
|
|
139
|
-
return [];
|
|
56
|
+
const currentRunId = input.currentRunId.trim();
|
|
57
|
+
if (!ownerRunId || !currentRunId || ownerRunId === currentRunId) {
|
|
58
|
+
return requested;
|
|
140
59
|
}
|
|
141
|
-
|
|
60
|
+
const updatedAtMs = input.updatedAt ? Date.parse(input.updatedAt) : NaN;
|
|
61
|
+
if (!Number.isFinite(updatedAtMs)) return 1;
|
|
62
|
+
const remainingMs = Math.max(
|
|
63
|
+
0,
|
|
64
|
+
updatedAtMs +
|
|
65
|
+
(input.stalenessFloorMs ?? RUNNING_RECEIPT_DEAD_OWNER_STALENESS_MS) -
|
|
66
|
+
(input.nowMs ?? Date.now()),
|
|
67
|
+
);
|
|
68
|
+
const attemptsUntilReclaim = Math.max(
|
|
69
|
+
1,
|
|
70
|
+
Math.floor(
|
|
71
|
+
remainingMs / (input.pollIntervalMs ?? RUNNING_RECEIPT_POLL_INTERVAL_MS),
|
|
72
|
+
) + 1,
|
|
73
|
+
);
|
|
74
|
+
return Math.min(requested, attemptsUntilReclaim);
|
|
142
75
|
}
|
|
143
76
|
|
|
144
77
|
export function canReclaimFailedWorkerToolReceipt(input: {
|
|
@@ -400,7 +400,7 @@ export type StepOptions<Row, Value = unknown> = {
|
|
|
400
400
|
* Legacy dataset-column recompute flag accepted for older authored plays.
|
|
401
401
|
*
|
|
402
402
|
* Prefer putting freshness on the actual reusable call
|
|
403
|
-
* (`ctx.tools.execute`, `ctx.step`,
|
|
403
|
+
* (`ctx.tools.execute`, `ctx.step`, or `ctx.fetch`).
|
|
404
404
|
*/
|
|
405
405
|
readonly recompute?: boolean;
|
|
406
406
|
/** Legacy error-recompute flag accepted for older authored plays. */
|
|
@@ -889,7 +889,7 @@ export interface DeeplinePlayRuntimeContext {
|
|
|
889
889
|
key: string,
|
|
890
890
|
playRef: string | PlayReferenceLike,
|
|
891
891
|
input: Record<string, unknown>,
|
|
892
|
-
options: { description: string
|
|
892
|
+
options: { description: string },
|
|
893
893
|
): Promise<TOutput>;
|
|
894
894
|
|
|
895
895
|
/**
|
|
@@ -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.
|
|
109
|
+
version: '0.1.210',
|
|
110
110
|
apiContract: '2026-06-dataset-handle-results-hard-cutover',
|
|
111
111
|
supportPolicy: {
|
|
112
|
-
latest: '0.1.
|
|
112
|
+
latest: '0.1.210',
|
|
113
113
|
minimumSupported: '0.1.53',
|
|
114
114
|
deprecatedBelow: '0.1.53',
|
|
115
115
|
commandMinimumSupported: [
|
|
@@ -44,8 +44,7 @@ export interface ChildRunIdInputs {
|
|
|
44
44
|
key?: string | null;
|
|
45
45
|
/**
|
|
46
46
|
* Canonical durable runPlay semantic key when the launching substrate has one.
|
|
47
|
-
*
|
|
48
|
-
* receipt identity and child run identity cannot drift apart.
|
|
47
|
+
* This scopes each fresh child invocation to its parent call site and row.
|
|
49
48
|
*/
|
|
50
49
|
runPlaySemanticKey?: string | null;
|
|
51
50
|
/** Child input payload (semantic-fallback input). */
|
|
@@ -57,9 +56,9 @@ export interface ChildRunIdInputs {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
/**
|
|
60
|
-
* A failed
|
|
61
|
-
*
|
|
62
|
-
* lease is the attempt fence
|
|
59
|
+
* A failed child launch must use a fresh attempt identity while ordinary
|
|
60
|
+
* transport retries keep attaching to the original child. The caller-provided
|
|
61
|
+
* lease is the attempt fence.
|
|
63
62
|
*/
|
|
64
63
|
export function childSubmitIdempotencyKey(input: {
|
|
65
64
|
receiptKey: string;
|
|
@@ -107,7 +107,7 @@ import {
|
|
|
107
107
|
} from './tool-execute-retry-policy';
|
|
108
108
|
import {
|
|
109
109
|
buildDurableCtxCallCacheKey,
|
|
110
|
-
|
|
110
|
+
buildDurableRunPlayInvocationScope,
|
|
111
111
|
buildDurableToolAggregateProviderIdempotencyKey,
|
|
112
112
|
buildDurableToolAggregateReceiptKey,
|
|
113
113
|
buildDurableToolCallAuthScopeDigest,
|
|
@@ -819,7 +819,7 @@ function stableDigest(value: string): string {
|
|
|
819
819
|
return sha256Hex(value);
|
|
820
820
|
}
|
|
821
821
|
|
|
822
|
-
type DurableCtxOperation = 'step' | 'tool' | 'fetch'
|
|
822
|
+
type DurableCtxOperation = 'step' | 'tool' | 'fetch';
|
|
823
823
|
|
|
824
824
|
function durableCtxKey(input: {
|
|
825
825
|
orgId?: string | null;
|
|
@@ -6370,8 +6370,6 @@ export class PlayContextImpl {
|
|
|
6370
6370
|
`Unable to resolve play "${resolvedName}" for ctx.runPlay(...).`,
|
|
6371
6371
|
);
|
|
6372
6372
|
}
|
|
6373
|
-
const childRevisionFingerprint =
|
|
6374
|
-
resolvedPlayRevisionFingerprint(resolvedPlay);
|
|
6375
6373
|
const childExecutionDecision = resolveChildExecutionStrategy({
|
|
6376
6374
|
pipeline: resolvedPlay.staticPipeline,
|
|
6377
6375
|
});
|
|
@@ -6413,14 +6411,12 @@ export class PlayContextImpl {
|
|
|
6413
6411
|
),
|
|
6414
6412
|
);
|
|
6415
6413
|
}
|
|
6416
|
-
const
|
|
6414
|
+
const runPlayInvocationScope = buildDurableRunPlayInvocationScope({
|
|
6417
6415
|
childPlayName: resolvedName,
|
|
6418
|
-
childRevisionFingerprint,
|
|
6419
6416
|
input,
|
|
6420
6417
|
rowScope: runPlayRowScope
|
|
6421
6418
|
? {
|
|
6422
6419
|
fieldName: runPlayRowScope.fieldName,
|
|
6423
|
-
rowId: runPlayRowScope.rowId,
|
|
6424
6420
|
rowKey: runPlayRowScope.rowKey ?? null,
|
|
6425
6421
|
tableNamespace: runPlayRowScope.tableNamespace ?? null,
|
|
6426
6422
|
}
|
|
@@ -6458,7 +6454,7 @@ export class PlayContextImpl {
|
|
|
6458
6454
|
parentRunId: this.currentRunId,
|
|
6459
6455
|
parentPlayName: this.#options.playName,
|
|
6460
6456
|
key: normalizedKey,
|
|
6461
|
-
runPlaySemanticKey,
|
|
6457
|
+
runPlaySemanticKey: runPlayInvocationScope,
|
|
6462
6458
|
input,
|
|
6463
6459
|
graphHash: null,
|
|
6464
6460
|
}),
|
|
@@ -6700,24 +6696,7 @@ export class PlayContextImpl {
|
|
|
6700
6696
|
}
|
|
6701
6697
|
};
|
|
6702
6698
|
|
|
6703
|
-
return await
|
|
6704
|
-
'runPlay',
|
|
6705
|
-
normalizedKey,
|
|
6706
|
-
this.currentRunId,
|
|
6707
|
-
{
|
|
6708
|
-
semanticKey: runPlaySemanticKey,
|
|
6709
|
-
staleAfterSeconds: options?.staleAfterSeconds,
|
|
6710
|
-
markSkipped: () => {
|
|
6711
|
-
this.log(
|
|
6712
|
-
`ctx.runPlay(${normalizedKey}): no-op due completed receipt`,
|
|
6713
|
-
);
|
|
6714
|
-
},
|
|
6715
|
-
repairRunningReceiptForSameRunAfterWaitTimeout: true,
|
|
6716
|
-
runningReceiptWaitMaxAttempts:
|
|
6717
|
-
resolveRuntimeToolReceiptWaitMaxAttempts(input),
|
|
6718
|
-
execute: executePlayCall,
|
|
6719
|
-
},
|
|
6720
|
-
);
|
|
6699
|
+
return await executePlayCall();
|
|
6721
6700
|
} finally {
|
|
6722
6701
|
scheduledChildPlayCall?.release();
|
|
6723
6702
|
}
|
|
@@ -281,7 +281,6 @@ export type CustomerDbQueryHandler = (
|
|
|
281
281
|
|
|
282
282
|
export interface PlayCallOptions {
|
|
283
283
|
description?: string;
|
|
284
|
-
staleAfterSeconds?: number;
|
|
285
284
|
}
|
|
286
285
|
|
|
287
286
|
export interface StepOptions {
|
|
@@ -591,7 +590,7 @@ export interface ContextOptions {
|
|
|
591
590
|
runId?: string;
|
|
592
591
|
/** Physical executor run that owns leases for an inline child context. */
|
|
593
592
|
runtimeReceiptOwnerRunId?: string;
|
|
594
|
-
/** Logical invocation scope for
|
|
593
|
+
/** Logical invocation scope for receipts created inside an inline child. */
|
|
595
594
|
runtimeReceiptScope?: string;
|
|
596
595
|
runAttempt?: number | null;
|
|
597
596
|
/**
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
export const DURABLE_CALL_CACHE_POLICY_VERSION = 'call-cache-v1';
|
|
11
11
|
|
|
12
|
-
export type DurableCallKind = 'tool' | 'step' | 'fetch'
|
|
12
|
+
export type DurableCallKind = 'tool' | 'step' | 'fetch';
|
|
13
13
|
|
|
14
14
|
function validateStaleAfterSeconds(staleAfterSeconds?: number | null): void {
|
|
15
15
|
if (staleAfterSeconds === undefined || staleAfterSeconds === null) {
|
|
@@ -121,16 +121,14 @@ export function buildDurableCtxCallCacheKey(input: {
|
|
|
121
121
|
return `ctx:${orgId}:call:${input.kind}:${normalizedId}:${digest}`;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export function
|
|
124
|
+
export function buildDurableRunPlayInvocationScope(input: {
|
|
125
125
|
childPlayName: string;
|
|
126
126
|
input: Record<string, unknown>;
|
|
127
|
-
childRevisionFingerprint?: string | null;
|
|
128
127
|
rowScope?: Record<string, unknown> | null;
|
|
129
128
|
}): string {
|
|
130
129
|
return sha256Hex(
|
|
131
130
|
stableStringify({
|
|
132
131
|
childPlayName: input.childPlayName.trim(),
|
|
133
|
-
childRevisionFingerprint: input.childRevisionFingerprint ?? null,
|
|
134
132
|
input: input.input,
|
|
135
133
|
rowScope: input.rowScope ?? null,
|
|
136
134
|
}),
|
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.
|
|
626
|
+
version: "0.1.210",
|
|
627
627
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
628
628
|
supportPolicy: {
|
|
629
|
-
latest: "0.1.
|
|
629
|
+
latest: "0.1.210",
|
|
630
630
|
minimumSupported: "0.1.53",
|
|
631
631
|
deprecatedBelow: "0.1.53",
|
|
632
632
|
commandMinimumSupported: [
|
|
@@ -17215,12 +17215,9 @@ function renderColumnRunIfFunction(command) {
|
|
|
17215
17215
|
if (!command.run_if_js) {
|
|
17216
17216
|
return null;
|
|
17217
17217
|
}
|
|
17218
|
-
|
|
17219
|
-
return `(__dlRawRow) => { const row = __dlPrepareEnrichRow(__dlRawRow, [${stringLiteral(command.alias)}]); const input = row; const context = row;
|
|
17218
|
+
return `(__dlRawRow) => { const row = __dlPrepareEnrichRow(__dlRawRow, [${stringLiteral(command.alias)}]); const input = row; const context = row;
|
|
17220
17219
|
${indent(renderJavascriptBody(command.run_if_js), 6)}
|
|
17221
17220
|
}`;
|
|
17222
|
-
}
|
|
17223
|
-
return renderRunIfFunction(command);
|
|
17224
17221
|
}
|
|
17225
17222
|
function renderCombinedRunIfFunction(precheck, runIfSource) {
|
|
17226
17223
|
if (!runIfSource) {
|
package/dist/cli/index.mjs
CHANGED
|
@@ -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.
|
|
611
|
+
version: "0.1.210",
|
|
612
612
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
613
613
|
supportPolicy: {
|
|
614
|
-
latest: "0.1.
|
|
614
|
+
latest: "0.1.210",
|
|
615
615
|
minimumSupported: "0.1.53",
|
|
616
616
|
deprecatedBelow: "0.1.53",
|
|
617
617
|
commandMinimumSupported: [
|
|
@@ -17244,12 +17244,9 @@ function renderColumnRunIfFunction(command) {
|
|
|
17244
17244
|
if (!command.run_if_js) {
|
|
17245
17245
|
return null;
|
|
17246
17246
|
}
|
|
17247
|
-
|
|
17248
|
-
return `(__dlRawRow) => { const row = __dlPrepareEnrichRow(__dlRawRow, [${stringLiteral(command.alias)}]); const input = row; const context = row;
|
|
17247
|
+
return `(__dlRawRow) => { const row = __dlPrepareEnrichRow(__dlRawRow, [${stringLiteral(command.alias)}]); const input = row; const context = row;
|
|
17249
17248
|
${indent(renderJavascriptBody(command.run_if_js), 6)}
|
|
17250
17249
|
}`;
|
|
17251
|
-
}
|
|
17252
|
-
return renderRunIfFunction(command);
|
|
17253
17250
|
}
|
|
17254
17251
|
function renderCombinedRunIfFunction(precheck, runIfSource) {
|
|
17255
17252
|
if (!runIfSource) {
|
package/dist/index.d.mts
CHANGED
|
@@ -3219,7 +3219,7 @@ type StepOptions<Row, Value = unknown> = {
|
|
|
3219
3219
|
* Legacy dataset-column recompute flag accepted for older authored plays.
|
|
3220
3220
|
*
|
|
3221
3221
|
* Prefer putting freshness on the actual reusable call
|
|
3222
|
-
* (`ctx.tools.execute`, `ctx.step`,
|
|
3222
|
+
* (`ctx.tools.execute`, `ctx.step`, or `ctx.fetch`).
|
|
3223
3223
|
*/
|
|
3224
3224
|
readonly recompute?: boolean;
|
|
3225
3225
|
/** Legacy error-recompute flag accepted for older authored plays. */
|
|
@@ -3607,7 +3607,6 @@ interface DeeplinePlayRuntimeContext {
|
|
|
3607
3607
|
*/
|
|
3608
3608
|
runPlay<TOutput = unknown>(key: string, playRef: string | PlayReferenceLike, input: Record<string, unknown>, options: {
|
|
3609
3609
|
description: string;
|
|
3610
|
-
staleAfterSeconds?: number;
|
|
3611
3610
|
}): Promise<TOutput>;
|
|
3612
3611
|
/**
|
|
3613
3612
|
* Emit a log line visible in `play tail` and the play's progress logs.
|
package/dist/index.d.ts
CHANGED
|
@@ -3219,7 +3219,7 @@ type StepOptions<Row, Value = unknown> = {
|
|
|
3219
3219
|
* Legacy dataset-column recompute flag accepted for older authored plays.
|
|
3220
3220
|
*
|
|
3221
3221
|
* Prefer putting freshness on the actual reusable call
|
|
3222
|
-
* (`ctx.tools.execute`, `ctx.step`,
|
|
3222
|
+
* (`ctx.tools.execute`, `ctx.step`, or `ctx.fetch`).
|
|
3223
3223
|
*/
|
|
3224
3224
|
readonly recompute?: boolean;
|
|
3225
3225
|
/** Legacy error-recompute flag accepted for older authored plays. */
|
|
@@ -3607,7 +3607,6 @@ interface DeeplinePlayRuntimeContext {
|
|
|
3607
3607
|
*/
|
|
3608
3608
|
runPlay<TOutput = unknown>(key: string, playRef: string | PlayReferenceLike, input: Record<string, unknown>, options: {
|
|
3609
3609
|
description: string;
|
|
3610
|
-
staleAfterSeconds?: number;
|
|
3611
3610
|
}): Promise<TOutput>;
|
|
3612
3611
|
/**
|
|
3613
3612
|
* Emit a log line visible in `play tail` and the play's progress logs.
|
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.
|
|
425
|
+
version: "0.1.210",
|
|
426
426
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
427
427
|
supportPolicy: {
|
|
428
|
-
latest: "0.1.
|
|
428
|
+
latest: "0.1.210",
|
|
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.
|
|
355
|
+
version: "0.1.210",
|
|
356
356
|
apiContract: "2026-06-dataset-handle-results-hard-cutover",
|
|
357
357
|
supportPolicy: {
|
|
358
|
-
latest: "0.1.
|
|
358
|
+
latest: "0.1.210",
|
|
359
359
|
minimumSupported: "0.1.53",
|
|
360
360
|
deprecatedBelow: "0.1.53",
|
|
361
361
|
commandMinimumSupported: [
|