deepline 0.3.74 → 0.3.76
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/sdk/src/client.ts +279 -21
- package/dist/bundling-sources/sdk/src/config.ts +21 -3
- package/dist/bundling-sources/sdk/src/index.ts +1 -0
- package/dist/bundling-sources/sdk/src/play.ts +3 -6
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/types.ts +3 -2
- package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +186 -0
- package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +41 -0
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +213 -55
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +172 -299
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +13 -32
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -1
- package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +7 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +11 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +16 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/testkit.ts +5 -4
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +12 -2
- package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +1 -1
- package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +74 -0
- package/dist/cli/index.js +940 -181
- package/dist/cli/index.mjs +940 -181
- package/dist/{compiler-manifest-CMNgA_JQ.d.mts → compiler-manifest-Dbov16F7.d.mts} +1 -1
- package/dist/{compiler-manifest-CMNgA_JQ.d.ts → compiler-manifest-Dbov16F7.d.ts} +1 -1
- package/dist/index.d.mts +176 -23
- package/dist/index.d.ts +176 -23
- package/dist/index.js +109 -12
- package/dist/index.mjs +109 -12
- package/dist/install-integrity.json +4 -2
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
|
@@ -574,6 +574,16 @@ export type IntegrationEventWaitHandler = {
|
|
|
574
574
|
context: IntegrationEventWaitRuntimeContext;
|
|
575
575
|
boundary: IntegrationEventWaitBoundary;
|
|
576
576
|
}) => Promise<IntegrationEventWaitBoundary>;
|
|
577
|
+
/**
|
|
578
|
+
* Runs during the resumed execution only when this boundary reached its
|
|
579
|
+
* durable timeout. Providers use this to make their external interaction
|
|
580
|
+
* terminal before the Play consumes the timeout payload.
|
|
581
|
+
*/
|
|
582
|
+
onTimeout?: (input: {
|
|
583
|
+
payload: Record<string, unknown>;
|
|
584
|
+
context: IntegrationEventWaitRuntimeContext;
|
|
585
|
+
boundary: IntegrationEventWaitBoundary;
|
|
586
|
+
}) => Promise<void>;
|
|
577
587
|
};
|
|
578
588
|
|
|
579
589
|
export interface ContextOptions {
|
|
@@ -197,25 +197,23 @@ export function buildDurableToolReceiptPrefix(input: {
|
|
|
197
197
|
return `ctx:${orgId}:call:tool:${normalizeTableNamespace(toolId)}:`;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
export function
|
|
201
|
-
|
|
200
|
+
export function buildProviderOperationIdempotencyKey(input: {
|
|
201
|
+
operationKey: string;
|
|
202
202
|
force?: boolean;
|
|
203
|
-
receiptLeaseId?: string | null;
|
|
204
203
|
fallbackAttemptId?: string | null;
|
|
205
204
|
}): string {
|
|
206
|
-
const
|
|
207
|
-
if (!
|
|
208
|
-
throw new Error('Need
|
|
205
|
+
const operationKey = input.operationKey.trim();
|
|
206
|
+
if (!operationKey) {
|
|
207
|
+
throw new Error('Need provider operation key.');
|
|
209
208
|
}
|
|
210
209
|
if (input.force !== true) {
|
|
211
|
-
return
|
|
210
|
+
return operationKey;
|
|
212
211
|
}
|
|
213
|
-
const attemptId =
|
|
214
|
-
input.receiptLeaseId?.trim() || input.fallbackAttemptId?.trim() || null;
|
|
212
|
+
const attemptId = input.fallbackAttemptId?.trim() || null;
|
|
215
213
|
if (!attemptId) {
|
|
216
|
-
throw new Error('Forced idempotency needs
|
|
214
|
+
throw new Error('Forced idempotency needs a logical call identity.');
|
|
217
215
|
}
|
|
218
|
-
return `${
|
|
216
|
+
return `${operationKey}:force:${attemptId}`;
|
|
219
217
|
}
|
|
220
218
|
|
|
221
219
|
export function buildDurableToolAggregateReceiptKey(input: {
|
|
@@ -258,25 +256,10 @@ function commonDurableToolReceiptPrefix(
|
|
|
258
256
|
return commonPrefix ?? '';
|
|
259
257
|
}
|
|
260
258
|
|
|
261
|
-
export function buildDurableToolAggregateProviderIdempotencyKey(input: {
|
|
262
|
-
aggregateReceiptKey: string;
|
|
263
|
-
receiptKeys: string[];
|
|
264
|
-
providerIdempotencyKeys: string[];
|
|
265
|
-
}): string {
|
|
266
|
-
const receiptKeys = input.receiptKeys.map((key) => key.trim());
|
|
267
|
-
const providerIdempotencyKeys = input.providerIdempotencyKeys.map((key) =>
|
|
268
|
-
key.trim(),
|
|
269
|
-
);
|
|
270
|
-
return `${input.aggregateReceiptKey}:ordered:${sha256Hex(
|
|
271
|
-
stableStringify({ receiptKeys, providerIdempotencyKeys }),
|
|
272
|
-
)}`;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
259
|
/**
|
|
276
260
|
* Provider-native batch caching is keyed by the exact compiled provider
|
|
277
|
-
* payload.
|
|
278
|
-
*
|
|
279
|
-
* can remove members after the original batch was planned.
|
|
261
|
+
* payload. Result-cache identities coordinate local publication; they never
|
|
262
|
+
* stand in for a provider operation identity.
|
|
280
263
|
*
|
|
281
264
|
* An explicit force refresh is deliberately a separate namespace: it asks for
|
|
282
265
|
* a fresh provider operation even when the payload itself is unchanged.
|
|
@@ -298,10 +281,8 @@ export function buildDurableToolPayloadProviderIdempotencyKey(input: {
|
|
|
298
281
|
const forceRefreshIdentities = input.forceRefreshIdentities
|
|
299
282
|
?.map((identity) => identity.trim())
|
|
300
283
|
.filter(Boolean);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
toolId,
|
|
304
|
-
})}payload-v1:${sha256Hex(
|
|
284
|
+
const orgId = input.orgId?.trim() || 'org';
|
|
285
|
+
return `ctx:${orgId}:operation:tool:${normalizeTableNamespace(toolId)}:payload-v1:${sha256Hex(
|
|
305
286
|
stableStringify({
|
|
306
287
|
executionAuthScopeDigest: input.executionAuthScopeDigest?.trim() || null,
|
|
307
288
|
providerActionVersion: input.providerActionVersion?.trim() || null,
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
PlayExecutionGovernor,
|
|
3
|
+
WorkLease,
|
|
4
|
+
} from './governor/governor';
|
|
2
5
|
|
|
3
6
|
export type RuntimeResourceLease = WorkLease;
|
|
4
7
|
|
|
@@ -211,7 +214,9 @@ export function createRuntimeResourceGovernor(input: {
|
|
|
211
214
|
const startedAt = Date.now();
|
|
212
215
|
const lease = await input.executionGovernor.acquireToolSlot(
|
|
213
216
|
toolInput.toolId,
|
|
214
|
-
{
|
|
217
|
+
{
|
|
218
|
+
signal: toolInput.signal,
|
|
219
|
+
},
|
|
215
220
|
);
|
|
216
221
|
toolCounters.acquired += 1;
|
|
217
222
|
observe({
|
package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { DAYTONA_PLAY_RUNNER_LABEL_SOURCE } from './daytona-labels';
|
|
|
15
15
|
const DAYTONA_CREATE_TIMEOUT_SECONDS = 10;
|
|
16
16
|
const DAYTONA_CREATE_RETRY_DELAYS_MS = [0, 500, 1_500] as const;
|
|
17
17
|
const DAYTONA_TIMED_OUT_CREATE_RECONCILE_DELAYS_MS = [
|
|
18
|
-
0, 1_000, 3_000, 6_000, 12_000,
|
|
18
|
+
0, 1_000, 3_000, 6_000, 12_000, 24_000, 36_000,
|
|
19
19
|
] as const;
|
|
20
20
|
// Explicit runner deadline + scheduler GC own the normal lifecycle. Daytona's
|
|
21
21
|
// inactivity stop is a wider crash backstop measured from sandbox creation, so
|
|
@@ -115,6 +115,11 @@ function isDaytonaSandboxStateChangeConflict(error: unknown): boolean {
|
|
|
115
115
|
return /Sandbox state change in progress/i.test(message);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function isDaytonaSandboxNotFound(error: unknown): boolean {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
return /\b(?:sandbox )?not found\b|\b404\b/i.test(message);
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
type DaytonaCreateResult = {
|
|
119
124
|
sandbox: DaytonaSandbox;
|
|
120
125
|
attempt: number;
|
|
@@ -329,7 +334,11 @@ async function reconcileAndDeleteTimedOutDaytonaSandbox(input: {
|
|
|
329
334
|
let sandbox: DaytonaSandbox;
|
|
330
335
|
try {
|
|
331
336
|
sandbox = await input.daytona.get(input.sandboxName);
|
|
332
|
-
} catch {
|
|
337
|
+
} catch (error) {
|
|
338
|
+
// A confirmed absence cannot become a cleanup-addressable resource on a
|
|
339
|
+
// later read. Stop immediately; only uncertain lookup failures merit
|
|
340
|
+
// the bounded retry window below.
|
|
341
|
+
if (isDaytonaSandboxNotFound(error)) return { kind: 'not_found' };
|
|
333
342
|
continue;
|
|
334
343
|
}
|
|
335
344
|
// `create` waits only a bounded interval, but the named sandbox may finish
|
|
@@ -218,7 +218,7 @@ export const RUNTIME_INCIDENT_DRILL_CATALOG: readonly RuntimeIncidentDrill[] = [
|
|
|
218
218
|
},
|
|
219
219
|
expected: {
|
|
220
220
|
outcome:
|
|
221
|
-
'The same logical run completes through
|
|
221
|
+
'The same logical run completes through terminal-cache replay; any provider retry is governed by its separate operation policy.',
|
|
222
222
|
safetyFacts: ['same_logical_run', 'bounded_provider_execution'],
|
|
223
223
|
},
|
|
224
224
|
verification: {
|
|
@@ -14,6 +14,10 @@ export type PlayExecutionSuspension =
|
|
|
14
14
|
boundaryId: string;
|
|
15
15
|
eventKey: string;
|
|
16
16
|
timeoutMs: number;
|
|
17
|
+
messageRef?: {
|
|
18
|
+
channel: string;
|
|
19
|
+
ts: string;
|
|
20
|
+
};
|
|
17
21
|
}
|
|
18
22
|
| {
|
|
19
23
|
kind: 'integration_event_batch';
|
|
@@ -21,6 +25,10 @@ export type PlayExecutionSuspension =
|
|
|
21
25
|
boundaryId: string;
|
|
22
26
|
eventKey: string;
|
|
23
27
|
timeoutMs: number;
|
|
28
|
+
messageRef?: {
|
|
29
|
+
channel: string;
|
|
30
|
+
ts: string;
|
|
31
|
+
};
|
|
24
32
|
}>;
|
|
25
33
|
}
|
|
26
34
|
| {
|
|
@@ -115,12 +123,20 @@ export class PlayRowExecutionSuspendedError extends Error {
|
|
|
115
123
|
boundaryId: string;
|
|
116
124
|
eventKey: string;
|
|
117
125
|
timeoutMs: number;
|
|
126
|
+
messageRef?: {
|
|
127
|
+
channel: string;
|
|
128
|
+
ts: string;
|
|
129
|
+
};
|
|
118
130
|
};
|
|
119
131
|
|
|
120
132
|
constructor(boundary: {
|
|
121
133
|
boundaryId: string;
|
|
122
134
|
eventKey: string;
|
|
123
135
|
timeoutMs: number;
|
|
136
|
+
messageRef?: {
|
|
137
|
+
channel: string;
|
|
138
|
+
ts: string;
|
|
139
|
+
};
|
|
124
140
|
}) {
|
|
125
141
|
super(
|
|
126
142
|
`Play row execution suspended waiting for integration event ${JSON.stringify(boundary.eventKey)}.`,
|
|
@@ -61,6 +61,7 @@ export type NativeBatchToolCallJob = {
|
|
|
61
61
|
*
|
|
62
62
|
* It reads each member's completed-result cache before assembling one physical
|
|
63
63
|
* provider batch for the misses, then atomically stores each successful split.
|
|
64
|
+
* The cache neither owns nor recovers a provider job.
|
|
64
65
|
*/
|
|
65
66
|
export function createNativeBatchToolCallJob(dependencies: {
|
|
66
67
|
receiptCohort: ToolCallReceiptCohortJob;
|
|
@@ -119,8 +119,9 @@ export function createMemoryToolResultReceipts(): ToolResultReceipts & {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* Models
|
|
123
|
-
* may share
|
|
122
|
+
* Models in-process request single-flight. Multiple result-cache
|
|
123
|
+
* representations may share a completed response, but no receipt identifies
|
|
124
|
+
* or recovers an external provider job.
|
|
124
125
|
*/
|
|
125
126
|
export function createSingleFlightToolCallDispatcher(input: {
|
|
126
127
|
dispatch(
|
|
@@ -134,8 +135,8 @@ export function createSingleFlightToolCallDispatcher(input: {
|
|
|
134
135
|
const dispatches: Parameters<ToolCallDispatcher['dispatch']>[0][] = [];
|
|
135
136
|
return {
|
|
136
137
|
async dispatch(call) {
|
|
137
|
-
// Receiptless calls are intentionally not single-flighted:
|
|
138
|
-
//
|
|
138
|
+
// Receiptless calls are intentionally not single-flighted: every
|
|
139
|
+
// invocation reaches the adapter.
|
|
139
140
|
if (!call.providerOperationKey) {
|
|
140
141
|
dispatches.push(call);
|
|
141
142
|
return await input.dispatch(call);
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
normalizeToolHttpErrorMessage,
|
|
4
4
|
type ToolHttpError,
|
|
5
5
|
} from './tool-http-errors';
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
ToolExecutionError,
|
|
8
|
+
type ToolExecutionErrorSchemaVersion,
|
|
9
|
+
} from '../plays/tool-execution-error';
|
|
7
10
|
|
|
8
11
|
export const TOOL_EXECUTE_TRANSIENT_HTTP_MAX_ATTEMPTS = 2;
|
|
9
12
|
// A provider may need to finish the original request before it can replay the
|
|
@@ -439,8 +442,15 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
439
442
|
? 'fail_run'
|
|
440
443
|
: 'settle_row_failure',
|
|
441
444
|
retryDelayMs,
|
|
445
|
+
// Only a provider-owned 429 may cool the shared provider bucket. Platform,
|
|
446
|
+
// caller, and malformed responses are never evidence that a provider is
|
|
447
|
+
// throttling; feeding any of them into the pacer can incorrectly exhaust
|
|
448
|
+
// every customer sharing that provider credential.
|
|
442
449
|
backpressureDelayMs:
|
|
443
|
-
input.status === 429 &&
|
|
450
|
+
input.status === 429 &&
|
|
451
|
+
retryDecision.reason !== 'hard_billing_error' &&
|
|
452
|
+
error instanceof ToolExecutionError &&
|
|
453
|
+
error.origin === 'provider'
|
|
444
454
|
? hasRetryAfterHeader && retryAfterMs > 0
|
|
445
455
|
? retryAfterMs
|
|
446
456
|
: TOOL_EXECUTE_BARE_RATE_LIMIT_BACKPRESSURE_MS
|
|
@@ -1139,7 +1139,7 @@ export interface PlayAuthoringRuntimeContext {
|
|
|
1139
1139
|
|
|
1140
1140
|
tools: {
|
|
1141
1141
|
/**
|
|
1142
|
-
* Execute a provider tool through the
|
|
1142
|
+
* Execute a provider tool through the terminal-result cache contract.
|
|
1143
1143
|
* @sdkReference runtime 150 ctx.tools.execute(request)
|
|
1144
1144
|
*/
|
|
1145
1145
|
execute<TOutput = PlayLooseObject>(
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The play deployment status machine, shared by the Convex control plane
|
|
3
|
+
* (convex/playDeployments.ts), worker auth (src/lib/auth/play-deploy-auth.ts),
|
|
4
|
+
* and the deploy-worker API surface. One spelling of the union, the terminal
|
|
5
|
+
* set, and the allowed transitions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const PLAY_DEPLOYMENT_STATUSES = [
|
|
9
|
+
'queued',
|
|
10
|
+
'building',
|
|
11
|
+
'publishing',
|
|
12
|
+
'succeeded',
|
|
13
|
+
// Everything this deployment set out to deploy deployed, but it has
|
|
14
|
+
// something the reader must see — today a play file skipped because
|
|
15
|
+
// another file already claimed its name. A warning, never a failure:
|
|
16
|
+
// deliberately its own terminal status rather than a flag on `succeeded`,
|
|
17
|
+
// so future warning reasons have somewhere to live and so the API, CLI,
|
|
18
|
+
// and dashboard can all filter on it without reading per-play results.
|
|
19
|
+
'succeeded_with_warnings',
|
|
20
|
+
'failed',
|
|
21
|
+
// Live pointers flipped, then per-play SQL listener reconciliation failed
|
|
22
|
+
// and the pointers were reverted. Loud, never silent.
|
|
23
|
+
'reconcile_failed',
|
|
24
|
+
'cancelled',
|
|
25
|
+
'superseded',
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export type PlayDeploymentStatus = (typeof PLAY_DEPLOYMENT_STATUSES)[number];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A terminal deployment's token is dead: worker auth returns the typed
|
|
32
|
+
* "superseded"/"terminal" code so the worker aborts instead of retrying.
|
|
33
|
+
* Typed ReadonlySet<string> so unvalidated route input can be tested for
|
|
34
|
+
* membership directly.
|
|
35
|
+
*/
|
|
36
|
+
export const TERMINAL_PLAY_DEPLOYMENT_STATUSES: ReadonlySet<string> =
|
|
37
|
+
new Set<PlayDeploymentStatus>([
|
|
38
|
+
'succeeded',
|
|
39
|
+
'succeeded_with_warnings',
|
|
40
|
+
'failed',
|
|
41
|
+
'reconcile_failed',
|
|
42
|
+
'cancelled',
|
|
43
|
+
'superseded',
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
export const ALLOWED_PLAY_DEPLOYMENT_TRANSITIONS: Record<
|
|
47
|
+
PlayDeploymentStatus,
|
|
48
|
+
readonly PlayDeploymentStatus[]
|
|
49
|
+
> = {
|
|
50
|
+
queued: ['building', 'failed', 'cancelled', 'superseded'],
|
|
51
|
+
building: [
|
|
52
|
+
'publishing',
|
|
53
|
+
'succeeded',
|
|
54
|
+
'succeeded_with_warnings',
|
|
55
|
+
'failed',
|
|
56
|
+
'cancelled',
|
|
57
|
+
'superseded',
|
|
58
|
+
],
|
|
59
|
+
// publishing -> superseded: the atomic stale-promotion guard fired (a
|
|
60
|
+
// newer production deployment exists), so the worker retires itself.
|
|
61
|
+
publishing: [
|
|
62
|
+
'succeeded',
|
|
63
|
+
'succeeded_with_warnings',
|
|
64
|
+
'failed',
|
|
65
|
+
'reconcile_failed',
|
|
66
|
+
'superseded',
|
|
67
|
+
],
|
|
68
|
+
succeeded: [],
|
|
69
|
+
succeeded_with_warnings: [],
|
|
70
|
+
failed: [],
|
|
71
|
+
reconcile_failed: [],
|
|
72
|
+
cancelled: [],
|
|
73
|
+
superseded: [],
|
|
74
|
+
};
|