deepline 0.3.128 → 0.3.129

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 (128) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +34 -9
  2. package/dist/bundling-sources/sdk/src/http.ts +22 -0
  3. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  4. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +22 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +265 -2
  6. package/dist/bundling-sources/shared_libs/play-runtime/bounded-batch-function.ts +682 -0
  7. package/dist/bundling-sources/shared_libs/play-runtime/child-execution-strategy.ts +21 -5
  8. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5390 -1732
  9. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +107 -1
  10. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +7 -0
  11. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +229 -7
  12. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +25 -4
  13. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-capacity.ts +71 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +1 -1
  15. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +4 -1
  16. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +694 -28
  17. package/dist/bundling-sources/shared_libs/play-runtime/egress-bandwidth-probe.ts +167 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/execute-server-timing.ts +103 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-receipt-output-reference.ts +34 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-response-size.ts +27 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/gateway-postgres-admission.ts +66 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +425 -71
  23. package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +210 -32
  24. package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +27 -13
  25. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +19 -6
  26. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +61 -2
  27. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +33 -1
  28. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +42 -1
  29. package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +11 -11
  30. package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +49 -16
  31. package/dist/bundling-sources/shared_libs/play-runtime/map-memory-limits.ts +1653 -98
  32. package/dist/bundling-sources/shared_libs/play-runtime/memory-ledger.ts +807 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/memory-pressure-signal.ts +138 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +85 -3
  35. package/dist/bundling-sources/shared_libs/play-runtime/persistence-latch.ts +60 -2
  36. package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +46 -1
  37. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +2 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +1 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +1 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +1 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +70 -27
  42. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +56 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/adaptive-provider-capacity-controller.ts +192 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/epoch-aimd-rate-controller.ts +194 -0
  45. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/fixture-capacity-simulator.ts +220 -0
  46. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/gradient2-concurrency-controller.ts +214 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-capacity-policy.ts +43 -0
  48. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-concurrency-policy.ts +30 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-rate-policy.ts +95 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/provider-capacity/provider-throttle-classifier.ts +40 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/resource-governor.ts +1535 -11
  52. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +49 -2
  53. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +6 -1
  54. package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +55 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/egress-probe.ts +221 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +339 -29
  57. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +28 -1
  58. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +192 -165
  59. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +6 -0
  60. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +90 -5
  61. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-runtime-watchdog.ts +281 -6
  62. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +27 -1
  63. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/local-process.ts +19 -0
  64. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/modal.ts +108 -0
  65. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +40 -0
  66. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/types.ts +11 -5
  67. package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +30 -0
  68. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api-compression.ts +276 -0
  69. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +723 -115
  70. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +98 -2
  71. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +3 -2
  72. package/dist/bundling-sources/shared_libs/play-runtime/runtime-environment.ts +41 -4
  73. package/dist/bundling-sources/shared_libs/play-runtime/runtime-flow.ts +448 -0
  74. package/dist/bundling-sources/shared_libs/play-runtime/runtime-map-row-feed.ts +215 -0
  75. package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +11 -0
  76. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +161 -14
  77. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-value.ts +184 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +607 -121
  79. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-placement-policy.ts +4 -0
  80. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sheet-row-writer.ts +21 -0
  81. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +15 -2
  82. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +71 -14
  83. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +2 -2
  84. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +288 -119
  85. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -6
  86. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +28 -3
  87. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/canonical-integration-operation-store.ts +179 -0
  88. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +80 -2
  89. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +31 -2
  90. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-store-contract.ts +104 -0
  91. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-reference.ts +47 -0
  92. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-retirement.ts +193 -0
  93. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/integration-operation-value-storage.ts +420 -0
  94. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +41 -0
  95. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +134 -3
  96. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +1657 -95
  97. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +885 -73
  98. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-completion-coalescer.ts +452 -0
  99. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +601 -42
  100. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +1 -0
  101. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +371 -0
  102. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +86 -7
  103. package/dist/bundling-sources/shared_libs/play-runtime/secret-redaction.ts +153 -22
  104. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/index.ts +1 -0
  105. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +188 -38
  106. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +28 -13
  107. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +44 -1
  108. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +56 -4
  109. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +2 -0
  110. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +16 -10
  111. package/dist/bundling-sources/shared_libs/plays/dataset.ts +4 -1
  112. package/dist/cli/index.js +250 -38
  113. package/dist/cli/index.mjs +250 -38
  114. package/dist/{compiler-manifest-DfSnbCTW.d.mts → compiler-manifest-BIqRyj5m.d.mts} +1 -1
  115. package/dist/{compiler-manifest-DfSnbCTW.d.ts → compiler-manifest-BIqRyj5m.d.ts} +1 -1
  116. package/dist/index.d.mts +4 -2
  117. package/dist/index.d.ts +4 -2
  118. package/dist/index.js +231 -35
  119. package/dist/index.mjs +231 -35
  120. package/dist/install-integrity.json +31 -2
  121. package/dist/plays/bundle-play-file.d.mts +2 -2
  122. package/dist/plays/bundle-play-file.d.ts +2 -2
  123. package/dist/plays/bundle-play-file.mjs +65 -1
  124. package/dist/release.d.mts +1 -1
  125. package/dist/release.d.ts +1 -1
  126. package/dist/release.js +1 -1
  127. package/dist/release.mjs +1 -1
  128. package/package.json +1 -1
