deepline 0.1.252 → 0.1.253

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 (23) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/sdk/src/types.ts +2 -0
  3. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +79 -64
  4. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +37 -35
  5. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +20 -5
  6. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +462 -29
  7. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +16 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +11 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +44 -1
  10. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +7 -4
  11. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +98 -13
  12. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +117 -0
  13. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +19 -3
  14. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +1 -0
  15. package/dist/bundling-sources/shared_libs/play-runtime/runtime-constants.ts +13 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +22 -0
  17. package/dist/cli/index.js +26 -7
  18. package/dist/cli/index.mjs +26 -7
  19. package/dist/index.d.mts +2 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +6 -4
  22. package/dist/index.mjs +6 -4
  23. package/package.json +1 -1
@@ -116,7 +116,7 @@ export const SDK_RELEASE = {
116
116
  // 0.1.252 hard-cuts the customer Monitor catalog to the two launched
117
117
  // Deepline-native radars. Older clients must update before discovering,
118
118
  // checking, or deploying an unlaunched monitor integration.
119
- version: '0.1.252',
119
+ version: '0.1.253',
120
120
  apiContract: '2026-07-native-monitor-launch-hard-cutover',
121
121
  supportPolicy: {
122
122
  minimumSupported: '0.1.53',
@@ -551,6 +551,8 @@ export interface PlayRunPackage {
551
551
  durationMs?: number | null;
552
552
  error?: string;
553
553
  };
554
+ /** Bounded customer-safe warnings about output projection or availability. */
555
+ warnings?: string[];
554
556
  /** Step-level summaries emitted by the runtime. */
555
557
  steps: Array<Record<string, unknown>>;
556
558
  /** Named output summaries, including dataset handles and scalar outputs. */
@@ -8282,9 +8282,9 @@ export class PlayContextImpl {
8282
8282
  const url = `${this.#options.baseUrl}/api/v2/integrations/${encodeURIComponent(toolId)}/execute`;
8283
8283
  const timeoutMs = resolveToolRuntimeTimeoutMs(toolId, options?.timeoutMs);
8284
8284
 
8285
- // The Governor's tool slot is the single seam for tool-call budget + the
8286
- // global tool-concurrency backstop + per-(org, provider) pacing. It blocks
8287
- // until all three are satisfied and is held (across retries) until release.
8285
+ // Hold one logical tool slot and charge one logical tool call for this
8286
+ // execution. Per-provider rate admission is intentionally deferred until
8287
+ // each physical fetch is ready to leave this process.
8288
8288
  const admissionStartedAt = Date.now();
8289
8289
  const toolSlot = await this.resourceGovernor.acquireTool({
8290
8290
  orgId: this.#options.orgId ?? null,
@@ -8408,7 +8408,6 @@ export class PlayContextImpl {
8408
8408
  `[perf] tool call id=${toolId} phase=before_provider_ownership elapsed_ms=${Date.now() - ownershipStartedAt}`,
8409
8409
  );
8410
8410
  }
8411
- providerCallStartedAt = Date.now();
8412
8411
  const heartbeatIntervalMs = hasReceiptHeartbeat
8413
8412
  ? runtimeLeaseHeartbeatIntervalFromExpiry({
8414
8413
  leaseExpiresAt: options?.receiptLeaseExpiresAt,
@@ -8438,71 +8437,87 @@ export class PlayContextImpl {
8438
8437
  : null;
8439
8438
  receiptHeartbeatSupervisor?.start();
8440
8439
  try {
8440
+ // Complete all local/receipt preparation before taking the
8441
+ // shared provider permit. Once admitted, the next awaited work
8442
+ // is the fetch itself, so independently delayed runners cannot
8443
+ // compress real provider arrivals after their tickets.
8444
+ const protectionHeaders = await this.vercelProtectionHeaders();
8445
+ const providerPermit =
8446
+ await this.resourceGovernor.acquireProviderPermit({
8447
+ toolId,
8448
+ signal: abortController?.signal,
8449
+ });
8441
8450
  const integrationFetchStartedAt = Date.now();
8442
- response = await fetch(url, {
8443
- method: 'POST',
8444
- signal: abortController?.signal,
8445
- headers: {
8446
- 'Content-Type': 'application/json',
8447
- Authorization: `Bearer ${this.#options.executorToken}`,
8448
- [EXECUTE_RESPONSE_CONTRACT_HEADER]:
8449
- V2_EXECUTE_RESPONSE_CONTRACT,
8450
- [EXECUTE_RESPONSE_INTENT_HEADER]: 'dataset',
8451
- [EXECUTE_TOOL_METADATA_HEADER]: 'true',
8452
- 'x-deepline-request-id': deeplineRequestId,
8453
- ...(providerIdempotencyKey
8454
- ? {
8455
- 'x-deepline-idempotency-key': providerIdempotencyKey,
8456
- }
8457
- : {}),
8458
- ...(await this.vercelProtectionHeaders()),
8459
- },
8460
- body: JSON.stringify({
8461
- payload: input,
8462
- metadata: {
8463
- parent_run_id: this.#options.runId,
8464
- ...(durableCallReceiptKey
8465
- ? {
8466
- durable_call_receipt_key: durableCallReceiptKey,
8467
- ...(executionAuthScopeDigest
8468
- ? {
8469
- execution_auth_scope_digest:
8470
- executionAuthScopeDigest,
8471
- }
8472
- : {}),
8473
- }
8474
- : {}),
8475
- ...(options?.customerDbDataset
8451
+ providerCallStartedAt = integrationFetchStartedAt;
8452
+ try {
8453
+ response = await fetch(url, {
8454
+ method: 'POST',
8455
+ signal: abortController?.signal,
8456
+ headers: {
8457
+ 'Content-Type': 'application/json',
8458
+ Authorization: `Bearer ${this.#options.executorToken}`,
8459
+ [EXECUTE_RESPONSE_CONTRACT_HEADER]:
8460
+ V2_EXECUTE_RESPONSE_CONTRACT,
8461
+ [EXECUTE_RESPONSE_INTENT_HEADER]: 'dataset',
8462
+ [EXECUTE_TOOL_METADATA_HEADER]: 'true',
8463
+ 'x-deepline-request-id': deeplineRequestId,
8464
+ ...(providerIdempotencyKey
8476
8465
  ? {
8477
- query_result_dataset: {
8478
- limit: options.customerDbDataset.limit,
8479
- offset: options.customerDbDataset.offset,
8480
- page_size: options.customerDbDataset.pageSize,
8481
- total_rows: options.customerDbDataset.totalRows,
8482
- },
8483
- ...(isCustomerDbDatasetTool(toolId)
8484
- ? {
8485
- customer_db_dataset: {
8486
- limit: options.customerDbDataset.limit,
8487
- offset: options.customerDbDataset.offset,
8488
- page_size:
8489
- options.customerDbDataset.pageSize,
8490
- total_rows:
8491
- options.customerDbDataset.totalRows,
8492
- },
8493
- }
8494
- : {}),
8466
+ 'x-deepline-idempotency-key':
8467
+ providerIdempotencyKey,
8495
8468
  }
8496
8469
  : {}),
8497
- ...(providerIdempotencyKey
8498
- ? { provider_idempotency_key: providerIdempotencyKey }
8499
- : {}),
8470
+ ...protectionHeaders,
8500
8471
  },
8501
- ...(this.#options.integrationMode
8502
- ? { integration_mode: this.#options.integrationMode }
8503
- : {}),
8504
- }),
8505
- });
8472
+ body: JSON.stringify({
8473
+ payload: input,
8474
+ metadata: {
8475
+ parent_run_id: this.#options.runId,
8476
+ ...(durableCallReceiptKey
8477
+ ? {
8478
+ durable_call_receipt_key: durableCallReceiptKey,
8479
+ ...(executionAuthScopeDigest
8480
+ ? {
8481
+ execution_auth_scope_digest:
8482
+ executionAuthScopeDigest,
8483
+ }
8484
+ : {}),
8485
+ }
8486
+ : {}),
8487
+ ...(options?.customerDbDataset
8488
+ ? {
8489
+ query_result_dataset: {
8490
+ limit: options.customerDbDataset.limit,
8491
+ offset: options.customerDbDataset.offset,
8492
+ page_size: options.customerDbDataset.pageSize,
8493
+ total_rows: options.customerDbDataset.totalRows,
8494
+ },
8495
+ ...(isCustomerDbDatasetTool(toolId)
8496
+ ? {
8497
+ customer_db_dataset: {
8498
+ limit: options.customerDbDataset.limit,
8499
+ offset: options.customerDbDataset.offset,
8500
+ page_size:
8501
+ options.customerDbDataset.pageSize,
8502
+ total_rows:
8503
+ options.customerDbDataset.totalRows,
8504
+ },
8505
+ }
8506
+ : {}),
8507
+ }
8508
+ : {}),
8509
+ ...(providerIdempotencyKey
8510
+ ? { provider_idempotency_key: providerIdempotencyKey }
8511
+ : {}),
8512
+ },
8513
+ ...(this.#options.integrationMode
8514
+ ? { integration_mode: this.#options.integrationMode }
8515
+ : {}),
8516
+ }),
8517
+ });
8518
+ } finally {
8519
+ providerPermit.release();
8520
+ }
8506
8521
  if (runtimeReceiptReadTraceEnabled) {
8507
8522
  this.log(
8508
8523
  `[perf] tool call id=${toolId} phase=integration_fetch_headers elapsed_ms=${Date.now() - integrationFetchStartedAt} status=${response.status}`,
@@ -6,7 +6,8 @@
6
6
  * at once" policy. See ADR 0007 + CONTEXT.md.
7
7
  *
8
8
  * Surface (small, by design):
9
- * - acquireRowSlot / acquireToolSlot → blocking leases
9
+ * - acquireRowSlot / acquireToolSlot → local execution leases
10
+ * - acquireProviderPermit → per-attempt provider admission
10
11
  * - chargeBudget → throws on breach
11
12
  * - forkInlineChild → inline recursion scope
12
13
  * - resolveRowConcurrency / reportProviderBackpressure / snapshot
@@ -102,15 +103,24 @@ export interface PlayExecutionGovernor {
102
103
  /** Block until a map-row slot is free. */
103
104
  acquireRowSlot(opts?: { signal?: AbortSignal }): Promise<WorkLease>;
104
105
  /**
105
- * Block until the per-provider pacer permit and then a global
106
- * tool-concurrency slot are free, charge the tool-call budget, and return a
107
- * lease. Parked future permits never consume scarce execution slots.
106
+ * Acquire the local execution slot and charge one logical tool-call budget.
107
+ * Provider pacing deliberately happens separately at the HTTP-attempt edge.
108
108
  */
109
109
  acquireToolSlot(
110
110
  toolId: string,
111
111
  opts?: { signal?: AbortSignal },
112
112
  ): Promise<WorkLease>;
113
113
 
114
+ /**
115
+ * Block until one physical provider HTTP attempt is admitted under the
116
+ * provider's shared rate/concurrency rules. Call this immediately before the
117
+ * outbound request and again for every retry.
118
+ */
119
+ acquireProviderPermit(
120
+ toolId: string,
121
+ opts?: { signal?: AbortSignal },
122
+ ): Promise<WorkLease>;
123
+
114
124
  /**
115
125
  * Suggested batch parallelism for a tool: the provider's own rate hints
116
126
  * tightened to the policy's suggested ceiling. No hints → the fallback.
@@ -373,44 +383,34 @@ export function createPlayExecutionGovernor(
373
383
  policy,
374
384
 
375
385
  acquireRowSlot: (opts) => rowSlots.acquire(opts?.signal),
376
- async acquireToolSlot(toolId, opts) {
377
- // 1. Per-provider pacing. The provider comes from the pacing
378
- // resolver, so callers only need the toolId. No rules no pacing.
386
+ async acquireToolSlot(_toolId, opts) {
387
+ const slot = await toolSlots.acquire(opts?.signal);
388
+ // Charge the logical call only after its local execution slot is held, so
389
+ // a failed/aborted slot acquisition never consumes the budget. The
390
+ // physical provider admission is intentionally deferred to
391
+ // acquireProviderPermit at the outbound HTTP edge.
392
+ try {
393
+ await chargeBudget('toolCall');
394
+ } catch (error) {
395
+ slot.release();
396
+ throw error;
397
+ }
398
+ return slot;
399
+ },
400
+
401
+ async acquireProviderPermit(toolId, opts) {
402
+ // The rate ticket is an admission for a physical outbound attempt, not a
403
+ // reservation made while receipt ownership, headers, or local tool slots
404
+ // may still be waiting. This is what keeps real provider arrivals inside
405
+ // the declared rolling window under concurrent runners.
379
406
  const pacing = await resolveAdaptivePacing(toolId);
380
407
  const rateScope = await resolveScope(toolId, pacing.provider);
381
- const permit = await input.rateState.acquire({
408
+ return await input.rateState.acquire({
382
409
  bucketId: rateScope[0],
383
410
  rateScopeToken: rateScope[1],
384
411
  rules: pacing.rules,
385
412
  signal: opts?.signal,
386
413
  });
387
- // 2. Acquire a scarce execution slot only after the provider permit is
388
- // due. If the slot wait aborts, refund any concurrency-bearing permit.
389
- let slot: WorkLease;
390
- try {
391
- slot = await toolSlots.acquire(opts?.signal);
392
- } catch (error) {
393
- permit.release();
394
- throw error;
395
- }
396
- // 3. Charge the budget only once the call is actually cleared to run, so a
397
- // failed/aborted acquisition never permanently consumes tool budget.
398
- try {
399
- await chargeBudget('toolCall');
400
- } catch (error) {
401
- permit.release();
402
- slot.release();
403
- throw error;
404
- }
405
- let released = false;
406
- return {
407
- release: () => {
408
- if (released) return;
409
- released = true;
410
- permit.release();
411
- slot.release();
412
- },
413
- };
414
414
  },
415
415
 
416
416
  async suggestedParallelism(toolId, fallback) {
@@ -541,6 +541,8 @@ function createInlineChildGovernor(
541
541
  policy: root.policy,
542
542
  acquireRowSlot: (opts) => root.acquireRowSlot(opts),
543
543
  acquireToolSlot: (toolId, opts) => root.acquireToolSlot(toolId, opts),
544
+ acquireProviderPermit: (toolId, opts) =>
545
+ root.acquireProviderPermit(toolId, opts),
544
546
  suggestedParallelism: (toolId, fallback) =>
545
547
  root.suggestedParallelism(toolId, fallback),
546
548
  chargeBudget: (kind, amount) => root.chargeBudget(kind, amount),
@@ -1,10 +1,14 @@
1
1
  import { createSecretRedactionContext } from './secret-redaction';
2
2
  import {
3
3
  assertRuntimeReceiptOutputWithinLimit,
4
- TERMINAL_RUN_RESULT_MAX_BYTES,
4
+ jsonByteLengthUpTo,
5
+ LEDGER_TERMINAL_RESULT_MAX_BYTES,
5
6
  } from './output-size-limits';
6
7
 
7
- export const MAX_LEDGER_RESULT_PAYLOAD_BYTES = TERMINAL_RUN_RESULT_MAX_BYTES;
8
+ // This is an inline Convex event limit, not the durable scheduler-terminal
9
+ // envelope. A full terminal return is projected to an out-of-line reference
10
+ // before it reaches here.
11
+ export const MAX_LEDGER_RESULT_PAYLOAD_BYTES = LEDGER_TERMINAL_RESULT_MAX_BYTES;
8
12
  export const MAX_LEDGER_LOG_LINES_PER_EVENT = 500;
9
13
  export const MAX_LEDGER_LOG_LINE_LENGTH = 2_000;
10
14
 
@@ -17,8 +21,11 @@ export function redactLedgerString(value: unknown): string | null {
17
21
 
18
22
  export function sanitizeLedgerPayload(value: unknown): unknown {
19
23
  if (value === undefined) return undefined;
20
- const serialized = JSON.stringify(value);
21
- if (serialized && serialized.length > MAX_LEDGER_RESULT_PAYLOAD_BYTES) {
24
+ const measurement = jsonByteLengthUpTo(
25
+ value,
26
+ MAX_LEDGER_RESULT_PAYLOAD_BYTES,
27
+ );
28
+ if (measurement.exceeded) {
22
29
  throw new Error('run event result payload is too large');
23
30
  }
24
31
  return ledgerIngressRedactor.redact(value);
@@ -26,11 +33,19 @@ export function sanitizeLedgerPayload(value: unknown): unknown {
26
33
 
27
34
  /** Secret-redact a receipt payload without coupling its 10 MiB cell limit to the ledger. */
28
35
  export function sanitizeRuntimeReceiptPayload(value: unknown): unknown {
36
+ // Reject an obviously oversized source before doing redaction work, then
37
+ // validate the exact value that will be persisted. Redaction normally shrinks
38
+ // credential-shaped values but can expand short matched strings.
29
39
  assertRuntimeReceiptOutputWithinLimit({
30
40
  output: value,
31
41
  path: 'runtime receipt output',
32
42
  });
33
- return ledgerIngressRedactor.redact(value);
43
+ const redacted = ledgerIngressRedactor.redact(value);
44
+ assertRuntimeReceiptOutputWithinLimit({
45
+ output: redacted,
46
+ path: 'runtime receipt output after secret redaction',
47
+ });
48
+ return redacted;
34
49
  }
35
50
 
36
51
  export function sanitizeLedgerLogLines(value: unknown): string[] {