deepline 0.1.201 → 0.1.203
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/entry.ts +30 -9
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +25 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +139 -124
- package/dist/bundling-sources/sdk/src/release.ts +2 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +17 -2
- package/dist/bundling-sources/shared_libs/play-runtime/bounded-dispatch.ts +52 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +154 -23
- package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +121 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/adaptive-admission.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts +22 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +86 -19
- package/dist/bundling-sources/shared_libs/play-runtime/governor/block-reserving-budget-state-backend.ts +141 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/budget-state-backend.ts +43 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +129 -41
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +40 -2
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-completion-sink.ts +92 -33
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +13 -5
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +122 -120
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +10 -8
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +140 -32
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-paths.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +225 -29
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +21 -0
- package/dist/bundling-sources/shared_libs/plays/dataset.ts +32 -10
- package/dist/cli/index.js +335 -77
- package/dist/cli/index.mjs +341 -83
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/adaptive-tool-dispatcher.ts +0 -355
|
@@ -24,12 +24,12 @@ import {
|
|
|
24
24
|
compileRequestsWithStrategy,
|
|
25
25
|
executeChunkedRequests,
|
|
26
26
|
} from './batch-runtime';
|
|
27
|
-
import { AdaptiveToolDispatcher } from './adaptive-tool-dispatcher';
|
|
28
27
|
import {
|
|
29
28
|
PLAY_RUNTIME_WORK_RECEIPT_LEASE_TTL_MS,
|
|
30
29
|
runtimeLeaseHeartbeatIntervalFromExpiry,
|
|
31
30
|
} from './lease-policy';
|
|
32
31
|
import { createRuntimeReceiptHeartbeatSupervisor } from './receipt-heartbeat-supervisor';
|
|
32
|
+
import { dispatchBoundedSettled } from './bounded-dispatch';
|
|
33
33
|
import type { PlayQueueHint } from './governor/rate-state-backend';
|
|
34
34
|
import type { MapRowOutcome } from './durability-store';
|
|
35
35
|
import type { WorkReceiptFailureKind } from './work-receipts';
|
|
@@ -43,6 +43,7 @@ import { RuntimeSheetRowsBlockedError } from './runtime-sheet-errors';
|
|
|
43
43
|
import { buildChildRunId } from './child-run-id';
|
|
44
44
|
import type { PlayCallGovernanceSnapshot } from './scheduler-backend';
|
|
45
45
|
import {
|
|
46
|
+
ABSURD_RELEASE_OVERRIDE_HEADER,
|
|
46
47
|
RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER,
|
|
47
48
|
SYNTHETIC_RUN_HEADER,
|
|
48
49
|
} from './coordinator-headers';
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
PLAY_RUNTIME_API_COMPAT_PATH,
|
|
51
52
|
PLAY_RUNTIME_CHILD_EXECUTOR_TOKEN_COMPAT_PATH,
|
|
52
53
|
} from './runtime-api-paths';
|
|
54
|
+
import { startRunViaAppRuntime } from './app-runtime-api';
|
|
53
55
|
import { vercelProtectionBypassHeader } from './vercel-protection';
|
|
54
56
|
export {
|
|
55
57
|
RuntimeSheetRowsBlockedError,
|
|
@@ -163,6 +165,8 @@ import {
|
|
|
163
165
|
createRuntimeMapRetainedRowsTracker,
|
|
164
166
|
estimateRuntimeMapRowsMemory,
|
|
165
167
|
resolveRuntimeMapRowAdmission,
|
|
168
|
+
NODE_RUNTIME_MAP_MATERIALIZED_MEMORY_MULTIPLIER,
|
|
169
|
+
NODE_RUNTIME_MAP_ROW_OVERHEAD_BYTES,
|
|
166
170
|
runtimeMapJsonByteLength,
|
|
167
171
|
resolveRuntimeMapMemoryLimits,
|
|
168
172
|
RuntimeMapMemoryLimitError,
|
|
@@ -1208,6 +1212,7 @@ export class PlayContextImpl {
|
|
|
1208
1212
|
rootRunId: options.runId ?? options.workflowId ?? 'run',
|
|
1209
1213
|
},
|
|
1210
1214
|
rateState: options.rateState ?? new InMemoryRateStateBackend(),
|
|
1215
|
+
budgetState: options.budgetState,
|
|
1211
1216
|
resolvePacing: createPacingResolver(options.getToolQueueHints),
|
|
1212
1217
|
// A root run keeps depth 0 (its first child play is depth 1) but still
|
|
1213
1218
|
// seeds its own play id into the ancestry/currentPlayId so the cycle guard
|
|
@@ -1367,6 +1372,16 @@ export class PlayContextImpl {
|
|
|
1367
1372
|
[SYNTHETIC_RUN_HEADER]: '1',
|
|
1368
1373
|
}
|
|
1369
1374
|
: {}),
|
|
1375
|
+
// Defense-in-depth: pin the child to the parent worker's release lane so
|
|
1376
|
+
// a child always finishes on the release that launched its parent, even
|
|
1377
|
+
// if the resolver default (schema-derived) would drift. The run route
|
|
1378
|
+
// honors this only for internal ctx.runPlay child launches.
|
|
1379
|
+
...(this.#options.absurdReleaseId?.trim()
|
|
1380
|
+
? {
|
|
1381
|
+
[ABSURD_RELEASE_OVERRIDE_HEADER]:
|
|
1382
|
+
this.#options.absurdReleaseId.trim(),
|
|
1383
|
+
}
|
|
1384
|
+
: {}),
|
|
1370
1385
|
...(await this.vercelProtectionHeaders()),
|
|
1371
1386
|
};
|
|
1372
1387
|
}
|
|
@@ -1777,6 +1792,30 @@ export class PlayContextImpl {
|
|
|
1777
1792
|
};
|
|
1778
1793
|
}
|
|
1779
1794
|
|
|
1795
|
+
private async skipRuntimeStepReceipt(
|
|
1796
|
+
key: string,
|
|
1797
|
+
runId: string,
|
|
1798
|
+
leaseId?: string | null,
|
|
1799
|
+
): Promise<RuntimeStepReceipt | null> {
|
|
1800
|
+
if (!this.#options.skipRuntimeStepReceipt) {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
const skipped = await this.#options.skipRuntimeStepReceipt({
|
|
1804
|
+
key,
|
|
1805
|
+
runId,
|
|
1806
|
+
runAttempt: this.currentRunAttempt,
|
|
1807
|
+
...(leaseId ? { leaseId } : {}),
|
|
1808
|
+
});
|
|
1809
|
+
if (!skipped || typeof skipped.key !== 'string' || !skipped.key.trim()) {
|
|
1810
|
+
return null;
|
|
1811
|
+
}
|
|
1812
|
+
return {
|
|
1813
|
+
...skipped,
|
|
1814
|
+
key: skipped.key.trim(),
|
|
1815
|
+
runId: skipped.runId ?? null,
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1780
1819
|
private async heartbeatRuntimeStepReceipt(
|
|
1781
1820
|
key: string,
|
|
1782
1821
|
runId: string,
|
|
@@ -3701,7 +3740,13 @@ export class PlayContextImpl {
|
|
|
3701
3740
|
await flushChunk();
|
|
3702
3741
|
};
|
|
3703
3742
|
|
|
3704
|
-
for await (const page of iteratePlayDatasetInputPages(items
|
|
3743
|
+
for await (const page of iteratePlayDatasetInputPages(items, {
|
|
3744
|
+
maxPageBytes: mapMemoryLimits.materializedBudgetBytes,
|
|
3745
|
+
estimateRowBytes: (row) =>
|
|
3746
|
+
runtimeMapJsonByteLength(row) *
|
|
3747
|
+
NODE_RUNTIME_MAP_MATERIALIZED_MEMORY_MULTIPLIER +
|
|
3748
|
+
NODE_RUNTIME_MAP_ROW_OVERHEAD_BYTES,
|
|
3749
|
+
})) {
|
|
3705
3750
|
const pageRawItems = page.rows.map((item) =>
|
|
3706
3751
|
this.toOutputRow(item as Record<string, unknown>),
|
|
3707
3752
|
);
|
|
@@ -3961,6 +4006,7 @@ export class PlayContextImpl {
|
|
|
3961
4006
|
{
|
|
3962
4007
|
baseUrl: this.#options.baseUrl!,
|
|
3963
4008
|
executorToken: this.#options.executorToken!,
|
|
4009
|
+
dbSessionStrategy: this.#options.dbSessionStrategy,
|
|
3964
4010
|
playName: this.#options.playName!,
|
|
3965
4011
|
userEmail: this.#options.userEmail,
|
|
3966
4012
|
runId: this.#options.runId!,
|
|
@@ -4484,6 +4530,7 @@ export class PlayContextImpl {
|
|
|
4484
4530
|
{
|
|
4485
4531
|
baseUrl: this.#options.baseUrl!,
|
|
4486
4532
|
executorToken: this.#options.executorToken!,
|
|
4533
|
+
dbSessionStrategy: this.#options.dbSessionStrategy,
|
|
4487
4534
|
playName: this.#options.playName!,
|
|
4488
4535
|
userEmail: this.#options.userEmail,
|
|
4489
4536
|
runId: this.#options.runId!,
|
|
@@ -5960,6 +6007,11 @@ export class PlayContextImpl {
|
|
|
5960
6007
|
authScopeAttempt < 2;
|
|
5961
6008
|
authScopeAttempt += 1
|
|
5962
6009
|
) {
|
|
6010
|
+
// Capture the lease the receipt store hands to `execute` for THIS
|
|
6011
|
+
// attempt's key. On an auth-scope re-claim we mark that old-scope receipt
|
|
6012
|
+
// `skipped` (below), and the skip is fenced on this run+attempt+lease so
|
|
6013
|
+
// it can only supersede the receipt this attempt owns.
|
|
6014
|
+
let claimedReceiptLeaseId: string | null = null;
|
|
5963
6015
|
try {
|
|
5964
6016
|
return await this.executeWithRuntimeReceipt(
|
|
5965
6017
|
'tool',
|
|
@@ -5991,7 +6043,10 @@ export class PlayContextImpl {
|
|
|
5991
6043
|
shouldPersistFailure: (error) =>
|
|
5992
6044
|
!(error instanceof ToolExecuteAuthScopeChangedError),
|
|
5993
6045
|
markRunningBeforeExecute: false,
|
|
5994
|
-
execute: ({ leaseId }) =>
|
|
6046
|
+
execute: ({ leaseId }) => {
|
|
6047
|
+
claimedReceiptLeaseId = leaseId;
|
|
6048
|
+
return executeTool({ leaseId });
|
|
6049
|
+
},
|
|
5995
6050
|
runningReceiptWaitMaxAttempts:
|
|
5996
6051
|
resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
5997
6052
|
typeof options?.receiptWaitMs === 'number'
|
|
@@ -6007,6 +6062,36 @@ export class PlayContextImpl {
|
|
|
6007
6062
|
) {
|
|
6008
6063
|
throw error;
|
|
6009
6064
|
}
|
|
6065
|
+
// Supersede the old-scope receipt so the run's node projection does not
|
|
6066
|
+
// surface it as `failed`. shouldPersistFailure kept it out of the failed
|
|
6067
|
+
// state, but the claim left it `running` with a now-orphaned lease under
|
|
6068
|
+
// the OLD cache key; without an explicit terminal it projects as a failed
|
|
6069
|
+
// node even though the work is about to be re-done under the new scope.
|
|
6070
|
+
// `skipped` is the terminal for superseded work; the skip is fenced on
|
|
6071
|
+
// this run+attempt+lease so it can only touch the receipt this attempt
|
|
6072
|
+
// owns. Best-effort: a skip that cannot fence (store returns null) must
|
|
6073
|
+
// not mask the reclaim itself.
|
|
6074
|
+
const supersededReceiptKey = durableCacheKey;
|
|
6075
|
+
try {
|
|
6076
|
+
const skipped = await this.skipRuntimeStepReceipt(
|
|
6077
|
+
supersededReceiptKey,
|
|
6078
|
+
this.currentRunId,
|
|
6079
|
+
claimedReceiptLeaseId,
|
|
6080
|
+
);
|
|
6081
|
+
if (skipped) {
|
|
6082
|
+
this.log(
|
|
6083
|
+
`ctx.tools.execute(${toolId}): auth_scope_changed_superseded ` +
|
|
6084
|
+
`old-scope receipt ${toolRequestIdentity} (label: ${normalizedKey}) ` +
|
|
6085
|
+
`marked skipped so the reclaim does not project as a failed node.`,
|
|
6086
|
+
);
|
|
6087
|
+
}
|
|
6088
|
+
} catch (skipError) {
|
|
6089
|
+
this.log(
|
|
6090
|
+
`ctx.tools.execute(${toolId}): auth_scope_changed_supersede_skip_failed ` +
|
|
6091
|
+
`(label: ${normalizedKey}); could not mark the old-scope receipt ` +
|
|
6092
|
+
`skipped: ${this.formatRuntimeError(skipError)}`,
|
|
6093
|
+
);
|
|
6094
|
+
}
|
|
6010
6095
|
// Make the bounded auth-scope re-claim observable in run logs instead
|
|
6011
6096
|
// of a silent continuation: the credential identity changed after the
|
|
6012
6097
|
// receipt key was prepared, so we evict the cached digest, re-resolve,
|
|
@@ -6213,7 +6298,7 @@ export class PlayContextImpl {
|
|
|
6213
6298
|
input,
|
|
6214
6299
|
graphHash: null,
|
|
6215
6300
|
});
|
|
6216
|
-
const childGovernance = this.governor.forkChild({
|
|
6301
|
+
const childGovernance = await this.governor.forkChild({
|
|
6217
6302
|
childPlayName: resolvedName,
|
|
6218
6303
|
childRunId,
|
|
6219
6304
|
});
|
|
@@ -6338,6 +6423,12 @@ export class PlayContextImpl {
|
|
|
6338
6423
|
this.consumePendingChildPlaySuspension() ?? suspension,
|
|
6339
6424
|
);
|
|
6340
6425
|
}
|
|
6426
|
+
await this.registerInlineChildRun({
|
|
6427
|
+
parentRunId: this.currentRunId,
|
|
6428
|
+
childRunId: childGovernance.currentRunId,
|
|
6429
|
+
childPlayName: resolvedName,
|
|
6430
|
+
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
6431
|
+
});
|
|
6341
6432
|
const childContext = createPlayContext({
|
|
6342
6433
|
...this.#options,
|
|
6343
6434
|
playId: resolvedName,
|
|
@@ -6496,6 +6587,40 @@ export class PlayContextImpl {
|
|
|
6496
6587
|
return body.executorToken.trim();
|
|
6497
6588
|
}
|
|
6498
6589
|
|
|
6590
|
+
private async registerInlineChildRun(input: {
|
|
6591
|
+
parentRunId: string;
|
|
6592
|
+
childRunId: string;
|
|
6593
|
+
childPlayName: string;
|
|
6594
|
+
staticPipeline: unknown;
|
|
6595
|
+
}): Promise<void> {
|
|
6596
|
+
const baseUrl = this.#options.baseUrl?.trim();
|
|
6597
|
+
const executorToken = this.#options.executorToken?.trim();
|
|
6598
|
+
if (!baseUrl || !executorToken) return;
|
|
6599
|
+
|
|
6600
|
+
await startRunViaAppRuntime(
|
|
6601
|
+
{
|
|
6602
|
+
baseUrl,
|
|
6603
|
+
executorToken,
|
|
6604
|
+
integrationMode: this.#options.integrationMode ?? null,
|
|
6605
|
+
vercelProtectionBypassToken:
|
|
6606
|
+
this.#options.vercelProtectionBypassToken ?? null,
|
|
6607
|
+
},
|
|
6608
|
+
{
|
|
6609
|
+
playName: input.childPlayName,
|
|
6610
|
+
runId: input.childRunId,
|
|
6611
|
+
parentRunId: input.parentRunId,
|
|
6612
|
+
rootRunId: this.governance.rootRunId,
|
|
6613
|
+
workflowFamilyKey: this.governance.rootRunId,
|
|
6614
|
+
artifactHash: this.#options.artifactHash ?? null,
|
|
6615
|
+
graphHash: this.#options.graphHash ?? null,
|
|
6616
|
+
schedulerSchema: this.#options.runtimeSchedulerSchema ?? null,
|
|
6617
|
+
executionProfile: this.#options.childRunProfile ?? 'absurd',
|
|
6618
|
+
staticPipeline: input.staticPipeline,
|
|
6619
|
+
source: 'published',
|
|
6620
|
+
},
|
|
6621
|
+
);
|
|
6622
|
+
}
|
|
6623
|
+
|
|
6499
6624
|
/**
|
|
6500
6625
|
* Extract a list from a tool result.
|
|
6501
6626
|
* e.g. ctx.extractList(result, 'people', ['first_name', 'last_name', 'email'])
|
|
@@ -7596,22 +7721,6 @@ export class PlayContextImpl {
|
|
|
7596
7721
|
},
|
|
7597
7722
|
});
|
|
7598
7723
|
} else {
|
|
7599
|
-
const initialRequestsPerSecond =
|
|
7600
|
-
await this.resourceGovernor.suggestedToolParallelism(
|
|
7601
|
-
toolId,
|
|
7602
|
-
this.governor.policy.pacing.suggestedMaxParallelism,
|
|
7603
|
-
);
|
|
7604
|
-
const dispatcher = new AdaptiveToolDispatcher<
|
|
7605
|
-
ToolCallRequest,
|
|
7606
|
-
unknown
|
|
7607
|
-
>({
|
|
7608
|
-
initialRequestsPerSecond,
|
|
7609
|
-
maxRequestsPerSecond:
|
|
7610
|
-
this.governor.policy.pacing.suggestedMaxParallelism,
|
|
7611
|
-
maxConcurrency: this.governor.policy.concurrency.toolCalls,
|
|
7612
|
-
defaultLatencyMs: 1_000,
|
|
7613
|
-
safetyFactor: 1.5,
|
|
7614
|
-
});
|
|
7615
7724
|
const completionBuffer: Array<{
|
|
7616
7725
|
request: ToolCallRequest;
|
|
7617
7726
|
result: unknown | null;
|
|
@@ -7675,8 +7784,29 @@ export class PlayContextImpl {
|
|
|
7675
7784
|
void flushCompletionBuffer();
|
|
7676
7785
|
}, 0);
|
|
7677
7786
|
});
|
|
7678
|
-
|
|
7787
|
+
// Seed the dispatch width from the governor's provider-shaped
|
|
7788
|
+
// parallelism instead of the flat policy.concurrency.toolCalls
|
|
7789
|
+
// ceiling. The flat width launched every pending row at once into
|
|
7790
|
+
// the pacer, so unhinted providers 429'd on the first burst before
|
|
7791
|
+
// AIMD could halve the rate. The shaped estimate is derived from the
|
|
7792
|
+
// provider's RPS/maxConcurrency pacing rules (see
|
|
7793
|
+
// suggestedParallelism in governor.ts), floored at 1 and capped by
|
|
7794
|
+
// the global tool-call concurrency ceiling. The pacer still gates
|
|
7795
|
+
// each in-flight call, so this only trims the launch burst.
|
|
7796
|
+
const toolCallConcurrencyCeiling =
|
|
7797
|
+
this.governor.policy.concurrency.toolCalls;
|
|
7798
|
+
const shapedToolParallelism =
|
|
7799
|
+
await this.resourceGovernor.suggestedToolParallelism(
|
|
7800
|
+
toolId,
|
|
7801
|
+
toolCallConcurrencyCeiling,
|
|
7802
|
+
);
|
|
7803
|
+
const dispatchWidth = Math.min(
|
|
7804
|
+
toolCallConcurrencyCeiling,
|
|
7805
|
+
Math.max(1, shapedToolParallelism),
|
|
7806
|
+
);
|
|
7807
|
+
const dispatchResults = await dispatchBoundedSettled(
|
|
7679
7808
|
pendingRequests,
|
|
7809
|
+
dispatchWidth,
|
|
7680
7810
|
async (request) => {
|
|
7681
7811
|
// Circuit breaker: do not dispatch this provider call once a
|
|
7682
7812
|
// persistence failure has occurred in this run.
|
|
@@ -8021,6 +8151,7 @@ export class PlayContextImpl {
|
|
|
8021
8151
|
}),
|
|
8022
8152
|
});
|
|
8023
8153
|
this.resourceGovernor.observe({
|
|
8154
|
+
toolId,
|
|
8024
8155
|
providerResourceKey: `tool:${toolId}`,
|
|
8025
8156
|
providerLatencyMs: Date.now() - providerCallStartedAt,
|
|
8026
8157
|
providerSuccess: response.ok,
|
|
@@ -8044,7 +8175,7 @@ export class PlayContextImpl {
|
|
|
8044
8175
|
const message =
|
|
8045
8176
|
error instanceof Error ? error.message : String(error);
|
|
8046
8177
|
if (transportAttempt < TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS) {
|
|
8047
|
-
this.governor.chargeBudget('retry');
|
|
8178
|
+
await this.governor.chargeBudget('retry');
|
|
8048
8179
|
const retryAfterMs =
|
|
8049
8180
|
TOOL_RETRY_AFTER_FALLBACK_MS * transportAttempt;
|
|
8050
8181
|
span.setAttribute(
|
|
@@ -8102,7 +8233,7 @@ export class PlayContextImpl {
|
|
|
8102
8233
|
}
|
|
8103
8234
|
if (failure.shouldRetry) {
|
|
8104
8235
|
if (failure.chargeRetryBudget) {
|
|
8105
|
-
this.governor.chargeBudget('retry');
|
|
8236
|
+
await this.governor.chargeBudget('retry');
|
|
8106
8237
|
}
|
|
8107
8238
|
const retryAttributePrefix = failure.isRateLimit
|
|
8108
8239
|
? 'rate_limit'
|
|
@@ -28,6 +28,16 @@ export const WORKER_CALLBACK_URL_OVERRIDE_HEADER =
|
|
|
28
28
|
'x-deepline-worker-callback-url';
|
|
29
29
|
export const RUNTIME_SCHEDULER_SCHEMA_OVERRIDE_HEADER =
|
|
30
30
|
'x-deepline-runtime-scheduler-schema';
|
|
31
|
+
/**
|
|
32
|
+
* Child-launch defense-in-depth: an Absurd worker sets this to its own
|
|
33
|
+
* `currentAbsurdReleaseId()` when it launches a `ctx.runPlay` child via
|
|
34
|
+
* `/api/v2/plays/run`. The run route pins the child to this release lane over
|
|
35
|
+
* the resolver's schema-derived default, so a child always finishes on the
|
|
36
|
+
* release that launched its parent. Honored ONLY for internal `internalRunPlay`
|
|
37
|
+
* child launches (executor-token authed, lineage-validated). Absent =>
|
|
38
|
+
* inherit-when-absent back-compat: the resolver default applies unchanged.
|
|
39
|
+
*/
|
|
40
|
+
export const ABSURD_RELEASE_OVERRIDE_HEADER = 'x-deepline-absurd-release';
|
|
31
41
|
/**
|
|
32
42
|
* CLI→app marker (NOT a coordinator header — it lives here only because both
|
|
33
43
|
* the SDK HTTP client and the run route already import this module). Set by
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
RateStateBackend,
|
|
14
14
|
} from './governor/rate-state-backend';
|
|
15
15
|
import type { GovernanceSnapshot } from './governor/governor';
|
|
16
|
+
import type { BudgetStateBackend } from './governor/budget-state-backend';
|
|
16
17
|
import type { RuntimeMapMemoryLimits } from './map-memory-limits';
|
|
17
18
|
import type { AnyBatchOperationStrategy } from './batching-types';
|
|
18
19
|
import type { ToolResultMetadataInput } from './tool-result';
|
|
@@ -472,6 +473,11 @@ export interface ContextOptions {
|
|
|
472
473
|
/** Short-lived HMAC-signed internal token for tool callbacks. Required for cloud execution. */
|
|
473
474
|
executorToken?: string;
|
|
474
475
|
baseUrl?: string;
|
|
476
|
+
/**
|
|
477
|
+
* Runtime-sheet transport selected by the runner. Daytona sandboxes use the
|
|
478
|
+
* execution gateway and must never fall back to minting direct DB sessions.
|
|
479
|
+
*/
|
|
480
|
+
dbSessionStrategy?: 'preloaded' | 'sandbox_public_key' | 'gateway_only';
|
|
475
481
|
vercelProtectionBypassToken?: string | null;
|
|
476
482
|
/** Optional per-run integration execution mode for provider calls. */
|
|
477
483
|
integrationMode?: 'live' | 'eval_stub' | 'fixture';
|
|
@@ -489,6 +495,13 @@ export interface ContextOptions {
|
|
|
489
495
|
* to the `absurd` profile.
|
|
490
496
|
*/
|
|
491
497
|
childRunProfile?: string | null;
|
|
498
|
+
/**
|
|
499
|
+
* The parent worker's own absurd release lane id. Threaded onto child
|
|
500
|
+
* `ctx.runPlay` launches via the `x-deepline-absurd-release` header so the
|
|
501
|
+
* child pins to the parent's lane (defense-in-depth). Null off the absurd
|
|
502
|
+
* path or on a pre-release parent.
|
|
503
|
+
*/
|
|
504
|
+
absurdReleaseId?: string | null;
|
|
492
505
|
staticPipeline?: PlayStaticPipeline | null;
|
|
493
506
|
workflowId?: string;
|
|
494
507
|
sessionId?: string;
|
|
@@ -561,6 +574,9 @@ export interface ContextOptions {
|
|
|
561
574
|
* contexts omit this and use the in-process backend.
|
|
562
575
|
*/
|
|
563
576
|
rateState?: RateStateBackend;
|
|
577
|
+
/** Shared root-run budget reservations. Production schedulers must provide
|
|
578
|
+
* the same durable backend to every child sandbox. */
|
|
579
|
+
budgetState?: BudgetStateBackend;
|
|
564
580
|
/**
|
|
565
581
|
* Loud guard for production Node substrates: when true, constructing a
|
|
566
582
|
* context without a shared rate-state backend is a configuration error.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export const RUNTIME_EXECUTION_CAPABILITIES = [
|
|
2
|
+
'artifact.read',
|
|
3
|
+
'file.read',
|
|
4
|
+
'receipt.read',
|
|
5
|
+
'receipt.write',
|
|
6
|
+
'tool.inspect',
|
|
7
|
+
'tool.execute',
|
|
8
|
+
'event.wait',
|
|
9
|
+
'run.child',
|
|
10
|
+
'run.progress',
|
|
11
|
+
'sheet.read',
|
|
12
|
+
'sheet.write',
|
|
13
|
+
'db.session',
|
|
14
|
+
'rate.acquire',
|
|
15
|
+
'budget.charge',
|
|
16
|
+
'billing.write',
|
|
17
|
+
'egress.fetch',
|
|
18
|
+
'secret.resolve',
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export type RuntimeExecutionCapability =
|
|
22
|
+
(typeof RUNTIME_EXECUTION_CAPABILITIES)[number];
|
|
23
|
+
|
|
24
|
+
const CAPABILITY_SET = new Set<string>(RUNTIME_EXECUTION_CAPABILITIES);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Immutable authority persisted with a durable launch. It contains facts, not
|
|
28
|
+
* a bearer credential, so a scheduler can mint one short-lived token per leg.
|
|
29
|
+
*/
|
|
30
|
+
export type RuntimeAuthorityDescriptor = {
|
|
31
|
+
orgId: string;
|
|
32
|
+
workflowId: string;
|
|
33
|
+
runId: string;
|
|
34
|
+
billingRunId?: string | null;
|
|
35
|
+
playName: string;
|
|
36
|
+
allowedSecrets: string[];
|
|
37
|
+
maxCreditsPerRun?: number | null;
|
|
38
|
+
integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
|
|
39
|
+
synthetic?: boolean | null;
|
|
40
|
+
actorUserId?: string | null;
|
|
41
|
+
actorEmail?: string | null;
|
|
42
|
+
capabilities: RuntimeExecutionCapability[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const DEFAULT_RUNTIME_EXECUTION_CAPABILITIES = [
|
|
46
|
+
...RUNTIME_EXECUTION_CAPABILITIES,
|
|
47
|
+
] as const satisfies readonly RuntimeExecutionCapability[];
|
|
48
|
+
|
|
49
|
+
/** Daytona receives these safe-to-steal capabilities, never raw DB access. */
|
|
50
|
+
export const DAYTONA_RUNTIME_EXECUTION_CAPABILITIES =
|
|
51
|
+
DEFAULT_RUNTIME_EXECUTION_CAPABILITIES.filter(
|
|
52
|
+
(capability) => capability !== 'db.session',
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export function isRuntimeExecutionCapability(
|
|
56
|
+
value: unknown,
|
|
57
|
+
): value is RuntimeExecutionCapability {
|
|
58
|
+
return typeof value === 'string' && CAPABILITY_SET.has(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeRuntimeExecutionCapabilities(
|
|
62
|
+
values: readonly unknown[] | null | undefined,
|
|
63
|
+
): RuntimeExecutionCapability[] {
|
|
64
|
+
if (!Array.isArray(values)) return [];
|
|
65
|
+
return [...new Set(values.filter(isRuntimeExecutionCapability))].sort();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function hasRuntimeExecutionCapability(
|
|
69
|
+
values: readonly string[] | null | undefined,
|
|
70
|
+
capability: RuntimeExecutionCapability,
|
|
71
|
+
): boolean {
|
|
72
|
+
return Array.isArray(values) && values.includes(capability);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function requireRuntimeAuthorityDescriptor(
|
|
76
|
+
value: unknown,
|
|
77
|
+
): RuntimeAuthorityDescriptor {
|
|
78
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
'Durable play launch is missing runtimeAuthority. Re-submit the run with the current runtime contract.',
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const descriptor = value as Partial<RuntimeAuthorityDescriptor>;
|
|
84
|
+
const requiredStrings = [
|
|
85
|
+
descriptor.orgId,
|
|
86
|
+
descriptor.workflowId,
|
|
87
|
+
descriptor.runId,
|
|
88
|
+
descriptor.playName,
|
|
89
|
+
];
|
|
90
|
+
const capabilities = normalizeRuntimeExecutionCapabilities(
|
|
91
|
+
descriptor.capabilities,
|
|
92
|
+
);
|
|
93
|
+
if (
|
|
94
|
+
requiredStrings.some(
|
|
95
|
+
(entry) => typeof entry !== 'string' || entry.trim().length === 0,
|
|
96
|
+
) ||
|
|
97
|
+
capabilities.length === 0
|
|
98
|
+
) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
'Durable play launch contains an invalid runtimeAuthority descriptor.',
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
orgId: descriptor.orgId!,
|
|
105
|
+
workflowId: descriptor.workflowId!,
|
|
106
|
+
runId: descriptor.runId!,
|
|
107
|
+
billingRunId: descriptor.billingRunId ?? null,
|
|
108
|
+
playName: descriptor.playName!,
|
|
109
|
+
allowedSecrets: Array.isArray(descriptor.allowedSecrets)
|
|
110
|
+
? descriptor.allowedSecrets.filter(
|
|
111
|
+
(entry): entry is string => typeof entry === 'string',
|
|
112
|
+
)
|
|
113
|
+
: [],
|
|
114
|
+
maxCreditsPerRun: descriptor.maxCreditsPerRun ?? null,
|
|
115
|
+
integrationMode: descriptor.integrationMode ?? null,
|
|
116
|
+
synthetic: descriptor.synthetic === true,
|
|
117
|
+
actorUserId: descriptor.actorUserId ?? null,
|
|
118
|
+
actorEmail: descriptor.actorEmail ?? null,
|
|
119
|
+
capabilities,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -53,6 +53,7 @@ type ProviderState = {
|
|
|
53
53
|
retryAfterUntilMs: number | null;
|
|
54
54
|
latencyMsEwma: number | null;
|
|
55
55
|
fallbackRules: PacingRule[];
|
|
56
|
+
hasSeenBackpressure: boolean;
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
function finitePositive(value: number, fallback: number): number {
|
|
@@ -118,6 +119,7 @@ export function createInMemoryAdaptiveAdmission(
|
|
|
118
119
|
retryAfterUntilMs: null,
|
|
119
120
|
latencyMsEwma: null,
|
|
120
121
|
fallbackRules: [...input.fallbackRules],
|
|
122
|
+
hasSeenBackpressure: false,
|
|
121
123
|
};
|
|
122
124
|
states.set(key, state);
|
|
123
125
|
}
|
|
@@ -178,7 +180,9 @@ export function createInMemoryAdaptiveAdmission(
|
|
|
178
180
|
) {
|
|
179
181
|
state.currentRequestsPerSecond = Math.min(
|
|
180
182
|
maxRequestsPerSecond,
|
|
181
|
-
state.
|
|
183
|
+
state.hasSeenBackpressure
|
|
184
|
+
? state.currentRequestsPerSecond + additiveIncreasePerWindow
|
|
185
|
+
: state.currentRequestsPerSecond * 2,
|
|
182
186
|
);
|
|
183
187
|
state.lastIncreaseAtMs = currentTime;
|
|
184
188
|
state.successCountAtLastIncrease = state.successCount;
|
|
@@ -189,6 +193,7 @@ export function createInMemoryAdaptiveAdmission(
|
|
|
189
193
|
const state = getExistingState(input.orgId, input.provider);
|
|
190
194
|
if (!state) return;
|
|
191
195
|
state.rateLimitCount += 1;
|
|
196
|
+
state.hasSeenBackpressure = true;
|
|
192
197
|
state.currentRequestsPerSecond = Math.max(
|
|
193
198
|
minRequestsPerSecond,
|
|
194
199
|
Math.floor(state.currentRequestsPerSecond / 2),
|
package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-budget-state-backend.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chargeGovernorBudgetViaAppRuntime,
|
|
3
|
+
type WorkerRuntimeApiContext,
|
|
4
|
+
} from '../app-runtime-api';
|
|
5
|
+
import { BlockReservingBudgetStateBackend } from './block-reserving-budget-state-backend';
|
|
6
|
+
|
|
7
|
+
export class AppRuntimeBudgetStateBackend extends BlockReservingBudgetStateBackend {
|
|
8
|
+
constructor(
|
|
9
|
+
context: WorkerRuntimeApiContext,
|
|
10
|
+
schedulerSchema?: string | null,
|
|
11
|
+
) {
|
|
12
|
+
super(
|
|
13
|
+
async (input) =>
|
|
14
|
+
await chargeGovernorBudgetViaAppRuntime(context, {
|
|
15
|
+
rootRunId: input.rootRunId,
|
|
16
|
+
reservationId: input.reservationId,
|
|
17
|
+
charges: [...input.charges],
|
|
18
|
+
schedulerSchema: schedulerSchema ?? null,
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|