@@ -14,6 +14,8 @@ const VERCEL_RUNTIME_API_DEPLOYMENT_MISSING_RE =
14
14
  /runtime API 404:[\s\S]*(?:DeploymentNotFound|DEPLOYMENT_NOT_FOUND|requested deployment .*exist|deployment could not be found on Vercel)/i;
15
15
  const RUNTIME_LIMIT_EXCEEDED_RE = /\bRUNTIME_LIMIT_EXCEEDED\b/i;
16
16
  const SANDBOX_CAPACITY_EXCEEDED_RE = /\bSANDBOX_CAPACITY_EXCEEDED\b/i;
17
+ const RUNTIME_CONTROL_PLANE_UNAVAILABLE_RE =
18
+ /\bRUNTIME_CONTROL_PLANE_UNAVAILABLE\b/i;
17
19
  const RUNTIME_RUNNER_LOST_RE = /\bRUNTIME_RUNNER_LOST\b/i;
18
20
  const RUNTIME_SANDBOX_INSPECTION_UNAVAILABLE_RE =
19
21
  /\bRUNTIME_SANDBOX_INSPECTION_UNAVAILABLE\b/i;
@@ -21,6 +23,7 @@ const RUNTIME_SANDBOX_LOST_RE = /\bRUNTIME_SANDBOX_LOST\b/i;
21
23
  const RUNTIME_SANDBOX_START_FAILED_RE = /\bRUNTIME_SANDBOX_START_FAILED\b/i;
22
24
  const RUNTIME_SANDBOX_OOM_RE =
23
25
  /\bRUNTIME_SANDBOX_OOM\b|(?:javascript heap out of memory|fatal error:.*(?:heap|allocation).*memory)/i;
26
+ const RUNTIME_SANDBOX_MEMORY_ABORT_RE = /\bRUNTIME_SANDBOX_MEMORY_ABORT\b/i;
24
27
  const RUNTIME_SANDBOX_KILLED_RE = /\bRUNTIME_SANDBOX_KILLED\b/i;
25
28
  const MODAL_PAYLOAD_UPLOAD_TIMEOUT_RE = /\bMODAL_PAYLOAD_UPLOAD_TIMEOUT\b/i;
26
29
  const OUTPUT_TOO_LARGE_RE = /\b(?:OUTPUT_TOO_LARGE|OutputTooLarge)\b/;
