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.
Files changed (34) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +279 -21
  2. package/dist/bundling-sources/sdk/src/config.ts +21 -3
  3. package/dist/bundling-sources/sdk/src/index.ts +1 -0
  4. package/dist/bundling-sources/sdk/src/play.ts +3 -6
  5. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  6. package/dist/bundling-sources/sdk/src/types.ts +3 -2
  7. package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +186 -0
  8. package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +41 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +213 -55
  10. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +172 -299
  11. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +13 -32
  13. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -1
  14. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +7 -2
  15. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +11 -2
  16. package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +1 -1
  17. package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +16 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +1 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/testkit.ts +5 -4
  20. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +12 -2
  21. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +1 -1
  22. package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +74 -0
  23. package/dist/cli/index.js +940 -181
  24. package/dist/cli/index.mjs +940 -181
  25. package/dist/{compiler-manifest-CMNgA_JQ.d.mts → compiler-manifest-Dbov16F7.d.mts} +1 -1
  26. package/dist/{compiler-manifest-CMNgA_JQ.d.ts → compiler-manifest-Dbov16F7.d.ts} +1 -1
  27. package/dist/index.d.mts +176 -23
  28. package/dist/index.d.ts +176 -23
  29. package/dist/index.js +109 -12
  30. package/dist/index.mjs +109 -12
  31. package/dist/install-integrity.json +4 -2
  32. package/dist/plays/bundle-play-file.d.mts +2 -2
  33. package/dist/plays/bundle-play-file.d.ts +2 -2
  34. 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 buildDurableToolProviderIdempotencyKey(input: {
201
- receiptKey: string;
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 receiptKey = input.receiptKey.trim();
207
- if (!receiptKey) {
208
- throw new Error('Need receipt key.');
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 receiptKey;
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 lease id.');
214
+ throw new Error('Forced idempotency needs a logical call identity.');
217
215
  }
218
- return `${receiptKey}:force:${attemptId}`;
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. Receipt identities coordinate ownership and terminal results; they
278
- * cannot stand in for request identity because cache hits and foreign leases
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
- return `${buildDurableToolReceiptPrefix({
302
- orgId: input.orgId,
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,
@@ -439,7 +439,11 @@ export function createPlayExecutionGovernor(
439
439
  slot.release();
440
440
  throw error;
441
441
  }
442
- return slot;
442
+ return {
443
+ release() {
444
+ slot.release();
445
+ },
446
+ };
443
447
  },
444
448
 
445
449
  acquireIntegrationRequestSlot: (opts) =>
@@ -1,4 +1,7 @@
1
- import type { PlayExecutionGovernor, WorkLease } from './governor/governor';
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
- { signal: toolInput.signal },
217
+ {
218
+ signal: toolInput.signal,
219
+ },
215
220
  );
216
221
  toolCounters.acquired += 1;
217
222
  observe({
@@ -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 receipt replay; recovery remains bounded if the provider outcome must be retried.',
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 the server-side operation fence: multiple receipt representations
123
- * may share one provider operation key while keeping separate result receipts.
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: they make no
138
- // provider-operation claim and every invocation reaches the adapter.
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 type { ToolExecutionErrorSchemaVersion } from '../plays/tool-execution-error';
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 && retryDecision.reason !== 'hard_billing_error'
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 durable receipt contract.
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
+ };