deepline 0.1.201 → 0.1.202
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 +152 -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 +11 -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.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 +198 -76
- package/dist/cli/index.mjs +204 -82
- 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
|
);
|
|
@@ -5960,6 +6005,11 @@ export class PlayContextImpl {
|
|
|
5960
6005
|
authScopeAttempt < 2;
|
|
5961
6006
|
authScopeAttempt += 1
|
|
5962
6007
|
) {
|
|
6008
|
+
// Capture the lease the receipt store hands to `execute` for THIS
|
|
6009
|
+
// attempt's key. On an auth-scope re-claim we mark that old-scope receipt
|
|
6010
|
+
// `skipped` (below), and the skip is fenced on this run+attempt+lease so
|
|
6011
|
+
// it can only supersede the receipt this attempt owns.
|
|
6012
|
+
let claimedReceiptLeaseId: string | null = null;
|
|
5963
6013
|
try {
|
|
5964
6014
|
return await this.executeWithRuntimeReceipt(
|
|
5965
6015
|
'tool',
|
|
@@ -5991,7 +6041,10 @@ export class PlayContextImpl {
|
|
|
5991
6041
|
shouldPersistFailure: (error) =>
|
|
5992
6042
|
!(error instanceof ToolExecuteAuthScopeChangedError),
|
|
5993
6043
|
markRunningBeforeExecute: false,
|
|
5994
|
-
execute: ({ leaseId }) =>
|
|
6044
|
+
execute: ({ leaseId }) => {
|
|
6045
|
+
claimedReceiptLeaseId = leaseId;
|
|
6046
|
+
return executeTool({ leaseId });
|
|
6047
|
+
},
|
|
5995
6048
|
runningReceiptWaitMaxAttempts:
|
|
5996
6049
|
resolveRuntimeToolReceiptWaitMaxAttempts(
|
|
5997
6050
|
typeof options?.receiptWaitMs === 'number'
|
|
@@ -6007,6 +6060,36 @@ export class PlayContextImpl {
|
|
|
6007
6060
|
) {
|
|
6008
6061
|
throw error;
|
|
6009
6062
|
}
|
|
6063
|
+
// Supersede the old-scope receipt so the run's node projection does not
|
|
6064
|
+
// surface it as `failed`. shouldPersistFailure kept it out of the failed
|
|
6065
|
+
// state, but the claim left it `running` with a now-orphaned lease under
|
|
6066
|
+
// the OLD cache key; without an explicit terminal it projects as a failed
|
|
6067
|
+
// node even though the work is about to be re-done under the new scope.
|
|
6068
|
+
// `skipped` is the terminal for superseded work; the skip is fenced on
|
|
6069
|
+
// this run+attempt+lease so it can only touch the receipt this attempt
|
|
6070
|
+
// owns. Best-effort: a skip that cannot fence (store returns null) must
|
|
6071
|
+
// not mask the reclaim itself.
|
|
6072
|
+
const supersededReceiptKey = durableCacheKey;
|
|
6073
|
+
try {
|
|
6074
|
+
const skipped = await this.skipRuntimeStepReceipt(
|
|
6075
|
+
supersededReceiptKey,
|
|
6076
|
+
this.currentRunId,
|
|
6077
|
+
claimedReceiptLeaseId,
|
|
6078
|
+
);
|
|
6079
|
+
if (skipped) {
|
|
6080
|
+
this.log(
|
|
6081
|
+
`ctx.tools.execute(${toolId}): auth_scope_changed_superseded ` +
|
|
6082
|
+
`old-scope receipt ${toolRequestIdentity} (label: ${normalizedKey}) ` +
|
|
6083
|
+
`marked skipped so the reclaim does not project as a failed node.`,
|
|
6084
|
+
);
|
|
6085
|
+
}
|
|
6086
|
+
} catch (skipError) {
|
|
6087
|
+
this.log(
|
|
6088
|
+
`ctx.tools.execute(${toolId}): auth_scope_changed_supersede_skip_failed ` +
|
|
6089
|
+
`(label: ${normalizedKey}); could not mark the old-scope receipt ` +
|
|
6090
|
+
`skipped: ${this.formatRuntimeError(skipError)}`,
|
|
6091
|
+
);
|
|
6092
|
+
}
|
|
6010
6093
|
// Make the bounded auth-scope re-claim observable in run logs instead
|
|
6011
6094
|
// of a silent continuation: the credential identity changed after the
|
|
6012
6095
|
// receipt key was prepared, so we evict the cached digest, re-resolve,
|
|
@@ -6213,7 +6296,7 @@ export class PlayContextImpl {
|
|
|
6213
6296
|
input,
|
|
6214
6297
|
graphHash: null,
|
|
6215
6298
|
});
|
|
6216
|
-
const childGovernance = this.governor.forkChild({
|
|
6299
|
+
const childGovernance = await this.governor.forkChild({
|
|
6217
6300
|
childPlayName: resolvedName,
|
|
6218
6301
|
childRunId,
|
|
6219
6302
|
});
|
|
@@ -6338,6 +6421,12 @@ export class PlayContextImpl {
|
|
|
6338
6421
|
this.consumePendingChildPlaySuspension() ?? suspension,
|
|
6339
6422
|
);
|
|
6340
6423
|
}
|
|
6424
|
+
await this.registerInlineChildRun({
|
|
6425
|
+
parentRunId: this.currentRunId,
|
|
6426
|
+
childRunId: childGovernance.currentRunId,
|
|
6427
|
+
childPlayName: resolvedName,
|
|
6428
|
+
staticPipeline: resolvedPlay.staticPipeline ?? null,
|
|
6429
|
+
});
|
|
6341
6430
|
const childContext = createPlayContext({
|
|
6342
6431
|
...this.#options,
|
|
6343
6432
|
playId: resolvedName,
|
|
@@ -6496,6 +6585,40 @@ export class PlayContextImpl {
|
|
|
6496
6585
|
return body.executorToken.trim();
|
|
6497
6586
|
}
|
|
6498
6587
|
|
|
6588
|
+
private async registerInlineChildRun(input: {
|
|
6589
|
+
parentRunId: string;
|
|
6590
|
+
childRunId: string;
|
|
6591
|
+
childPlayName: string;
|
|
6592
|
+
staticPipeline: unknown;
|
|
6593
|
+
}): Promise<void> {
|
|
6594
|
+
const baseUrl = this.#options.baseUrl?.trim();
|
|
6595
|
+
const executorToken = this.#options.executorToken?.trim();
|
|
6596
|
+
if (!baseUrl || !executorToken) return;
|
|
6597
|
+
|
|
6598
|
+
await startRunViaAppRuntime(
|
|
6599
|
+
{
|
|
6600
|
+
baseUrl,
|
|
6601
|
+
executorToken,
|
|
6602
|
+
integrationMode: this.#options.integrationMode ?? null,
|
|
6603
|
+
vercelProtectionBypassToken:
|
|
6604
|
+
this.#options.vercelProtectionBypassToken ?? null,
|
|
6605
|
+
},
|
|
6606
|
+
{
|
|
6607
|
+
playName: input.childPlayName,
|
|
6608
|
+
runId: input.childRunId,
|
|
6609
|
+
parentRunId: input.parentRunId,
|
|
6610
|
+
rootRunId: this.governance.rootRunId,
|
|
6611
|
+
workflowFamilyKey: this.governance.rootRunId,
|
|
6612
|
+
artifactHash: this.#options.artifactHash ?? null,
|
|
6613
|
+
graphHash: this.#options.graphHash ?? null,
|
|
6614
|
+
schedulerSchema: this.#options.runtimeSchedulerSchema ?? null,
|
|
6615
|
+
executionProfile: this.#options.childRunProfile ?? 'absurd',
|
|
6616
|
+
staticPipeline: input.staticPipeline,
|
|
6617
|
+
source: 'published',
|
|
6618
|
+
},
|
|
6619
|
+
);
|
|
6620
|
+
}
|
|
6621
|
+
|
|
6499
6622
|
/**
|
|
6500
6623
|
* Extract a list from a tool result.
|
|
6501
6624
|
* e.g. ctx.extractList(result, 'people', ['first_name', 'last_name', 'email'])
|
|
@@ -7596,22 +7719,6 @@ export class PlayContextImpl {
|
|
|
7596
7719
|
},
|
|
7597
7720
|
});
|
|
7598
7721
|
} 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
7722
|
const completionBuffer: Array<{
|
|
7616
7723
|
request: ToolCallRequest;
|
|
7617
7724
|
result: unknown | null;
|
|
@@ -7675,8 +7782,29 @@ export class PlayContextImpl {
|
|
|
7675
7782
|
void flushCompletionBuffer();
|
|
7676
7783
|
}, 0);
|
|
7677
7784
|
});
|
|
7678
|
-
|
|
7785
|
+
// Seed the dispatch width from the governor's provider-shaped
|
|
7786
|
+
// parallelism instead of the flat policy.concurrency.toolCalls
|
|
7787
|
+
// ceiling. The flat width launched every pending row at once into
|
|
7788
|
+
// the pacer, so unhinted providers 429'd on the first burst before
|
|
7789
|
+
// AIMD could halve the rate. The shaped estimate is derived from the
|
|
7790
|
+
// provider's RPS/maxConcurrency pacing rules (see
|
|
7791
|
+
// suggestedParallelism in governor.ts), floored at 1 and capped by
|
|
7792
|
+
// the global tool-call concurrency ceiling. The pacer still gates
|
|
7793
|
+
// each in-flight call, so this only trims the launch burst.
|
|
7794
|
+
const toolCallConcurrencyCeiling =
|
|
7795
|
+
this.governor.policy.concurrency.toolCalls;
|
|
7796
|
+
const shapedToolParallelism =
|
|
7797
|
+
await this.resourceGovernor.suggestedToolParallelism(
|
|
7798
|
+
toolId,
|
|
7799
|
+
toolCallConcurrencyCeiling,
|
|
7800
|
+
);
|
|
7801
|
+
const dispatchWidth = Math.min(
|
|
7802
|
+
toolCallConcurrencyCeiling,
|
|
7803
|
+
Math.max(1, shapedToolParallelism),
|
|
7804
|
+
);
|
|
7805
|
+
const dispatchResults = await dispatchBoundedSettled(
|
|
7679
7806
|
pendingRequests,
|
|
7807
|
+
dispatchWidth,
|
|
7680
7808
|
async (request) => {
|
|
7681
7809
|
// Circuit breaker: do not dispatch this provider call once a
|
|
7682
7810
|
// persistence failure has occurred in this run.
|
|
@@ -8021,6 +8149,7 @@ export class PlayContextImpl {
|
|
|
8021
8149
|
}),
|
|
8022
8150
|
});
|
|
8023
8151
|
this.resourceGovernor.observe({
|
|
8152
|
+
toolId,
|
|
8024
8153
|
providerResourceKey: `tool:${toolId}`,
|
|
8025
8154
|
providerLatencyMs: Date.now() - providerCallStartedAt,
|
|
8026
8155
|
providerSuccess: response.ok,
|
|
@@ -8044,7 +8173,7 @@ export class PlayContextImpl {
|
|
|
8044
8173
|
const message =
|
|
8045
8174
|
error instanceof Error ? error.message : String(error);
|
|
8046
8175
|
if (transportAttempt < TOOL_EXECUTE_TRANSPORT_MAX_ATTEMPTS) {
|
|
8047
|
-
this.governor.chargeBudget('retry');
|
|
8176
|
+
await this.governor.chargeBudget('retry');
|
|
8048
8177
|
const retryAfterMs =
|
|
8049
8178
|
TOOL_RETRY_AFTER_FALLBACK_MS * transportAttempt;
|
|
8050
8179
|
span.setAttribute(
|
|
@@ -8102,7 +8231,7 @@ export class PlayContextImpl {
|
|
|
8102
8231
|
}
|
|
8103
8232
|
if (failure.shouldRetry) {
|
|
8104
8233
|
if (failure.chargeRetryBudget) {
|
|
8105
|
-
this.governor.chargeBudget('retry');
|
|
8234
|
+
await this.governor.chargeBudget('retry');
|
|
8106
8235
|
}
|
|
8107
8236
|
const retryAttributePrefix = failure.isRateLimit
|
|
8108
8237
|
? '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';
|
|
@@ -489,6 +490,13 @@ export interface ContextOptions {
|
|
|
489
490
|
* to the `absurd` profile.
|
|
490
491
|
*/
|
|
491
492
|
childRunProfile?: string | null;
|
|
493
|
+
/**
|
|
494
|
+
* The parent worker's own absurd release lane id. Threaded onto child
|
|
495
|
+
* `ctx.runPlay` launches via the `x-deepline-absurd-release` header so the
|
|
496
|
+
* child pins to the parent's lane (defense-in-depth). Null off the absurd
|
|
497
|
+
* path or on a pre-release parent.
|
|
498
|
+
*/
|
|
499
|
+
absurdReleaseId?: string | null;
|
|
492
500
|
staticPipeline?: PlayStaticPipeline | null;
|
|
493
501
|
workflowId?: string;
|
|
494
502
|
sessionId?: string;
|
|
@@ -561,6 +569,9 @@ export interface ContextOptions {
|
|
|
561
569
|
* contexts omit this and use the in-process backend.
|
|
562
570
|
*/
|
|
563
571
|
rateState?: RateStateBackend;
|
|
572
|
+
/** Shared root-run budget reservations. Production schedulers must provide
|
|
573
|
+
* the same durable backend to every child sandbox. */
|
|
574
|
+
budgetState?: BudgetStateBackend;
|
|
564
575
|
/**
|
|
565
576
|
* Loud guard for production Node substrates: when true, constructing a
|
|
566
577
|
* 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
|
+
}
|