@@ -38,12 +41,25 @@ export const RUNTIME_SANDBOX_LOST_MESSAGE =
38
41
  'The execution sandbox became unreachable before returning a terminal result. Re-run the same command; if this keeps happening, contact Deepline support with the run ID.';
39
42
  export const RUNTIME_SANDBOX_OOM_MESSAGE =
40
43
  'The execution sandbox ran out of memory. Completed work is durably recorded. Deepline does not automatically retry this run because the same resource profile is unlikely to succeed. Reduce the batch size or row payload before starting a new run.';
44
+ /**
45
+ * Controlled counterpart to RUNTIME_SANDBOX_OOM. The external watchdog stops
46
+ * the runner while the sandbox is still under its cgroup limit, so the run
47
+ * reports a diagnosis (observed usage/limit plus the runtime's byte ledger)
48
+ * instead of vanishing into a kernel OOM kill. Same customer-safe shape and
49
+ * same non-retryable disposition as the OOM: the resource profile, not the
50
+ * attempt, is what has to change.
51
+ */
52
+ export const RUNTIME_SANDBOX_MEMORY_ABORT_CODE = 'RUNTIME_SANDBOX_MEMORY_ABORT';
53
+ export const RUNTIME_SANDBOX_MEMORY_ABORT_MESSAGE =
54
+ 'The execution sandbox reached its memory limit, so Deepline stopped this run before the sandbox was killed. Completed work is durably recorded. Deepline does not automatically retry this run because the same resource profile is unlikely to succeed. Reduce the batch size or row payload before starting a new run.';
41
55
  export const RUNTIME_SANDBOX_KILLED_MESSAGE =
42
56
  'The execution sandbox was killed before it could return a result, and the kill reason is unavailable. Completed work is durably recorded. Retry the same command safely; Deepline reuses completed steps and does not repeat their provider calls.';
43
57
  export const RUNTIME_LIMIT_EXCEEDED_MESSAGE =
44
- 'The play reached its 30 minute runtime limit and was stopped. Completed row state was preserved; run a smaller batch or continue from the persisted rows.';
58
+ 'The play reached its 30 minute runtime limit and was stopped before returning. Saved datasets remain available: inspect and export them before rerunning unfinished work. Use fewer rows or increase runtime.timeout in the Play; remove the override to use the 30-minute default. Compatible completed work can be reused; an in-flight response is not proof of a saved result.';
45
59
  export const SANDBOX_CAPACITY_EXCEEDED_MESSAGE =
46
60
  'Your organization has reached its concurrency limit, so this play did not start. Try again after another play finishes, or contact Deepline to request a higher limit.';
61
+ export const RUNTIME_CONTROL_PLANE_UNAVAILABLE_MESSAGE =
62
+ 'Deepline could not reach the runtime control plane long enough to safely continue this run, so it stopped the execution sandbox. Completed work is preserved. Inspect and export saved datasets before deciding whether to rerun unfinished work; in-flight provider calls are not automatically replayed.';
47
63
  export const RUNTIME_RUNNER_LOST_MESSAGE =
48
64
  'The execution runner stopped reporting liveness before returning a terminal result. Completed work is preserved, but Deepline did not automatically replay the run because provider side effects may already exist. Re-run only when it is safe to do so.';
49
65
  export const RUNTIME_SANDBOX_INSPECTION_UNAVAILABLE_MESSAGE =
@@ -326,7 +342,11 @@ export function runtimeLimitExceededCause(timeoutSeconds: number): string {
326
342
 
327
343
  export function runtimeLimitExceededMessage(timeoutSeconds: number): string {
328
344
  if (timeoutSeconds === 30 * 60) return RUNTIME_LIMIT_EXCEEDED_MESSAGE;
329
- return `The play reached its configured runtime limit of ${formatRuntimeLimitDuration(timeoutSeconds)} and was stopped. Completed row state was preserved; run a smaller batch or continue from the persisted rows.`;
345
+ const guidance =
346
+ timeoutSeconds >= 4 * 60 * 60
347
+ ? 'This Play already uses the maximum 4-hour runtime.timeout; split unfinished work into smaller batches.'
348
+ : 'Use fewer rows or increase runtime.timeout in the Play; remove the override to use the 30-minute default.';
349
+ return `The play reached its configured runtime limit of ${formatRuntimeLimitDuration(timeoutSeconds)} and was stopped before returning. Saved datasets remain available: inspect and export them before rerunning unfinished work. ${guidance} Compatible completed work can be reused; an in-flight response is not proof of a saved result.`;
330
350
  }
331
351
 
332
352
  export function runtimeLimitExceededFailure(
@@ -466,6 +486,20 @@ export function normalizePlayRunFailure(error: unknown): PlayRunFailureDetails {
466
486
  cause,
467
487
  };
468
488
  }
489
+ if (RUNTIME_CONTROL_PLANE_UNAVAILABLE_RE.test(rawCause)) {
490
+ const stack = error instanceof Error ? boundedFailureStack(error) : null;
491
+ const causes = error instanceof Error ? failureCauseTexts(error) : [];
492
+ return {
493
+ code: 'RUNTIME_CONTROL_PLANE_UNAVAILABLE',
494
+ phase: 'infrastructure',
495
+ message: RUNTIME_CONTROL_PLANE_UNAVAILABLE_MESSAGE,
496
+ retryable: false,
497
+ cause,
498
+ ...(error instanceof Error ? { name: error.name || 'Error' } : {}),
499
+ ...(stack ? { stack } : {}),
500
+ ...(causes.length > 0 ? { causes } : {}),
501
+ };
502
+ }
469
503
  if (RUNTIME_RUNNER_LOST_RE.test(rawCause)) {
470
504
  const stack = error instanceof Error ? boundedFailureStack(error) : null;
471
505
  const causes = error instanceof Error ? failureCauseTexts(error) : [];
@@ -519,6 +553,19 @@ export function normalizePlayRunFailure(error: unknown): PlayRunFailureDetails {
519
553
  cause,
520
554
  };
521
555
  }
556
+ // Ordered before the OOM branch: a controlled abort is the diagnosed case
557
+ // and must never collapse into the generic out-of-memory classification.
558
+ if (RUNTIME_SANDBOX_MEMORY_ABORT_RE.test(rawCause)) {
559
+ return {
560
+ code: RUNTIME_SANDBOX_MEMORY_ABORT_CODE,
561
+ phase: 'infrastructure',
562
+ message: RUNTIME_SANDBOX_MEMORY_ABORT_MESSAGE,
563
+ // The observed usage, limit, and byte ledger ride on `cause`, which is
564
+ // the failure packet's verbatim evidence channel.
565
+ retryable: false,
566
+ cause,
567
+ };
568
+ }
522
569
  if (RUNTIME_SANDBOX_OOM_RE.test(rawCause)) {
523
570
  return {
524
571
  code: 'RUNTIME_SANDBOX_OOM',
@@ -679,7 +679,12 @@ export function normalizePlayRunLedgerSnapshot(
679
679
  finiteNumber(value.totalLogCount) ?? logTail.length,
680
680
  logTail.length,
681
681
  ),
682
- logsTruncated: value.logsTruncated === true,
682
+ // A persisted snapshot carrying more lines than the tail keeps is
683
+ // truncated here, whatever the stored flag says. Reporting `false` while
684
+ // dropping lines is silent loss: a reader would take the tail for the
685
+ // whole log.
686
+ logsTruncated:
687
+ value.logsTruncated === true || logTail.length > LOG_TAIL_LIMIT,
683
688
  activeStepId: optionalNullableString(value.activeStepId),
684
689
  activeActivityId: optionalNullableString(value.activeActivityId),
685
690
  activeArtifactTableNamespace: optionalNullableString(
@@ -0,0 +1,55 @@
1
+ /** Limits apply before logs enter runner memory or a transport request. */
2
+ export const RUNNER_LOG_MAX_CHARS = 2_000;
3
+ export const RUNNER_LOG_MAX_BYTES = 5 * 1024 * 1024;
4
+ export const RUNNER_LOG_MAX_LINES = 25_000;
5
+ const TERMINAL_RESERVE_BYTES = 64 * 1024;
6
+ const TERMINAL_RESERVE_LINES = 32;
7
+ const encoder = new TextEncoder();
8
+
9
+ export function boundRunLogLine(line: string): string {
10
+ if (line.length <= RUNNER_LOG_MAX_CHARS) return line;
11
+ const suffix =
12
+ '… [log shortened; log identifiers/counts, or save a dataset and use runs export for full values]';
13
+ return line.slice(0, RUNNER_LOG_MAX_CHARS - suffix.length) + suffix;
14
+ }
15
+
16
+ /**
17
+ * Append-only so existing positional replay cursors remain valid. Ordinary
18
+ * logs share the durable stream's budget; a small reserve lets the runner
19
+ * explain a late timeout after a customer has exhausted that budget.
20
+ */
21
+ export class RunLogBuffer {
22
+ readonly lines: string[] = [];
23
+ private bytes = 0;
24
+ private omitted = 0;
25
+ private marked = false;
26
+
27
+ append(raw: string, terminal = false): string[] {
28
+ const line = boundRunLogLine(raw);
29
+ const bytes = encoder.encode(line).byteLength;
30
+ const byteLimit =
31
+ RUNNER_LOG_MAX_BYTES - (terminal ? 0 : TERMINAL_RESERVE_BYTES);
32
+ const lineLimit =
33
+ RUNNER_LOG_MAX_LINES - (terminal ? 0 : TERMINAL_RESERVE_LINES);
34
+ if (this.bytes + bytes > byteLimit || this.lines.length >= lineLimit) {
35
+ this.omitted++;
36
+ if (this.marked) return [];
37
+ this.marked = true;
38
+ return this.append(
39
+ '[warn] [logs limited: runner log budget reached; further lines may be omitted. Remove per-row logging; save rows in a dataset and use deepline runs export. Run status and saved datasets remain available.]',
40
+ true,
41
+ );
42
+ }
43
+ this.lines.push(line);
44
+ this.bytes += bytes;
45
+ return [line];
46
+ }
47
+
48
+ snapshot() {
49
+ return {
50
+ retainedBytes: this.bytes,
51
+ retainedLines: this.lines.length,
52
+ omittedLines: this.omitted,
53
+ };
54
+ }
55
+ }
@@ -0,0 +1,221 @@
1
+ import * as diagnosticsChannel from 'node:diagnostics_channel';
2
+
3
+ import {
4
+ EGRESS_BANDWIDTH_PROBE_BUDGET_MS,
5
+ formatEgressBandwidthLine,
6
+ probeEgressBandwidth,
7
+ } from '../egress-bandwidth-probe';
8
+
9
+ export type EgressProbeTarget = 'gateway' | 'relay';
10
+
11
+ export type EgressProbeObservation = {
12
+ target: EgressProbeTarget;
13
+ timingsMs: number[];
14
+ attempted: number;
15
+ errorClass: string | null;
16
+ connects: number | null;
17
+ requestCreates: number | null;
18
+ };
19
+
20
+ function median(values: readonly number[]): number | null {
21
+ if (values.length === 0) return null;
22
+ const sorted = [...values].sort((left, right) => left - right);
23
+ const middle = Math.floor(sorted.length / 2);
24
+ return sorted.length % 2 === 1
25
+ ? sorted[middle]!
26
+ : Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
27
+ }
28
+
29
+ export function egressProbeConnectionReuse(input: {
30
+ connects: number | null;
31
+ requestCreates: number | null;
32
+ attempted: number;
33
+ }): 'true' | 'false' | 'unknown' {
34
+ const { connects, requestCreates, attempted } = input;
35
+ if (connects === null || requestCreates === null) return 'unknown';
36
+ if (attempted <= 1 || requestCreates < attempted) return 'unknown';
37
+ return connects < attempted ? 'true' : 'false';
38
+ }
39
+
40
+ export function formatEgressProbeLine(
41
+ observation: EgressProbeObservation,
42
+ ): string {
43
+ const [first, ...rest] = observation.timingsMs;
44
+ const restP50 = median(rest);
45
+ const restMax = rest.length > 0 ? Math.max(...rest) : null;
46
+ const number = (value: number | null | undefined) =>
47
+ value === null || value === undefined ? 'none' : String(Math.round(value));
48
+ const reused = egressProbeConnectionReuse({
49
+ connects: observation.connects,
50
+ requestCreates: observation.requestCreates,
51
+ attempted: observation.attempted,
52
+ });
53
+ return (
54
+ `[perf] egress_probe target=${observation.target} ` +
55
+ `first_ms=${number(first)} rest_p50_ms=${number(restP50)} ` +
56
+ `rest_max_ms=${number(restMax)} reused_connection=${reused} ` +
57
+ `samples=${observation.timingsMs.length}/${observation.attempted}` +
58
+ (observation.errorClass ? ` error=${observation.errorClass}` : '')
59
+ );
60
+ }
61
+
62
+ export function formatEgressProbeRegionLine(
63
+ env: Record<string, string | undefined>,
64
+ ): string {
65
+ const hint = (name: string) => env[name]?.trim() || 'none';
66
+ return (
67
+ `[perf] egress_probe_region daytona_target=${hint('DAYTONA_TARGET')} ` +
68
+ `sandbox_target=${hint('DEEPLINE_SANDBOX_TARGET')} ` +
69
+ `fly_region=${hint('FLY_REGION')}`
70
+ );
71
+ }
72
+
73
+ export async function probeEgressTarget(input: {
74
+ target: EgressProbeTarget;
75
+ baseUrl: string;
76
+ fetchImpl: typeof fetch;
77
+ requests: number;
78
+ deadlineAtMs: number;
79
+ now?: () => number;
80
+ }): Promise<EgressProbeObservation> {
81
+ const now = input.now ?? (() => Date.now());
82
+ const url = `${input.baseUrl.replace(/\/$/, '')}/healthz`;
83
+ const timingsMs: number[] = [];
84
+ let attempted = 0;
85
+ let errorClass: string | null = null;
86
+ const counters = subscribeEgressProbeCounters();
87
+ try {
88
+ for (let index = 0; index < input.requests; index += 1) {
89
+ const remainingMs = input.deadlineAtMs - now();
90
+ if (remainingMs <= 0) break;
91
+ attempted += 1;
92
+ const startedAt = now();
93
+ try {
94
+ const response = await input.fetchImpl(url, {
95
+ method: 'GET',
96
+ redirect: 'manual',
97
+ signal: AbortSignal.timeout(remainingMs),
98
+ });
99
+ await response.text().catch(() => '');
100
+ timingsMs.push(now() - startedAt);
101
+ } catch (error) {
102
+ errorClass ??=
103
+ error instanceof Error ? error.constructor.name : typeof error;
104
+ }
105
+ }
106
+ } finally {
107
+ counters.stop();
108
+ }
109
+ return {
110
+ target: input.target,
111
+ timingsMs,
112
+ attempted,
113
+ errorClass,
114
+ connects: counters.connects,
115
+ requestCreates: counters.requestCreates,
116
+ };
117
+ }
118
+
119
+ function subscribeEgressProbeCounters(): {
120
+ readonly connects: number | null;
121
+ readonly requestCreates: number | null;
122
+ stop: () => void;
123
+ } {
124
+ let connects = 0;
125
+ let requestCreates = 0;
126
+ let live = false;
127
+ let unsubscribe = () => {};
128
+ try {
129
+ const onConnect = () => {
130
+ connects += 1;
131
+ };
132
+ const onCreate = () => {
133
+ requestCreates += 1;
134
+ };
135
+ diagnosticsChannel.subscribe('undici:client:connected', onConnect);
136
+ diagnosticsChannel.subscribe('undici:request:create', onCreate);
137
+ live = true;
138
+ unsubscribe = () => {
139
+ diagnosticsChannel.unsubscribe('undici:client:connected', onConnect);
140
+ diagnosticsChannel.unsubscribe('undici:request:create', onCreate);
141
+ };
142
+ } catch {
143
+ live = false;
144
+ }
145
+ return {
146
+ get connects() {
147
+ return live ? connects : null;
148
+ },
149
+ get requestCreates() {
150
+ return live ? requestCreates : null;
151
+ },
152
+ stop: () => {
153
+ try {
154
+ unsubscribe();
155
+ } catch {
156
+ // Diagnostics teardown must not affect a customer run.
157
+ }
158
+ },
159
+ };
160
+ }
161
+
162
+ export const EGRESS_PROBE_BUDGET_MS = 3_000;
163
+ const EGRESS_PROBE_REQUESTS_PER_TARGET = 6;
164
+
165
+ export async function collectStartupEgressProbeLines(input: {
166
+ receiptGatewayBaseUrl: string | null;
167
+ executionRelayBaseUrl: string | null;
168
+ /** Bearer credential for the gateway's authenticated bandwidth sink. */
169
+ executorToken?: string | null;
170
+ }): Promise<string[]> {
171
+ const gateway = input.receiptGatewayBaseUrl?.trim().replace(/\/$/, '') || '';
172
+ const relay = input.executionRelayBaseUrl?.trim().replace(/\/$/, '') || '';
173
+ const targets: Array<{ target: EgressProbeTarget; baseUrl: string }> = [];
174
+ if (gateway) targets.push({ target: 'gateway', baseUrl: gateway });
175
+ if (relay && relay !== gateway) {
176
+ targets.push({ target: 'relay', baseUrl: relay });
177
+ }
178
+ if (targets.length === 0) return [];
179
+ const lines: string[] = [formatEgressProbeRegionLine(process.env)];
180
+ const deadlineAtMs = Date.now() + EGRESS_PROBE_BUDGET_MS;
181
+ for (const target of targets) {
182
+ try {
183
+ const observation = await probeEgressTarget({
184
+ ...target,
185
+ fetchImpl: globalThis.fetch,
186
+ requests: EGRESS_PROBE_REQUESTS_PER_TARGET,
187
+ deadlineAtMs,
188
+ });
189
+ lines.push(formatEgressProbeLine(observation));
190
+ } catch (error) {
191
+ lines.push(
192
+ `[perf] egress_probe target=${target.target} failed error=${
193
+ error instanceof Error ? error.constructor.name : typeof error
194
+ }`,
195
+ );
196
+ }
197
+ }
198
+ const executorToken = input.executorToken?.trim() || null;
199
+ if (gateway && executorToken) {
200
+ try {
201
+ lines.push(
202
+ formatEgressBandwidthLine(
203
+ await probeEgressBandwidth({
204
+ target: 'gateway',
205
+ baseUrl: gateway,
206
+ fetchImpl: globalThis.fetch,
207
+ headers: { Authorization: `Bearer ${executorToken}` },
208
+ deadlineAtMs: Date.now() + EGRESS_BANDWIDTH_PROBE_BUDGET_MS,
209
+ }),
210
+ ),
211
+ );
212
+ } catch (error) {
213
+ lines.push(
214
+ `[perf] egress_bandwidth target=gateway failed error=${
215
+ error instanceof Error ? error.constructor.name : typeof error
216
+ }`,
217
+ );
218
+ }
219
+ }
220
+ return lines;
221
+ }