deepline 0.1.293 → 0.1.295

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.
@@ -155,7 +155,7 @@ export const SDK_RELEASE = {
155
155
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
156
156
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
157
157
  // Operators use the checkout-local deepline-admin binary instead.
158
- version: '0.1.293',
158
+ version: '0.1.295',
159
159
  contracts: {
160
160
  api: {
161
161
  name: 'sdk-http-api',
@@ -24,7 +24,6 @@ import type {
24
24
  PlayVisualNodeProgressMap,
25
25
  } from '@shared_libs/play-runtime/worker-api-types';
26
26
  import type { RuntimeStepReceipt } from '@shared_libs/play-runtime/ctx-types';
27
- import type { WorkReceiptFailureKind } from '@shared_libs/play-runtime/work-receipts';
28
27
  import type {
29
28
  CreateDbSessionRequest,
30
29
  CreateDbSessionResponse,
@@ -45,6 +44,7 @@ import {
45
44
  import { PLAY_RUNTIME_API_COMPAT_PATH } from '@shared_libs/play-runtime/runtime-api-paths';
46
45
  import { PLAY_RUNTIME_TEST_FAULT_HEADER } from '@shared_libs/play-runtime/test-runtime-seams';
47
46
  import { vercelProtectionBypassHeaders } from '@shared_libs/play-runtime/vercel-protection';
47
+ import type { RuntimeReceiptAction } from '@shared_libs/play-runtime/runtime-actions';
48
48
 
49
49
  export type StoredPlayArtifactPayload = {
50
50
  sourceCode: string;
@@ -204,156 +204,7 @@ type RuntimeApiRequest =
204
204
  | ({
205
205
  action: 'governor_budget_charge';
206
206
  } & PlayRunnerBudgetChargeInput)
207
- | {
208
- action: 'get_runtime_step_receipt';
209
- playName: string;
210
- runId: string;
211
- key: string;
212
- }
213
- | {
214
- action: 'acquire_runtime_receipt_execution_lock';
215
- playName: string;
216
- runId: string;
217
- key: string;
218
- ownerExecutionId: string;
219
- ttlMs: number;
220
- }
221
- | {
222
- action: 'release_runtime_receipt_execution_lock';
223
- playName: string;
224
- runId: string;
225
- key: string;
226
- ownerExecutionId: string;
227
- }
228
- | {
229
- action: 'claim_runtime_step_receipt';
230
- playName: string;
231
- runId: string;
232
- key: string;
233
- leaseId?: string;
234
- runAttempt?: number | null;
235
- leaseAware?: boolean;
236
- reclaimRunning?: boolean;
237
- forceRefresh?: boolean;
238
- forceFailedRefresh?: boolean;
239
- }
240
- | {
241
- action: 'complete_runtime_step_receipt';
242
- playName: string;
243
- runId: string;
244
- key: string;
245
- runAttempt?: number | null;
246
- leaseId?: string | null;
247
- output: unknown;
248
- }
249
- | {
250
- action: 'release_runtime_step_receipt';
251
- playName: string;
252
- runId: string;
253
- key: string;
254
- runAttempt?: number | null;
255
- leaseId?: string | null;
256
- }
257
- | {
258
- action: 'fail_runtime_step_receipt';
259
- playName: string;
260
- runId: string;
261
- key: string;
262
- runAttempt?: number | null;
263
- leaseId?: string | null;
264
- error: string;
265
- failureKind?: WorkReceiptFailureKind | null;
266
- }
267
- | {
268
- action: 'skip_runtime_step_receipt';
269
- playName: string;
270
- runId: string;
271
- key: string;
272
- runAttempt?: number | null;
273
- leaseId?: string | null;
274
- output?: unknown;
275
- }
276
- | {
277
- action: 'get_runtime_step_receipts';
278
- playName: string;
279
- runId: string;
280
- keys: string[];
281
- }
282
- | {
283
- action: 'claim_runtime_step_receipts';
284
- playName: string;
285
- runId: string;
286
- keys: string[];
287
- leaseIds?: string[];
288
- runAttempt?: number | null;
289
- leaseAware?: boolean;
290
- reclaimRunning?: boolean;
291
- forceRefresh?: boolean;
292
- forceFailedRefresh?: boolean;
293
- }
294
- | {
295
- action: 'mark_runtime_step_receipt_running';
296
- playName: string;
297
- runId: string;
298
- key: string;
299
- runAttempt?: number | null;
300
- leaseId?: string | null;
301
- }
302
- | {
303
- action: 'mark_runtime_step_receipts_running';
304
- playName: string;
305
- runId: string;
306
- receipts: Array<{
307
- key: string;
308
- runId: string;
309
- runAttempt?: number | null;
310
- leaseId?: string | null;
311
- }>;
312
- }
313
- | {
314
- action: 'mark_runtime_step_receipts_queued';
315
- playName: string;
316
- runId: string;
317
- receipts: Array<{
318
- key: string;
319
- runId: string;
320
- runAttempt?: number | null;
321
- leaseId?: string | null;
322
- }>;
323
- }
324
- | {
325
- action: 'complete_runtime_step_receipts';
326
- playName: string;
327
- runId: string;
328
- receipts: Array<{
329
- key: string;
330
- runId: string;
331
- runAttempt?: number | null;
332
- leaseId?: string | null;
333
- output: unknown;
334
- }>;
335
- }
336
- | {
337
- action: 'fail_runtime_step_receipts';
338
- playName: string;
339
- runId: string;
340
- receipts: Array<{
341
- key: string;
342
- runId: string;
343
- runAttempt?: number | null;
344
- leaseId?: string | null;
345
- error: string;
346
- failureKind?: WorkReceiptFailureKind | null;
347
- }>;
348
- }
349
- | {
350
- action: 'heartbeat_runtime_step_receipts';
351
- playName: string;
352
- runId: string;
353
- runAttempt?: number | null;
354
- leaseId: string;
355
- keys: string[];
356
- };
207
+ | RuntimeReceiptAction;
357
208
 
358
209
  export type WorkerRuntimeApiContext = {
359
210
  baseUrl: string;
@@ -2,6 +2,7 @@ export type GatewayPostgresLane = 'ordinary' | 'commit';
2
2
 
3
3
  export const GATEWAY_POSTGRES_MAX_ORDINARY_ACTIVE = 8;
4
4
  export const GATEWAY_POSTGRES_MAX_TOTAL_ACTIVE = 10;
5
+ export const GATEWAY_POSTGRES_MAX_CONSECUTIVE_COMMIT_PRIORITY_GRANTS = 8;
5
6
 
6
7
  export type GatewayPostgresAdmissionSnapshot = {
7
8
  active: Record<GatewayPostgresLane, number> & { total: number };
@@ -35,6 +36,7 @@ export class GatewayPostgresAdmission {
35
36
  #commitActive = 0;
36
37
  #cancelled = 0;
37
38
  #commitPriorityGrants = 0;
39
+ #consecutiveCommitPriorityGrants = 0;
38
40
 
39
41
  async acquire(
40
42
  lane: GatewayPostgresLane,
@@ -115,21 +117,38 @@ export class GatewayPostgresAdmission {
115
117
 
116
118
  #drain(): void {
117
119
  while (this.#activeTotal() < GATEWAY_POSTGRES_MAX_TOTAL_ACTIVE) {
120
+ const ordinaryCanRun =
121
+ this.#waiting.ordinary.length > 0 &&
122
+ this.#ordinaryActive < GATEWAY_POSTGRES_MAX_ORDINARY_ACTIVE;
123
+ if (
124
+ ordinaryCanRun &&
125
+ this.#waiting.commit.length > 0 &&
126
+ this.#consecutiveCommitPriorityGrants >=
127
+ GATEWAY_POSTGRES_MAX_CONSECUTIVE_COMMIT_PRIORITY_GRANTS
128
+ ) {
129
+ this.#consecutiveCommitPriorityGrants = 0;
130
+ this.#resolve(this.#waiting.ordinary.shift()!);
131
+ continue;
132
+ }
118
133
  const commit = this.#waiting.commit.shift();
119
134
  if (commit) {
120
- if (this.#waiting.ordinary.length > 0) {
135
+ if (ordinaryCanRun) {
121
136
  this.#commitPriorityGrants += 1;
137
+ this.#consecutiveCommitPriorityGrants += 1;
138
+ } else {
139
+ this.#consecutiveCommitPriorityGrants = 0;
122
140
  }
123
141
  this.#resolve(commit);
124
142
  continue;
125
143
  }
126
- if (
127
- this.#ordinaryActive < GATEWAY_POSTGRES_MAX_ORDINARY_ACTIVE &&
128
- this.#waiting.ordinary.length > 0
129
- ) {
144
+ if (ordinaryCanRun) {
145
+ this.#consecutiveCommitPriorityGrants = 0;
130
146
  this.#resolve(this.#waiting.ordinary.shift()!);
131
147
  continue;
132
148
  }
149
+ if (this.#waiting.ordinary.length === 0) {
150
+ this.#consecutiveCommitPriorityGrants = 0;
151
+ }
133
152
  return;
134
153
  }
135
154
  }
@@ -228,13 +228,14 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
228
228
 
229
229
  const ordered = [...rules].sort((a, b) => a.ruleId.localeCompare(b.ruleId));
230
230
  const rulesKey = rulesSignature(ordered);
231
+ const laneKey = rateLaneKey(bucketId, rulesKey);
231
232
 
232
233
  // Drawing a cached permit is a synchronous, single-threaded critical
233
234
  // section (`drawFromBlock` has no `await`), so concurrent acquires can never
234
235
  // hand out the same permit — no lock is needed for the fast path. The ONLY
235
236
  // thing that needs coordination is the block refill, which we single-flight
236
- // per bucket: one network round-trip (and any window-exhausted sleep) serves
237
- // the whole waiting wave, then everyone re-draws. This replaces the old
237
+ // per bucket + rule set: one network round-trip (and any window-exhausted
238
+ // sleep) serves the whole matching wave, then everyone re-draws. This replaces the old
238
239
  // per-bucket lock that held across the RPC + up-to-5s sleep and thereby
239
240
  // serialized every map row behind one sleeping acquirer.
240
241
  while (true) {
@@ -243,7 +244,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
243
244
  ? signal.reason
244
245
  : new Error('Rate-state acquire aborted.');
245
246
  }
246
- const localPermit = this.drawFromBlock(bucketId, rulesKey);
247
+ const localPermit = this.drawFromBlock(bucketId, laneKey);
247
248
  if (localPermit) {
248
249
  const waitMs = localPermit.scheduledAtMs - this.now();
249
250
  if (waitMs > 0) await this.sleep(waitMs);
@@ -258,19 +259,20 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
258
259
  // Count this acquirer as waiting on the refill so the single-flight refill
259
260
  // can size its block to the live wave. Decremented once a block lands (or
260
261
  // the refill errors) — see the finally in `refillBlock`.
261
- this.waiters.set(bucketId, (this.waiters.get(bucketId) ?? 0) + 1);
262
+ this.waiters.set(laneKey, (this.waiters.get(laneKey) ?? 0) + 1);
262
263
  try {
263
264
  await this.refillBlock(
264
265
  bucketId,
265
266
  input.rateScopeToken?.trim() || null,
266
267
  ordered,
267
268
  rulesKey,
269
+ laneKey,
268
270
  signal,
269
271
  );
270
272
  } finally {
271
- const remaining = (this.waiters.get(bucketId) ?? 1) - 1;
272
- if (remaining > 0) this.waiters.set(bucketId, remaining);
273
- else this.waiters.delete(bucketId);
273
+ const remaining = (this.waiters.get(laneKey) ?? 1) - 1;
274
+ if (remaining > 0) this.waiters.set(laneKey, remaining);
275
+ else this.waiters.delete(laneKey);
274
276
  }
275
277
  }
276
278
  }
@@ -285,7 +287,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
285
287
  * without reserving a single slot that cannot be used.
286
288
  */
287
289
  private requestedForBucket(
288
- bucketId: string,
290
+ laneKey: string,
289
291
  rules: readonly PacingRule[],
290
292
  ): number {
291
293
  const floor = APP_RUNTIME_RATE_STATE_LEASE_BLOCK_SIZE;
@@ -294,7 +296,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
294
296
  );
295
297
  const horizonBudget = Math.max(
296
298
  1,
297
- Math.ceil(this.effectiveRps.get(bucketId) ?? configuredRps),
299
+ Math.ceil(this.effectiveRps.get(laneKey) ?? configuredRps),
298
300
  );
299
301
  const rateBudget = Math.min(
300
302
  horizonBudget,
@@ -303,7 +305,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
303
305
  .filter((value) => Number.isFinite(value) && value > 0),
304
306
  );
305
307
  if (!Number.isFinite(rateBudget) || rateBudget <= 0) return floor;
306
- const waiters = this.waiters.get(bucketId) ?? 1;
308
+ const waiters = this.waiters.get(laneKey) ?? 1;
307
309
  const concurrencyLimits = rules.flatMap((rule) =>
308
310
  rule.maxConcurrency != null &&
309
311
  Number.isFinite(rule.maxConcurrency) &&
@@ -391,22 +393,24 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
391
393
  rateScopeToken: string | null,
392
394
  ordered: PacingRule[],
393
395
  rulesKey: string,
396
+ laneKey: string,
394
397
  signal?: AbortSignal,
395
398
  ): Promise<void> {
396
- const existing = this.refills.get(bucketId);
399
+ const existing = this.refills.get(laneKey);
397
400
  if (existing) return existing;
398
401
  const promise = this.performRefill(
399
402
  bucketId,
400
403
  rateScopeToken,
401
404
  ordered,
402
405
  rulesKey,
406
+ laneKey,
403
407
  signal,
404
408
  ).finally(() => {
405
- if (this.refills.get(bucketId) === promise) {
406
- this.refills.delete(bucketId);
409
+ if (this.refills.get(laneKey) === promise) {
410
+ this.refills.delete(laneKey);
407
411
  }
408
412
  });
409
- this.refills.set(bucketId, promise);
413
+ this.refills.set(laneKey, promise);
410
414
  return promise;
411
415
  }
412
416
 
@@ -415,6 +419,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
415
419
  rateScopeToken: string | null,
416
420
  ordered: PacingRule[],
417
421
  rulesKey: string,
422
+ laneKey: string,
418
423
  signal?: AbortSignal,
419
424
  ): Promise<void> {
420
425
  let consecutiveFailures = 0;
@@ -435,24 +440,25 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
435
440
  if (signal?.aborted) return;
436
441
  }
437
442
  try {
438
- const observedSuccesses = this.pendingSuccesses.get(bucketId) ?? 0;
443
+ const successKey = pendingSuccessKey(bucketId, ordered);
444
+ const observedSuccesses = this.pendingSuccesses.get(successKey) ?? 0;
439
445
  const response = await this.port.acquire({
440
446
  bucketId,
441
447
  rateScopeToken,
442
448
  // Stamp adaptiveMaxRps on default-pacing rules so the DB row's ceiling
443
449
  // matches the old in-memory AIMD range.
444
450
  rules: withAdaptiveMaxRps(ordered),
445
- requested: this.requestedForBucket(bucketId, ordered),
451
+ requested: this.requestedForBucket(laneKey, ordered),
446
452
  observedSuccesses,
447
453
  schedulerSchema: this.schedulerSchema,
448
454
  });
449
455
  if (observedSuccesses > 0) {
450
456
  const remaining = Math.max(
451
457
  0,
452
- (this.pendingSuccesses.get(bucketId) ?? 0) - observedSuccesses,
458
+ (this.pendingSuccesses.get(successKey) ?? 0) - observedSuccesses,
453
459
  );
454
- if (remaining > 0) this.pendingSuccesses.set(bucketId, remaining);
455
- else this.pendingSuccesses.delete(bucketId);
460
+ if (remaining > 0) this.pendingSuccesses.set(successKey, remaining);
461
+ else this.pendingSuccesses.delete(successKey);
456
462
  }
457
463
  this.recordCooldown(bucketId, response);
458
464
  if (
@@ -460,7 +466,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
460
466
  Number(response.effectiveRequestsPerSecond) > 0
461
467
  ) {
462
468
  this.effectiveRps.set(
463
- bucketId,
469
+ laneKey,
464
470
  Number(response.effectiveRequestsPerSecond),
465
471
  );
466
472
  }
@@ -472,6 +478,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
472
478
  rateScopeToken,
473
479
  ordered,
474
480
  rulesKey,
481
+ laneKey,
475
482
  response,
476
483
  );
477
484
  return;
@@ -523,6 +530,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
523
530
  rateScopeToken: string | null,
524
531
  ordered: PacingRule[],
525
532
  rulesKey: string,
533
+ laneKey: string,
526
534
  response: {
527
535
  granted: number;
528
536
  leaseIds?: unknown;
@@ -579,6 +587,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
579
587
  hasConcurrency ? (leaseIds[index] ?? null) : null,
580
588
  ]),
581
589
  rulesKey,
590
+ laneKey,
582
591
  ordered,
583
592
  );
584
593
  }
@@ -589,7 +598,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
589
598
  cooldownMs: number;
590
599
  }): Promise<void> {
591
600
  if (input.cooldownMs <= 0) return;
592
- this.drainBlock(input.bucketId);
601
+ this.drainBlocksForBucket(input.bucketId);
593
602
  try {
594
603
  await this.port.penalize({
595
604
  bucketId: input.bucketId,
@@ -609,10 +618,14 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
609
618
  }
610
619
  }
611
620
 
612
- observeSuccess(input: { bucketId: string }): void {
621
+ observeSuccess(input: {
622
+ bucketId: string;
623
+ rules: readonly PacingRule[];
624
+ }): void {
625
+ const key = pendingSuccessKey(input.bucketId, input.rules);
613
626
  this.pendingSuccesses.set(
614
- input.bucketId,
615
- Math.min(1_000_000, (this.pendingSuccesses.get(input.bucketId) ?? 0) + 1),
627
+ key,
628
+ Math.min(1_000_000, (this.pendingSuccesses.get(key) ?? 0) + 1),
616
629
  );
617
630
  }
618
631
 
@@ -645,12 +658,13 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
645
658
  rateScopeToken: string | null,
646
659
  permits: LeasedBlock['permits'],
647
660
  rulesKey: string,
661
+ laneKey: string,
648
662
  rules: PacingRule[],
649
663
  ): void {
650
664
  const lastScheduledAtMs = permits.at(-1)?.[0] ?? this.now();
651
665
  const freshExpiresAt =
652
666
  Math.max(this.now(), lastScheduledAtMs) + leasedBlockTtlMs(rules);
653
- const existing = this.blocks.get(bucketId);
667
+ const existing = this.blocks.get(laneKey);
654
668
  if (
655
669
  existing &&
656
670
  existing.rulesKey === rulesKey &&
@@ -668,7 +682,7 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
668
682
  existing.rules,
669
683
  existing.permits.flatMap(([, leaseId]) => (leaseId ? [leaseId] : [])),
670
684
  );
671
- this.blocks.set(bucketId, {
685
+ this.blocks.set(laneKey, {
672
686
  permits,
673
687
  rateScopeToken,
674
688
  expiresAt: freshExpiresAt,
@@ -679,12 +693,12 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
679
693
 
680
694
  private drawFromBlock(
681
695
  bucketId: string,
682
- rulesKey: string,
696
+ laneKey: string,
683
697
  ): { permit: PacingPermit; scheduledAtMs: number } | null {
684
- const block = this.blocks.get(bucketId);
698
+ const block = this.blocks.get(laneKey);
685
699
  if (!block) return null;
686
- if (block.rulesKey !== rulesKey || block.expiresAt <= this.now()) {
687
- this.blocks.delete(bucketId);
700
+ if (block.expiresAt <= this.now()) {
701
+ this.blocks.delete(laneKey);
688
702
  // Unused window permits are forfeited on TTL expiry (the server already
689
703
  // decremented the provider window for them, and the rule-scoped TTL is
690
704
  // capped so at most one block's worth is stranded per bucket/window).
@@ -699,10 +713,10 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
699
713
  }
700
714
  const scheduled = block.permits.shift();
701
715
  if (!scheduled) {
702
- this.blocks.delete(bucketId);
716
+ this.blocks.delete(laneKey);
703
717
  return null;
704
718
  }
705
- if (block.permits.length === 0) this.blocks.delete(bucketId);
719
+ if (block.permits.length === 0) this.blocks.delete(laneKey);
706
720
  return {
707
721
  permit: this.permitFor(
708
722
  bucketId,
@@ -714,16 +728,18 @@ export class AppRuntimeRateStateBackend implements RateStateBackend {
714
728
  };
715
729
  }
716
730
 
717
- private drainBlock(bucketId: string): void {
718
- const block = this.blocks.get(bucketId);
719
- if (!block) return;
720
- this.blocks.delete(bucketId);
721
- this.releaseReserved(
722
- bucketId,
723
- block.rateScopeToken,
724
- block.rules,
725
- block.permits.flatMap(([, leaseId]) => (leaseId ? [leaseId] : [])),
726
- );
731
+ private drainBlocksForBucket(bucketId: string): void {
732
+ const prefix = `${bucketId}\0`;
733
+ for (const [laneKey, block] of this.blocks) {
734
+ if (!laneKey.startsWith(prefix)) continue;
735
+ this.blocks.delete(laneKey);
736
+ this.releaseReserved(
737
+ bucketId,
738
+ block.rateScopeToken,
739
+ block.rules,
740
+ block.permits.flatMap(([, leaseId]) => (leaseId ? [leaseId] : [])),
741
+ );
742
+ }
727
743
  }
728
744
 
729
745
  private releaseReserved(
@@ -814,20 +830,22 @@ function isDefaultPacingRuleId(ruleId: string): boolean {
814
830
  }
815
831
 
816
832
  /**
817
- * Stamp `adaptiveMaxRps` onto default-pacing rules so the DB row's ceiling matches
818
- * the old in-memory AIMD range (base * 25). Declared rules pass through untouched
819
- * (the DB pins them at base). The field is extra on the wire; the server's
820
- * `normalizeRules` reads it, everyone else ignores it.
833
+ * Preserve explicit advisory ceilings and stamp the legacy base * 25 ceiling
834
+ * only onto undeclared default-pacing rules. Declared rules without a ceiling
835
+ * remain pinned at base.
821
836
  */
822
837
  function withAdaptiveMaxRps(rules: readonly PacingRule[]): PacingRule[] {
823
838
  return rules.map((rule) =>
824
- isDefaultPacingRuleId(rule.ruleId)
825
- ? ({
826
- ...rule,
827
- adaptiveMaxRps:
828
- rule.requestsPerWindow * DEFAULT_PACING_ADAPTIVE_MAX_RPS_MULTIPLIER,
829
- } as PacingRule)
830
- : rule,
839
+ rule.adaptiveMaxRps != null
840
+ ? rule
841
+ : isDefaultPacingRuleId(rule.ruleId)
842
+ ? ({
843
+ ...rule,
844
+ adaptiveMaxRps:
845
+ rule.requestsPerWindow *
846
+ DEFAULT_PACING_ADAPTIVE_MAX_RPS_MULTIPLIER,
847
+ } as PacingRule)
848
+ : rule,
831
849
  );
832
850
  }
833
851
 
@@ -835,12 +853,23 @@ function rulesSignature(rules: readonly PacingRule[]): string {
835
853
  return [...rules]
836
854
  .map(
837
855
  (rule) =>
838
- `${rule.ruleId}:${rule.requestsPerWindow}:${rule.windowMs}:${rule.maxConcurrency ?? ''}`,
856
+ `${rule.ruleId}:${rule.requestsPerWindow}:${rule.windowMs}:${rule.adaptiveMaxRps ?? ''}:${rule.maxConcurrency ?? ''}`,
839
857
  )
840
858
  .sort()
841
859
  .join('|');
842
860
  }
843
861
 
862
+ function rateLaneKey(bucketId: string, rulesKey: string): string {
863
+ return `${bucketId}\0${rulesKey}`;
864
+ }
865
+
866
+ function pendingSuccessKey(
867
+ bucketId: string,
868
+ rules: readonly PacingRule[],
869
+ ): string {
870
+ return rateLaneKey(bucketId, rulesSignature(rules));
871
+ }
872
+
844
873
  function leasedBlockTtlMs(rules: readonly PacingRule[]): number {
845
874
  const shortestWindowMs = Math.min(
846
875
  ...rules
@@ -267,6 +267,7 @@ export function createPlayExecutionGovernor(
267
267
  });
268
268
  const budgetState = input.budgetState ?? new InMemoryBudgetStateBackend();
269
269
  const providerByTool = new Map<string, string>();
270
+ const pacingRulesByTool = new Map<string, PacingRule[]>();
270
271
  const scopeByTool = new Map<string, [string, string]>();
271
272
  const providerConcurrencySlotsByTool = new Map<string, Semaphore>();
272
273
 
@@ -308,6 +309,7 @@ export function createPlayExecutionGovernor(
308
309
  // row is authoritative.
309
310
  const pacing = declaredPacing ?? defaultPacingForTool(toolId, policy);
310
311
  providerByTool.set(toolId, pacing.provider);
312
+ pacingRulesByTool.set(toolId, pacing.rules);
311
313
  return pacing;
312
314
  }
313
315
  const pacing = adaptiveAdmission.resolvePacing({
@@ -317,6 +319,7 @@ export function createPlayExecutionGovernor(
317
319
  fallbackPacing: defaultPacingForTool(toolId, policy),
318
320
  });
319
321
  providerByTool.set(toolId, pacing.provider);
322
+ pacingRulesByTool.set(toolId, pacing.rules);
320
323
  return pacing;
321
324
  }
322
325
 
@@ -486,9 +489,13 @@ export function createPlayExecutionGovernor(
486
489
 
487
490
  observeProviderSuccess(success) {
488
491
  if (dbAuthoritativePacing) {
492
+ if (!success.toolId) return;
489
493
  const rateScope = scopeFor(success.toolId, success.provider);
494
+ const rules = pacingRulesByTool.get(success.toolId);
495
+ if (!rules) return;
490
496
  input.rateState.observeSuccess?.({
491
497
  bucketId: rateScope[0],
498
+ rules,
492
499
  });
493
500
  return;
494
501
  }
@@ -11,6 +11,8 @@ export interface PacingRule {
11
11
  readonly ruleId: string;
12
12
  readonly requestsPerWindow: number;
13
13
  readonly windowMs: number;
14
+ /** Optional learned-rate ceiling in requests per second. */
15
+ readonly adaptiveMaxRps?: number | null;
14
16
  /**
15
17
  * Optional simultaneous-in-flight cap for this rule.
16
18
  *
@@ -35,6 +37,7 @@ export interface PlayQueueHint {
35
37
  ruleId: string;
36
38
  requestsPerWindow: number;
37
39
  windowMs: number;
40
+ adaptiveMaxRps?: number | null;
38
41
  maxConcurrency: number | null;
39
42
  }
40
43
 
@@ -88,6 +91,7 @@ export interface RateStateBackend {
88
91
  observeSuccess?(input: {
89
92
  bucketId: string;
90
93
  rateScopeToken?: string | null;
94
+ rules: readonly PacingRule[];
91
95
  }): void;
92
96
  }
93
97
 
@@ -41,6 +41,12 @@ export const RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
41
41
  * is required to resume execution.
42
42
  */
43
43
  export const RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
44
+ /**
45
+ * Receipt commands are measured before the runtime client adds its request
46
+ * envelope. Keep a full 4 MiB below the gateway body ceiling so one legal
47
+ * 10 MiB receipt fits while compatible fat receipts split before transport.
48
+ */
49
+ export const RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
44
50
  /**
45
51
  * The terminal `result` line is the crash-recovery record in Daytona stdout.
46
52
  * Once that line is written, retries may still emit runner-owned diagnostics.
@@ -8,7 +8,12 @@ export type ResolvedPacingPolicy = {
8
8
 
9
9
  type PacingPolicyQueueHint = Pick<
10
10
  PlayQueueHint,
11
- 'provider' | 'ruleId' | 'requestsPerWindow' | 'windowMs' | 'maxConcurrency'
11
+ | 'provider'
12
+ | 'ruleId'
13
+ | 'requestsPerWindow'
14
+ | 'windowMs'
15
+ | 'adaptiveMaxRps'
16
+ | 'maxConcurrency'
12
17
  >;
13
18
 
14
19
  function isFinitePositiveNumber(value: unknown): value is number {
@@ -35,6 +40,9 @@ export function pacingPolicyFromQueueHints(
35
40
  ruleId: hint.ruleId,
36
41
  requestsPerWindow: hint.requestsPerWindow,
37
42
  windowMs: hint.windowMs,
43
+ adaptiveMaxRps: isFinitePositiveNumber(hint.adaptiveMaxRps)
44
+ ? hint.adaptiveMaxRps
45
+ : null,
38
46
  maxConcurrency:
39
47
  typeof hint.maxConcurrency === 'number' &&
40
48
  Number.isFinite(hint.maxConcurrency) &&
@@ -65,6 +73,8 @@ export function pacingPolicyFromUnknownQueueHints(
65
73
  : Number.NaN,
66
74
  windowMs:
67
75
  typeof hint.windowMs === 'number' ? hint.windowMs : Number.NaN,
76
+ adaptiveMaxRps:
77
+ typeof hint.adaptiveMaxRps === 'number' ? hint.adaptiveMaxRps : null,
68
78
  maxConcurrency:
69
79
  typeof hint.maxConcurrency === 'number' ? hint.maxConcurrency : null,
70
80
  })),
@@ -111,9 +111,26 @@ export async function inspectDetachedDaytonaStartup(input: {
111
111
  * verification.
112
112
  */
113
113
 
114
- export type DetachedDaytonaCommandStart = {
115
- sessionId: string;
116
- cmdId: string;
114
+ export type DetachedDaytonaRunnerState =
115
+ | { phase: 'preparing'; sessionId: string }
116
+ | {
117
+ phase: 'command_accepted';
118
+ sessionId: string;
119
+ cmdId: string;
120
+ baselineHeartbeatAt: string | null;
121
+ }
122
+ | {
123
+ phase: 'ready';
124
+ sessionId: string;
125
+ cmdId: string;
126
+ baselineHeartbeatAt: string | null;
127
+ };
128
+
129
+ export type DetachedDaytonaRunnerSupervisor = {
130
+ start(input: {
131
+ command: string;
132
+ exitCodePath: string;
133
+ }): Promise<Extract<DetachedDaytonaRunnerState, { phase: 'ready' }>>;
117
134
  };
118
135
 
119
136
  // The durable heartbeat proves runner-process liveness plus gateway reachability.
@@ -235,44 +252,82 @@ export class DaytonaRunnerInitializationError extends Error {
235
252
  }
236
253
 
237
254
  /**
238
- * Start `command` detached in a fresh session. Returns the session + command id
239
- * immediately (no await on completion).
255
+ * Begin the independent control-plane work needed by a detached runner while
256
+ * its payload uploads. `start` crosses the customer-code side-effect boundary:
257
+ * it accepts one command, then returns only after the scheduler has observed
258
+ * durable runner liveness.
240
259
  */
241
- export async function startDetachedDaytonaCommand(input: {
260
+ export function prepareDetachedDaytonaRunner(input: {
242
261
  sandbox: DaytonaSandbox;
243
262
  sessionId: string;
244
- command: string;
245
- }): Promise<DetachedDaytonaCommandStart> {
246
- await createDetachedDaytonaSession(input);
247
- return executeDetachedDaytonaSessionCommand(input);
248
- }
249
-
250
- export async function createDetachedDaytonaSession(input: {
251
- sandbox: DaytonaSandbox;
252
- sessionId: string;
253
- }): Promise<void> {
254
- await input.sandbox.process.createSession(input.sessionId);
255
- }
263
+ readiness: DetachedRunnerReadinessPort;
264
+ cancellation?: Promise<never>;
265
+ }): DetachedDaytonaRunnerSupervisor {
266
+ const cancellation = input.cancellation ?? new Promise<never>(() => {});
267
+ const sessionCreation = input.sandbox.process
268
+ .createSession(input.sessionId)
269
+ .then(
270
+ () => ({ ok: true as const }),
271
+ (error: unknown) => ({ ok: false as const, error }),
272
+ );
273
+ const readinessBaseline = captureDetachedDaytonaRunnerReadinessBaseline({
274
+ readiness: input.readiness,
275
+ }).then(
276
+ (heartbeatAt) => ({ ok: true as const, heartbeatAt }),
277
+ (error: unknown) => ({ ok: false as const, error }),
278
+ );
279
+ let state: DetachedDaytonaRunnerState = {
280
+ phase: 'preparing',
281
+ sessionId: input.sessionId,
282
+ };
283
+ let started = false;
256
284
 
257
- export async function executeDetachedDaytonaSessionCommand(input: {
258
- sandbox: DaytonaSandbox;
259
- sessionId: string;
260
- command: string;
261
- }): Promise<DetachedDaytonaCommandStart> {
262
- const response = await input.sandbox.process.executeSessionCommand(
263
- input.sessionId,
264
- {
265
- command: input.command,
266
- runAsync: true,
285
+ return {
286
+ async start({ command, exitCodePath }) {
287
+ if (started) {
288
+ throw new Error(
289
+ `Detached Daytona runner session ${input.sessionId} was already started.`,
290
+ );
291
+ }
292
+ started = true;
293
+ const session = await Promise.race([sessionCreation, cancellation]);
294
+ if (session.ok === false) throw session.error;
295
+ const baseline = await Promise.race([readinessBaseline, cancellation]);
296
+ if (baseline.ok === false) throw baseline.error;
297
+ const response = await Promise.race([
298
+ input.sandbox.process.executeSessionCommand(input.sessionId, {
299
+ command,
300
+ runAsync: true,
301
+ }),
302
+ cancellation,
303
+ ]);
304
+ const cmdId = response?.cmdId;
305
+ if (!cmdId) {
306
+ throw new Error(
307
+ `Daytona executeSessionCommand did not return a cmdId for session ${input.sessionId}.`,
308
+ );
309
+ }
310
+ state = {
311
+ phase: 'command_accepted',
312
+ sessionId: input.sessionId,
313
+ cmdId,
314
+ baselineHeartbeatAt: baseline.heartbeatAt,
315
+ };
316
+ await Promise.race([
317
+ confirmDetachedDaytonaRunnerReady({
318
+ sandbox: input.sandbox,
319
+ sessionId: state.sessionId,
320
+ cmdId: state.cmdId,
321
+ exitCodePath,
322
+ readiness: input.readiness,
323
+ baselineHeartbeatAt: state.baselineHeartbeatAt,
324
+ }),
325
+ cancellation,
326
+ ]);
327
+ state = { ...state, phase: 'ready' };
328
+ return state;
267
329
  },
268
- );
269
- const cmdId = response?.cmdId;
270
- if (!cmdId) {
271
- throw new Error(
272
- `Daytona executeSessionCommand did not return a cmdId for session ${input.sessionId}.`,
273
- );
274
- }
275
- return { sessionId: input.sessionId, cmdId };
330
+ };
276
331
  }
277
332
 
278
333
  /**
@@ -34,11 +34,8 @@ import {
34
34
  } from './daytona-lifecycle';
35
35
  import { stageDaytonaRunnerPayload } from './daytona-payload-transport';
36
36
  import {
37
- captureDetachedDaytonaRunnerReadinessBaseline,
38
- confirmDetachedDaytonaRunnerReady,
39
- createDetachedDaytonaSession,
40
37
  DaytonaRunnerInitializationError,
41
- executeDetachedDaytonaSessionCommand,
38
+ prepareDetachedDaytonaRunner,
42
39
  } from './daytona-session-execution';
43
40
 
44
41
  const DAYTONA_EXECUTE_TIMEOUT_SECONDS = STANDARD_PLAY_RUNTIME_LIMIT_SECONDS;
@@ -894,20 +891,12 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
894
891
  // Session allocation is independent of payload staging. Start it
895
892
  // now so Daytona control-plane latency overlaps compression/upload.
896
893
  const sessionId = `deepline-play-${randomUUID()}`;
897
- const sessionCreation = createDetachedDaytonaSession({
894
+ const detachedRunner = prepareDetachedDaytonaRunner({
898
895
  sandbox,
899
896
  sessionId,
900
- }).then(
901
- () => ({ ok: true as const }),
902
- (error: unknown) => ({ ok: false as const, error }),
903
- );
904
- const readinessBaseline =
905
- captureDetachedDaytonaRunnerReadinessBaseline({
906
- readiness: readRunnerReadiness,
907
- }).then(
908
- (heartbeatAt) => ({ ok: true as const, heartbeatAt }),
909
- (error: unknown) => ({ ok: false as const, error }),
910
- );
897
+ readiness: readRunnerReadiness,
898
+ cancellation: cancellationPromise,
899
+ });
911
900
 
912
901
  const uploadStartedAt = Date.now();
913
902
  let uploadDeadlineTimer: ReturnType<typeof setTimeout> | null = null;
@@ -996,28 +985,12 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
996
985
  // gateway heartbeat. A Daytona cmdId alone does not prove liveness.
997
986
  // After that bounded startup check, the worker parks (releasing its
998
987
  // claim and slot) and wakes on terminal push or the ceiling timeout.
999
- const session = await Promise.race([
1000
- sessionCreation,
1001
- cancellationPromise,
1002
- ]);
1003
- if (session.ok === false) throw session.error;
1004
- const baseline = await Promise.race([
1005
- readinessBaseline,
1006
- cancellationPromise,
1007
- ]);
1008
- if (baseline.ok === false) throw baseline.error;
1009
- let start: Awaited<
1010
- ReturnType<typeof executeDetachedDaytonaSessionCommand>
1011
- >;
988
+ let start: Awaited<ReturnType<typeof detachedRunner.start>>;
1012
989
  try {
1013
- start = await Promise.race([
1014
- executeDetachedDaytonaSessionCommand({
1015
- sandbox,
1016
- sessionId,
1017
- command: stagedPayload.command,
1018
- }),
1019
- cancellationPromise,
1020
- ]);
990
+ start = await detachedRunner.start({
991
+ command: stagedPayload.command,
992
+ exitCodePath: stagedPayload.exitCodePath,
993
+ });
1021
994
  } catch (error) {
1022
995
  if (
1023
996
  cancellationRequested ||
@@ -1030,17 +1003,6 @@ export const daytonaPlayRunnerBackend: PlayRunnerBackend = {
1030
1003
  // classification below decide fresh-sandbox retry vs loud failure.
1031
1004
  throw error;
1032
1005
  }
1033
- await Promise.race([
1034
- confirmDetachedDaytonaRunnerReady({
1035
- sandbox,
1036
- sessionId: start.sessionId,
1037
- cmdId: start.cmdId,
1038
- exitCodePath: stagedPayload.exitCodePath,
1039
- readiness: readRunnerReadiness,
1040
- baselineHeartbeatAt: baseline.heartbeatAt,
1041
- }),
1042
- cancellationPromise,
1043
- ]);
1044
1006
  const runnerAttempt = Math.max(
1045
1007
  0,
1046
1008
  Math.floor(config.context.runAttempt ?? 0),
@@ -3,6 +3,7 @@ import type { DbLogicalTable, DbSessionOperation } from './db-session';
3
3
  import type { PlayRowUpdate } from './ctx-types';
4
4
  import type { PlaySheetContract } from '../plays/static-pipeline';
5
5
  import type { PlayRunLedgerEvent } from './run-ledger';
6
+ import type { WorkReceiptFailureKind } from './work-receipts';
6
7
 
7
8
  export type RuntimeAppendRunEventsAction = {
8
9
  action: 'append_run_events';
@@ -94,11 +95,47 @@ export type RuntimeReceiptAction =
94
95
  runId: string;
95
96
  key: string;
96
97
  }
98
+ | {
99
+ action: 'get_runtime_step_receipts';
100
+ playName: string;
101
+ runId: string;
102
+ keys: string[];
103
+ }
104
+ | {
105
+ action: 'acquire_runtime_receipt_execution_lock';
106
+ playName: string;
107
+ runId: string;
108
+ key: string;
109
+ ownerExecutionId: string;
110
+ ttlMs: number;
111
+ }
112
+ | {
113
+ action: 'release_runtime_receipt_execution_lock';
114
+ playName: string;
115
+ runId: string;
116
+ key: string;
117
+ ownerExecutionId: string;
118
+ }
97
119
  | {
98
120
  action: 'claim_runtime_step_receipt';
99
121
  playName: string;
100
122
  runId: string;
101
123
  key: string;
124
+ leaseId?: string;
125
+ runAttempt?: number | null;
126
+ leaseAware?: boolean;
127
+ reclaimRunning?: boolean;
128
+ forceRefresh?: boolean;
129
+ forceFailedRefresh?: boolean;
130
+ }
131
+ | {
132
+ action: 'claim_runtime_step_receipts';
133
+ playName: string;
134
+ runId: string;
135
+ keys: string[];
136
+ leaseIds?: string[];
137
+ runAttempt?: number | null;
138
+ leaseAware?: boolean;
102
139
  reclaimRunning?: boolean;
103
140
  forceRefresh?: boolean;
104
141
  forceFailedRefresh?: boolean;
@@ -108,6 +145,8 @@ export type RuntimeReceiptAction =
108
145
  playName: string;
109
146
  runId: string;
110
147
  key: string;
148
+ runAttempt?: number | null;
149
+ leaseId?: string | null;
111
150
  output: unknown;
112
151
  }
113
152
  | {
@@ -115,7 +154,7 @@ export type RuntimeReceiptAction =
115
154
  playName: string;
116
155
  runId: string;
117
156
  key: string;
118
- runAttempt?: number;
157
+ runAttempt?: number | null;
119
158
  leaseId?: string | null;
120
159
  }
121
160
  | {
@@ -123,16 +162,77 @@ export type RuntimeReceiptAction =
123
162
  playName: string;
124
163
  runId: string;
125
164
  key: string;
165
+ runAttempt?: number | null;
166
+ leaseId?: string | null;
126
167
  error: string;
168
+ failureKind?: WorkReceiptFailureKind | null;
127
169
  }
128
170
  | {
129
171
  action: 'skip_runtime_step_receipt';
130
172
  playName: string;
131
173
  runId: string;
132
174
  key: string;
175
+ runAttempt?: number | null;
176
+ leaseId?: string | null;
133
177
  output?: unknown;
178
+ }
179
+ | {
180
+ action: 'mark_runtime_step_receipt_running';
181
+ playName: string;
182
+ runId: string;
183
+ key: string;
184
+ runAttempt?: number | null;
185
+ leaseId?: string | null;
186
+ }
187
+ | {
188
+ action: 'mark_runtime_step_receipts_running';
189
+ playName: string;
190
+ runId: string;
191
+ receipts: RuntimeReceiptLease[];
192
+ }
193
+ | {
194
+ action: 'mark_runtime_step_receipts_queued';
195
+ playName: string;
196
+ runId: string;
197
+ receipts: RuntimeReceiptLease[];
198
+ }
199
+ | {
200
+ action: 'complete_runtime_step_receipts';
201
+ playName: string;
202
+ runId: string;
203
+ receipts: RuntimeReceiptCompletion[];
204
+ }
205
+ | {
206
+ action: 'fail_runtime_step_receipts';
207
+ playName: string;
208
+ runId: string;
209
+ receipts: RuntimeReceiptFailure[];
210
+ }
211
+ | {
212
+ action: 'heartbeat_runtime_step_receipts';
213
+ playName: string;
214
+ runId: string;
215
+ runAttempt?: number | null;
216
+ leaseId: string;
217
+ keys: string[];
134
218
  };
135
219
 
220
+ export type RuntimeReceiptLease = {
221
+ key: string;
222
+ runId: string;
223
+ runAttempt?: number | null;
224
+ leaseId?: string | null;
225
+ };
226
+
227
+ export type RuntimeReceiptCompletion = RuntimeReceiptLease & {
228
+ output: unknown;
229
+ };
230
+
231
+ export type RuntimeReceiptFailure = RuntimeReceiptLease & {
232
+ error: string;
233
+ failureKind?: WorkReceiptFailureKind | null;
234
+ };
235
+
136
236
  export type RuntimePlayResolutionAction = {
137
237
  action: 'resolve_play';
138
238
  playRef: string;
@@ -9,6 +9,7 @@ import type {
9
9
  RuntimeStepReceipt,
10
10
  SkipRuntimeStepReceiptInput,
11
11
  } from './ctx-types';
12
+ import { RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES } from './output-size-limits';
12
13
  import {
13
14
  acquireRuntimeReceiptExecutionLockViaAppRuntime,
14
15
  AppRuntimeApiResponseError,
@@ -120,6 +121,7 @@ export class RuntimeReceiptStoreAdapter {
120
121
  playName: string;
121
122
  runId: string;
122
123
  maxBatchSize?: number;
124
+ maxBatchBytes?: number;
123
125
  maxFlushMs?: number;
124
126
  maxBufferedBytes?: number;
125
127
  }) {
@@ -130,6 +132,8 @@ export class RuntimeReceiptStoreAdapter {
130
132
  this.#writer = new RuntimeReceiptWriter({
131
133
  batchKey: receiptCommandBatchKey,
132
134
  maxBatchSize: options.maxBatchSize,
135
+ maxBatchBytes:
136
+ options.maxBatchBytes ?? RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES,
133
137
  maxFlushMs: options.maxFlushMs,
134
138
  maxBufferedBytes: options.maxBufferedBytes,
135
139
  classifyRetryableError: classifyReceiptRetry,
@@ -2,6 +2,8 @@ type RetryableReceiptError = {
2
2
  retryAfterMs: number;
3
3
  };
4
4
 
5
+ const MAX_RETRY_JITTER_RATIO = 0.25;
6
+
5
7
  type PendingWrite<Input, Output> = {
6
8
  input: Input;
7
9
  bytes: number;
@@ -320,7 +322,7 @@ export class RuntimeReceiptWriter<Input, Output> {
320
322
  const retry = this.#classifyRetryableError(error);
321
323
  if (!retry) throw error;
322
324
  await this.#waitForRetry(
323
- Math.max(0, Math.floor(retry.retryAfterMs)),
325
+ jitteredRetryDelayMs(retry.retryAfterMs),
324
326
  batch,
325
327
  );
326
328
  }
@@ -489,6 +491,14 @@ function normalizePositiveInteger(
489
491
  return Math.max(1, Math.floor(value ?? fallback));
490
492
  }
491
493
 
494
+ function jitteredRetryDelayMs(delayMs: number): number {
495
+ const normalized = Math.max(0, Math.floor(delayMs));
496
+ if (normalized === 0) return 0;
497
+ return (
498
+ normalized + Math.floor(normalized * MAX_RETRY_JITTER_RATIO * Math.random())
499
+ );
500
+ }
501
+
492
502
  function abortReason(signal: AbortSignal | null): unknown {
493
503
  return signal?.reason ?? new Error('Runtime receipt write was aborted.');
494
504
  }
package/dist/cli/index.js CHANGED
@@ -718,7 +718,7 @@ var SDK_RELEASE = {
718
718
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
719
719
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
720
720
  // Operators use the checkout-local deepline-admin binary instead.
721
- version: "0.1.293",
721
+ version: "0.1.295",
722
722
  contracts: {
723
723
  api: {
724
724
  name: "sdk-http-api",
@@ -1600,6 +1600,7 @@ var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
1600
1600
  var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
1601
1601
  var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
1602
1602
  var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
1603
+ var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
1603
1604
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
1604
1605
 
1605
1606
  // ../shared_libs/play-runtime/ledger-safe-payload.ts
@@ -703,7 +703,7 @@ var SDK_RELEASE = {
703
703
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
704
704
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
705
705
  // Operators use the checkout-local deepline-admin binary instead.
706
- version: "0.1.293",
706
+ version: "0.1.295",
707
707
  contracts: {
708
708
  api: {
709
709
  name: "sdk-http-api",
@@ -1585,6 +1585,7 @@ var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
1585
1585
  var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
1586
1586
  var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
1587
1587
  var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
1588
+ var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
1588
1589
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
1589
1590
 
1590
1591
  // ../shared_libs/play-runtime/ledger-safe-payload.ts
package/dist/index.js CHANGED
@@ -438,7 +438,7 @@ var SDK_RELEASE = {
438
438
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
439
439
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
440
440
  // Operators use the checkout-local deepline-admin binary instead.
441
- version: "0.1.293",
441
+ version: "0.1.295",
442
442
  contracts: {
443
443
  api: {
444
444
  name: "sdk-http-api",
@@ -1320,6 +1320,7 @@ var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
1320
1320
  var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
1321
1321
  var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
1322
1322
  var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
1323
+ var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
1323
1324
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
1324
1325
 
1325
1326
  // ../shared_libs/play-runtime/ledger-safe-payload.ts
package/dist/index.mjs CHANGED
@@ -367,7 +367,7 @@ var SDK_RELEASE = {
367
367
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
368
368
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
369
369
  // Operators use the checkout-local deepline-admin binary instead.
370
- version: "0.1.293",
370
+ version: "0.1.295",
371
371
  contracts: {
372
372
  api: {
373
373
  name: "sdk-http-api",
@@ -1249,6 +1249,7 @@ var CUSTOMER_OUTPUT_TOTAL_MAX_BYTES = 5 * 1024 * 1024;
1249
1249
  var RUNTIME_RECEIPT_OUTPUT_MAX_BYTES = 10 * 1024 * 1024;
1250
1250
  var RUNTIME_RECEIPT_COMPLETION_BUFFER_MAX_BYTES = 32 * 1024 * 1024;
1251
1251
  var RUNNER_TERMINAL_PUSH_MAX_BODY_BYTES = 16 * 1024 * 1024;
1252
+ var RUNTIME_RECEIPT_GATEWAY_BATCH_MAX_BYTES = 12 * 1024 * 1024;
1252
1253
  var RUNNER_POST_TERMINAL_DIAGNOSTIC_MAX_BYTES = 64 * 1024;
1253
1254
 
1254
1255
  // ../shared_libs/play-runtime/ledger-safe-payload.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.293",
3
+ "version": "0.1.295",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {