deepline 0.1.26 → 0.1.28

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.
@@ -106,7 +106,6 @@ import {
106
106
  // re-bundle harness internals into per-play. Keep that in mind.
107
107
  import {
108
108
  harnessPersistCompletedSheetRows,
109
- harnessPrewarmPostgresSessions,
110
109
  harnessReadSheetDatasetRows,
111
110
  harnessReadStagedFileChunk,
112
111
  harnessStartSheetDataset,
@@ -568,7 +567,11 @@ async function postRuntimeApi<T>(
568
567
  // hits the same handler with the same auth — only the transport changes.
569
568
  const serializedBody = JSON.stringify(body);
570
569
  let lastError: unknown = null;
571
- for (let attempt = 0; attempt <= RUNTIME_API_RETRY_DELAYS_MS.length; attempt += 1) {
570
+ for (
571
+ let attempt = 0;
572
+ attempt <= RUNTIME_API_RETRY_DELAYS_MS.length;
573
+ attempt += 1
574
+ ) {
572
575
  let res: Response;
573
576
  try {
574
577
  res = await fetchRuntimeApi(baseUrl, '/api/v2/plays/internal/runtime', {
@@ -618,7 +621,13 @@ function isRetryableRuntimeApiError(error: unknown): boolean {
618
621
  }
619
622
 
620
623
  function isRetryableRuntimeApiResponse(status: number, body: string): boolean {
621
- if (status === 408 || status === 429 || status === 502 || status === 503 || status === 504) {
624
+ if (
625
+ status === 408 ||
626
+ status === 429 ||
627
+ status === 502 ||
628
+ status === 503 ||
629
+ status === 504
630
+ ) {
622
631
  return true;
623
632
  }
624
633
  return (
@@ -2465,7 +2474,10 @@ function readHarnessStagedFileChunks(input: {
2465
2474
  }
2466
2475
 
2467
2476
  const requiredBytes = expectedBytes ?? objectSize;
2468
- if (typeof requiredBytes === 'number' && observedBytes !== requiredBytes) {
2477
+ if (
2478
+ typeof requiredBytes === 'number' &&
2479
+ observedBytes !== requiredBytes
2480
+ ) {
2469
2481
  recordRunnerPerfTrace({
2470
2482
  req: input.req,
2471
2483
  phase: 'csv.read_mismatch',
@@ -2761,6 +2773,28 @@ async function prepareMapRows(input: {
2761
2773
  userEmail: input.req.userEmail,
2762
2774
  preloadedDbSessions: input.req.preloadedDbSessions ?? null,
2763
2775
  });
2776
+ for (const timing of result.timings ?? []) {
2777
+ const phase =
2778
+ typeof timing.phase === 'string' && timing.phase.trim()
2779
+ ? timing.phase.trim()
2780
+ : 'unknown';
2781
+ const ms =
2782
+ typeof timing.ms === 'number' && Number.isFinite(timing.ms)
2783
+ ? timing.ms
2784
+ : 0;
2785
+ const { phase: _phase, ms: _ms, ...extra } = timing;
2786
+ void _phase;
2787
+ void _ms;
2788
+ recordRunnerPerfTrace({
2789
+ req: input.req,
2790
+ phase: `sheet_start.${phase}`,
2791
+ ms,
2792
+ extra: {
2793
+ tableNamespace: input.tableNamespace,
2794
+ ...extra,
2795
+ },
2796
+ });
2797
+ }
2764
2798
  return {
2765
2799
  inserted: result.inserted,
2766
2800
  skipped: result.skipped,
@@ -3717,7 +3751,8 @@ function createMinimalWorkerCtx(
3717
3751
  if (matchByPath) {
3718
3752
  file = {
3719
3753
  logicalPath: matchByPath.playPath,
3720
- fileName: matchByPath.playPath.split('/').pop() ?? matchByPath.playPath,
3754
+ fileName:
3755
+ matchByPath.playPath.split('/').pop() ?? matchByPath.playPath,
3721
3756
  storageKey: matchByPath.storageKey,
3722
3757
  contentType: matchByPath.contentType,
3723
3758
  bytes: matchByPath.bytes,
@@ -4245,19 +4280,6 @@ async function executeRunRequest(
4245
4280
  });
4246
4281
  const abortController = options?.abortController ?? new AbortController();
4247
4282
  const abortSignal = abortController.signal;
4248
- const postgresPrewarmStartedAt = nowMs();
4249
- await harnessPrewarmPostgresSessions({
4250
- executorToken: req.executorToken,
4251
- sessions: req.preloadedDbSessions ?? [],
4252
- });
4253
- recordRunnerPerfTrace({
4254
- req,
4255
- phase: 'runner.prewarm_postgres',
4256
- ms: nowMs() - postgresPrewarmStartedAt,
4257
- extra: {
4258
- sessions: req.preloadedDbSessions?.length ?? 0,
4259
- },
4260
- });
4261
4283
  let runLogBuffer: string[] = [];
4262
4284
  let pendingRunLogLines: string[] = [];
4263
4285
  let stepProgressByNodeId: LiveNodeProgressMap = {};
@@ -4427,6 +4449,11 @@ async function executeRunRequest(
4427
4449
  if (!options?.persistResultDatasets) return;
4428
4450
  await ledgerFlushInFlight.catch(() => undefined);
4429
4451
  const now = nowMs();
4452
+ pendingRunLogLines = runLogBuffer;
4453
+ dirtyProgressNodeIds = new Set([
4454
+ ...dirtyProgressNodeIds,
4455
+ ...Object.keys(stepProgressByNodeId),
4456
+ ]);
4430
4457
  pendingLedgerEvents = [...pendingLedgerEvents, terminalEvent];
4431
4458
  const events = drainPendingLedgerEvents(now);
4432
4459
  if (events.length === 0) return;
@@ -4510,12 +4537,12 @@ async function executeRunRequest(
4510
4537
  ms: nowMs() - serializeStartedAt,
4511
4538
  });
4512
4539
  if (options?.persistResultDatasets) {
4513
- const persistStartedAt = nowMs();
4540
+ const ledgerFlushWaitStartedAt = nowMs();
4514
4541
  await ledgerFlushInFlight.catch(() => undefined);
4515
4542
  recordRunnerPerfTrace({
4516
4543
  req,
4517
4544
  phase: 'runner.run_ledger_flush_wait',
4518
- ms: nowMs() - persistStartedAt,
4545
+ ms: nowMs() - ledgerFlushWaitStartedAt,
4519
4546
  });
4520
4547
  const resultDatasetStartedAt = nowMs();
4521
4548
  await persistResultDatasets(req, result, serializedResult);
@@ -4526,31 +4553,58 @@ async function executeRunRequest(
4526
4553
  });
4527
4554
  const terminalResult = trimResultForStatus(serializedResult);
4528
4555
  const terminalOccurredAt = nowMs();
4556
+ const terminalLedgerPromise = (async () => {
4557
+ const terminalUpdateStartedAt = nowMs();
4558
+ await flushTerminalLedgerEvents({
4559
+ type: 'run.completed',
4560
+ runId: req.runId,
4561
+ source: 'worker',
4562
+ occurredAt: terminalOccurredAt,
4563
+ result: terminalResult,
4564
+ });
4565
+ recordRunnerPerfTrace({
4566
+ req,
4567
+ phase: 'runner.terminal_ledger_append',
4568
+ ms: nowMs() - terminalUpdateStartedAt,
4569
+ });
4570
+ })().catch((error) => {
4571
+ console.error(
4572
+ `[play-harness] non-fatal terminal ledger append failed runId=${req.runId}: ${
4573
+ error instanceof Error ? error.message : String(error)
4574
+ }`,
4575
+ );
4576
+ });
4577
+
4578
+ await terminalLedgerPromise;
4579
+
4529
4580
  const billingStartedAt = nowMs();
4530
- await finalizeWorkerComputeBilling({
4581
+ const billingPromise = finalizeWorkerComputeBilling({
4531
4582
  req,
4532
4583
  success: true,
4533
4584
  actionEstimate: 4,
4585
+ }).then(() => {
4586
+ recordRunnerPerfTrace({
4587
+ req,
4588
+ phase: 'runner.compute_billing_finalize',
4589
+ ms: nowMs() - billingStartedAt,
4590
+ });
4534
4591
  });
4535
- recordRunnerPerfTrace({
4536
- req,
4537
- phase: 'runner.compute_billing_finalize',
4538
- ms: nowMs() - billingStartedAt,
4539
- });
4540
-
4541
- const terminalUpdateStartedAt = nowMs();
4542
- await flushTerminalLedgerEvents({
4543
- type: 'run.completed',
4544
- runId: req.runId,
4545
- source: 'worker',
4546
- occurredAt: terminalOccurredAt,
4547
- result: terminalResult,
4548
- });
4549
- recordRunnerPerfTrace({
4550
- req,
4551
- phase: 'runner.terminal_ledger_append',
4552
- ms: nowMs() - terminalUpdateStartedAt,
4553
- });
4592
+ if (extractMaxCreditsPerRun(req.contractSnapshot) !== null) {
4593
+ await billingPromise;
4594
+ } else {
4595
+ const nonBlockingBillingPromise = billingPromise.catch((error) => {
4596
+ console.error(
4597
+ `[play-harness] non-fatal compute billing finalize failed runId=${req.runId}: ${
4598
+ error instanceof Error ? error.message : String(error)
4599
+ }`,
4600
+ );
4601
+ });
4602
+ if (options?.waitUntil) {
4603
+ options.waitUntil(nonBlockingBillingPromise);
4604
+ } else {
4605
+ await nonBlockingBillingPromise;
4606
+ }
4607
+ }
4554
4608
  }
4555
4609
  const parentSignalStartedAt = nowMs();
4556
4610
  await signalParentPlayTerminal({
@@ -4713,7 +4767,9 @@ function runRequestFromWorkflowParams(
4713
4767
  inputFile && inputStorageKey
4714
4768
  ? {
4715
4769
  [fileName]: {
4716
- logicalPath: String(inputFile.logicalPath ?? inputFile.path ?? fileName),
4770
+ logicalPath: String(
4771
+ inputFile.logicalPath ?? inputFile.path ?? fileName,
4772
+ ),
4717
4773
  fileName,
4718
4774
  storageKey: inputStorageKey,
4719
4775
  contentType:
@@ -5255,7 +5311,10 @@ function inferOutputRows(result: unknown): number {
5255
5311
  }
5256
5312
  if (typeof value !== 'object') return;
5257
5313
  const record = value as Record<string, unknown>;
5258
- if (typeof record.tableNamespace === 'string' && typeof record.count === 'number') {
5314
+ if (
5315
+ typeof record.tableNamespace === 'string' &&
5316
+ typeof record.count === 'number'
5317
+ ) {
5259
5318
  datasets.push(record.count);
5260
5319
  }
5261
5320
  for (const [key, child] of Object.entries(record)) {
@@ -8,13 +8,11 @@
8
8
  *
9
9
  * What it does:
10
10
  * - Exposes thin functions that look like the in-bundle implementations
11
- * they're replacing (validate, runtime-api call, …).
11
+ * they're replacing (runtime-api call, staged dataset IO, …).
12
12
  * - Each function calls `env.HARNESS.<method>(...)` — the typed RPC
13
13
  * stub provided by the Cloudflare service binding.
14
14
  *
15
15
  * What it does NOT do:
16
- * - Does NOT import zod (that's the whole point — zod stays in the
17
- * harness Worker).
18
16
  * - Does NOT import the harness Worker's `PlayHarness` class (that
19
17
  * would re-bundle the whole harness; only TYPES are imported).
20
18
  * - Does NOT cache RPC results across plays — caching belongs to
@@ -36,12 +34,10 @@ import type {
36
34
  PreloadedRuntimeDbSessionInput,
37
35
  RuntimeApiCallInput,
38
36
  RuntimeApiCallResult,
39
- RuntimePayloadSchemaId,
40
37
  SheetDatasetRowsInput,
41
38
  SheetDatasetRowsResult,
42
39
  StagedFileChunkInput,
43
40
  StagedFileChunkResult,
44
- ValidatePayloadResult,
45
41
  } from '../../../apps/play-harness-worker/src/rpc-types';
46
42
 
47
43
  /**
@@ -173,6 +169,7 @@ export async function harnessStartSheetDataset(input: {
173
169
  pendingRows: Array<Record<string, unknown>>;
174
170
  completedRows: Array<Record<string, unknown>>;
175
171
  tableNamespace: string;
172
+ timings?: Array<Record<string, unknown>>;
176
173
  }> {
177
174
  return requireBinding().startSheetDataset(input);
178
175
  }
@@ -195,18 +192,3 @@ export async function harnessPersistCompletedSheetRows(input: {
195
192
  }): Promise<{ ok: true; rowsWritten: number; tableNamespace: string }> {
196
193
  return requireBinding().persistCompletedMapRows(input);
197
194
  }
198
-
199
- /**
200
- * Validate a payload against a named schema. The schema definitions
201
- * (and zod itself) live in the harness Worker — see
202
- * `apps/play-harness-worker/src/leaves/validate.ts → KNOWN_SCHEMAS`.
203
- *
204
- * Use a schema id of the form `tool:<provider>:<operation>` for tool
205
- * inputs, or `runtime-api:<action>` for runtime-api request bodies.
206
- */
207
- export async function harnessValidatePayload(
208
- schemaId: RuntimePayloadSchemaId,
209
- payload: unknown,
210
- ): Promise<ValidatePayloadResult> {
211
- return requireBinding().validatePayload({ schemaId, payload });
212
- }
@@ -1,2 +1,2 @@
1
- export const SDK_VERSION = "0.1.26";
1
+ export const SDK_VERSION = "0.1.28";
2
2
  export const SDK_API_CONTRACT = "2026-05-runs-v2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {