deepline 0.1.226 → 0.1.227

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.
@@ -2539,6 +2539,7 @@ type WorkerStepProgram = {
2539
2539
 
2540
2540
  type WorkerMapOptions = {
2541
2541
  description?: string;
2542
+ mode?: 'upsert' | 'net_new';
2542
2543
  concurrency?: number;
2543
2544
  key?:
2544
2545
  | string
@@ -4870,9 +4871,18 @@ function createMinimalWorkerCtx(
4870
4871
  runId: req.runId,
4871
4872
  inputOffset: 0,
4872
4873
  attemptLeaseTtlMs: runtimeSheetAttemptLeaseTtlMs(req),
4874
+ ...(opts?.mode ? { mode: opts.mode } : {}),
4873
4875
  ...mapWriteAttempt,
4874
4876
  });
4875
4877
  const mapWriteVersion = mapReservation?.writeVersion ?? null;
4878
+ const netNewRowKeys =
4879
+ opts?.mode === 'net_new'
4880
+ ? new Set(
4881
+ (mapReservation?.pendingRows ?? [])
4882
+ .map((row) => resolveMapRowOutcomeKey(row))
4883
+ .filter((key): key is string => Boolean(key)),
4884
+ )
4885
+ : null;
4876
4886
 
4877
4887
  let totalRowsWritten = 0;
4878
4888
  let datasetLifecycleRegistered = false;
@@ -4896,7 +4906,7 @@ function createMinimalWorkerCtx(
4896
4906
  chunkIndex,
4897
4907
  });
4898
4908
  const keyStartedAt = nowMs();
4899
- const chunkEntries = chunkRows.map((row, localIndex) => {
4909
+ let chunkEntries = chunkRows.map((row, localIndex) => {
4900
4910
  const absoluteIndex = baseOffset + chunkStart + localIndex;
4901
4911
  const rowKey = resolveRowKey(
4902
4912
  row,
@@ -4906,6 +4916,11 @@ function createMinimalWorkerCtx(
4906
4916
  );
4907
4917
  return { row, absoluteIndex, rowKey };
4908
4918
  });
4919
+ if (netNewRowKeys) {
4920
+ chunkEntries = chunkEntries.filter(({ rowKey }) =>
4921
+ netNewRowKeys.has(rowKey),
4922
+ );
4923
+ }
4909
4924
  recordRunnerPerfTrace({
4910
4925
  req,
4911
4926
  phase: 'runner.map_chunk.keys',
@@ -4968,7 +4983,10 @@ function createMinimalWorkerCtx(
4968
4983
  },
4969
4984
  });
4970
4985
  mapLatencyProfile.recordPhase('prepare_rows', nowMs() - prepareStartedAt);
4971
- const progressTotalRows = rowCountHint ?? chunkRows.length;
4986
+ // net_new reserves the entire source before chunk execution, so its
4987
+ // progress describes the admitted subset rather than the source CSV.
4988
+ const progressTotalRows =
4989
+ netNewRowKeys?.size ?? rowCountHint ?? chunkRows.length;
4972
4990
  const preparedCompletedRows = Math.min(
4973
4991
  progressTotalRows,
4974
4992
  totalRowsWritten,
@@ -5023,15 +5041,8 @@ function createMinimalWorkerCtx(
5023
5041
  ({ rowKey }) => !preparedKeys.has(rowKey),
5024
5042
  );
5025
5043
  if (missingPreparedRows.length > 0) {
5026
- const rowKeySamples = missingPreparedRows
5027
- .map(({ rowKey }) => rowKey)
5028
- .slice(0, MAP_ROW_FAILURE_SAMPLE_LIMIT);
5029
5044
  throw new Error(
5030
- `ctx.dataset("${name}") runtime preparation omitted ${missingPreparedRows.length} row disposition(s). ` +
5031
- `Every input row must be claimed, completed, or blocked before execution starts.` +
5032
- (rowKeySamples.length > 0
5033
- ? ` Missing row keys: ${rowKeySamples.join(', ')}`
5034
- : ''),
5045
+ `ctx.dataset("${name}") preparation omitted ${missingPreparedRows.length} row disposition(s).`,
5035
5046
  );
5036
5047
  }
5037
5048
  const rowsToExecuteEntries = chunkEntries.filter(({ rowKey }) =>
@@ -5986,9 +5997,8 @@ function createMinimalWorkerCtx(
5986
5997
  // dataset (their recoverable state lives in the runtime sheet).
5987
5998
  if (key && executedRow) resultByKey.set(key, executedRow);
5988
5999
  }
5989
- const outEntries = chunkRows
5990
- .map((_row, index) => {
5991
- const entry = chunkEntries[index]!;
6000
+ const outEntries = chunkEntries
6001
+ .map((entry) => {
5992
6002
  const row = resultByKey.get(entry.rowKey);
5993
6003
  return row
5994
6004
  ? {
@@ -6531,11 +6541,11 @@ function createMinimalWorkerCtx(
6531
6541
  }
6532
6542
  enqueueMapProgressUpdate({
6533
6543
  completed: totalRowsWritten,
6534
- total: rowCountHint ?? undefined,
6544
+ total: netNewRowKeys?.size ?? rowCountHint ?? undefined,
6535
6545
  ...(totalRowsFailed > 0 ? { failed: totalRowsFailed } : {}),
6536
6546
  message: formatMapProgressMessage(
6537
6547
  totalRowsWritten,
6538
- rowCountHint ?? undefined,
6548
+ netNewRowKeys?.size ?? rowCountHint ?? undefined,
6539
6549
  ),
6540
6550
  });
6541
6551
  if (previewRows.length < WORKER_DATASET_PREVIEW_ROWS) {
@@ -560,6 +560,11 @@ export type DatasetBuilder<
560
560
  */
561
561
  run(options?: {
562
562
  description?: string;
563
+ /**
564
+ * `upsert` (default) returns every input row, reusing persisted work for
565
+ * existing keys. `net_new` atomically inserts and returns only unseen keys.
566
+ */
567
+ mode?: 'upsert' | 'net_new';
563
568
  key?:
564
569
  | (keyof InputRow & string)
565
570
  | readonly (keyof InputRow & string)[]
@@ -108,10 +108,10 @@ export const SDK_RELEASE = {
108
108
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
109
109
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
110
110
  // automatically without blocking their current command.
111
- version: '0.1.226',
111
+ version: '0.1.227',
112
112
  apiContract: '2026-06-dataset-handle-results-hard-cutover',
113
113
  supportPolicy: {
114
- latest: '0.1.226',
114
+ latest: '0.1.227',
115
115
  minimumSupported: '0.1.53',
116
116
  deprecatedBelow: '0.1.219',
117
117
  commandMinimumSupported: [
@@ -3943,7 +3943,10 @@ export class PlayContextImpl {
3943
3943
  this.#options.runId,
3944
3944
  );
3945
3945
  if (shouldStreamRuntimeBackedDataset) {
3946
- totalInputCount = await items.count();
3946
+ // Net-new admission happens one source page at a time. Its progress and
3947
+ // returned dataset describe admitted rows, not every provider/CSV row
3948
+ // supplied to this run, so accumulate the admitted count below.
3949
+ totalInputCount = options?.mode === 'net_new' ? 0 : await items.count();
3947
3950
  const mapScope = this.createMapExecutionScope({
3948
3951
  logicalNamespace: normalizedMapNamespace,
3949
3952
  artifactTableNamespace: resolvedTableNamespace,
@@ -4097,6 +4100,7 @@ export class PlayContextImpl {
4097
4100
  staticPipeline: this.currentStaticPipeline,
4098
4101
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4099
4102
  inputOffset: page.offset,
4103
+ mode: options?.mode,
4100
4104
  },
4101
4105
  );
4102
4106
  resolvedTableNamespace = normalizeTableNamespace(
@@ -4140,12 +4144,21 @@ export class PlayContextImpl {
4140
4144
  index: originalIndex,
4141
4145
  };
4142
4146
  });
4147
+ const admittedSeededItems =
4148
+ options?.mode === 'net_new'
4149
+ ? seededItems.filter(({ row, index }) =>
4150
+ pendingRowsByKey.has(seedRowIdentity(row, index)),
4151
+ )
4152
+ : seededItems;
4153
+ if (options?.mode === 'net_new') {
4154
+ totalInputCount += admittedSeededItems.length;
4155
+ }
4143
4156
 
4144
4157
  const rowsToExecuteByKey = new Map<
4145
4158
  string,
4146
4159
  { row: Record<string, unknown>; originalIndex: number }
4147
4160
  >();
4148
- for (const { row, index } of seededItems) {
4161
+ for (const { row, index } of admittedSeededItems) {
4149
4162
  if (explicitKeyResolver) {
4150
4163
  const explicitKey = explicitKeyResolver(row, index);
4151
4164
  if (seenExplicitKeys.has(explicitKey)) {
@@ -4280,6 +4293,45 @@ export class PlayContextImpl {
4280
4293
  page.rows.length = 0;
4281
4294
  }
4282
4295
 
4296
+ // Isolated failures are a partial result only when at least one admitted
4297
+ // row completed. A fully failed admission run is systemic: preserve the
4298
+ // failed sheet rows for recovery, but fail the enclosing play loudly.
4299
+ if (totalInputCount > 0 && failedCount === totalInputCount) {
4300
+ const completedRows = 0;
4301
+ this.setMapFrame({
4302
+ mapInvocationId: mapScope.mapInvocationId,
4303
+ mapNodeId: mapScope.mapNodeId ?? null,
4304
+ logicalNamespace: mapScope.logicalNamespace,
4305
+ artifactTableNamespace: resolvedTableNamespace,
4306
+ status: 'failed',
4307
+ totalRows: totalInputCount,
4308
+ completedRowKeys: [],
4309
+ pendingRowKeys: [],
4310
+ completedRowsCount: completedRows,
4311
+ pendingRowsCount: 0,
4312
+ failedRowsCount: failedCount,
4313
+ startedAt:
4314
+ this.checkpoint.mapFrames?.[mapScope.mapInvocationId]?.startedAt ??
4315
+ Date.now(),
4316
+ updatedAt: Date.now(),
4317
+ });
4318
+ this.emitExecutionEvent({
4319
+ type: 'map.failed',
4320
+ mapInvocationId: mapScope.mapInvocationId,
4321
+ mapNodeId: mapScope.mapNodeId ?? null,
4322
+ logicalNamespace: mapScope.logicalNamespace,
4323
+ artifactTableNamespace: resolvedTableNamespace,
4324
+ completedRows,
4325
+ failedRows: failedCount,
4326
+ totalRows: totalInputCount,
4327
+ at: Date.now(),
4328
+ });
4329
+ throw new Error(
4330
+ `ctx.dataset("${normalizedMapNamespace}") failed for every admitted row (${failedCount}/${totalInputCount}). ` +
4331
+ 'Failed rows were persisted and will be retried on the next run.',
4332
+ );
4333
+ }
4334
+
4283
4335
  this.setMapFrame({
4284
4336
  mapInvocationId: mapScope.mapInvocationId,
4285
4337
  mapNodeId: mapScope.mapNodeId ?? null,
@@ -4531,6 +4583,7 @@ export class PlayContextImpl {
4531
4583
  executorToken: this.#options.executorToken,
4532
4584
  staticPipeline: this.currentStaticPipeline,
4533
4585
  forceRefresh: this.#options.cachePolicy?.forceToolRefresh === true,
4586
+ mode: options?.mode,
4534
4587
  },
4535
4588
  );
4536
4589
  resolvedTableNamespace = normalizeTableNamespace(
@@ -4587,8 +4640,16 @@ export class PlayContextImpl {
4587
4640
  index,
4588
4641
  };
4589
4642
  });
4590
- itemsToProcess = seededItems.map((item) => item.row);
4591
- itemOriginalIndexes = seededItems.map((item) => item.index);
4643
+ const admittedItems =
4644
+ options?.mode === 'net_new'
4645
+ ? seededItems.filter(({ row, index }) =>
4646
+ pendingRowsByKey.has(seedRowIdentity(row, index)),
4647
+ )
4648
+ : seededItems;
4649
+ rawItems = admittedItems.map((item) => item.row);
4650
+ itemsToProcess = admittedItems.map((item) => item.row);
4651
+ itemOriginalIndexes = admittedItems.map((item) => item.index);
4652
+ totalInputCount = admittedItems.length;
4592
4653
  }
4593
4654
 
4594
4655
  const mapScope = this.createMapExecutionScope({
@@ -248,6 +248,16 @@ export interface DatasetOptions<TItem = Record<string, unknown>> {
248
248
  * and are reused on re-run.
249
249
  */
250
250
  onRowError?: 'isolate' | 'fail';
251
+ /**
252
+ * Controls how a persisted dataset admits rows whose key already exists.
253
+ *
254
+ * `upsert` (the default) preserves the row-preserving enrichment contract:
255
+ * existing rows are reused and returned with the current input. `net_new`
256
+ * atomically inserts only previously unseen keys and returns/processes only
257
+ * those inserted rows. Use `net_new` for a durable sourcing table, not for
258
+ * ordinary CSV enrichment reruns.
259
+ */
260
+ mode?: 'upsert' | 'net_new';
251
261
  }
252
262
 
253
263
  export type DatasetDefinitionOptions<TItem = Record<string, unknown>> = Omit<
@@ -257,7 +267,7 @@ export type DatasetDefinitionOptions<TItem = Record<string, unknown>> = Omit<
257
267
 
258
268
  export type DatasetRunOptions<TItem = Record<string, unknown>> = Pick<
259
269
  DatasetOptions<TItem>,
260
- 'description' | 'key' | 'onRowError'
270
+ 'description' | 'key' | 'onRowError' | 'mode'
261
271
  >;
262
272
 
263
273
  export interface ToolCallOptions {
@@ -597,6 +607,7 @@ export interface ContextOptions {
597
607
  staticPipeline?: PlayStaticPipeline | null;
598
608
  forceRefresh?: boolean;
599
609
  inputOffset?: number;
610
+ mode?: DatasetRunOptions['mode'];
600
611
  },
601
612
  ) => Promise<MapStartResult>;
602
613
  /**
@@ -273,6 +273,8 @@ type RuntimeSheetPreparedRowDisposition = {
273
273
  key: string;
274
274
  disposition: RuntimeSheetPrepareDisposition;
275
275
  };
276
+
277
+ type RuntimeSheetDatasetMode = 'upsert' | 'net_new';
276
278
  const dbSessionCache = new Map<string, DbSessionCacheEntry>();
277
279
  const dbSessionInFlight = new Map<string, Promise<CreateDbSessionResponse>>();
278
280
  const postgresPools = new Map<string, RuntimePool>();
@@ -2976,6 +2978,7 @@ async function prepareRuntimeSheetDatasetRows(
2976
2978
  physicalUpsertSetSql: string;
2977
2979
  outputPhysicalColumns: PhysicalSheetColumnProjection[];
2978
2980
  force?: boolean;
2981
+ mode?: RuntimeSheetDatasetMode;
2979
2982
  },
2980
2983
  ): Promise<{
2981
2984
  inserted: number;
@@ -3378,6 +3381,121 @@ async function prepareRuntimeSheetDatasetRows(
3378
3381
  return { inserted, rowDispositions };
3379
3382
  }
3380
3383
 
3384
+ /**
3385
+ * Atomically admit source-table keys that have not completed. A completed row
3386
+ * is consumed forever; failed rows and rows owned by this logical run remain
3387
+ * recoverable. This is deliberately a separate insert-only path: deriving
3388
+ * "new" from a read before the normal insert would race a concurrent sourcing
3389
+ * run.
3390
+ */
3391
+ async function prepareNetNewRuntimeSheetDatasetRows(
3392
+ client: RuntimeQueryClient,
3393
+ session: RuntimePostgresSession,
3394
+ input: {
3395
+ chunks: RuntimeDatasetRowEntry[][];
3396
+ runId: string;
3397
+ attemptId: string;
3398
+ attemptOwnerRunId: string;
3399
+ attemptExpiresAt: string;
3400
+ attemptSeq: number;
3401
+ writeVersion: number;
3402
+ physicalInsertColumnsSql: string;
3403
+ physicalInsertValuesSql: string;
3404
+ },
3405
+ ): Promise<{
3406
+ inserted: number;
3407
+ rowDispositions: RuntimeSheetPreparedRowDisposition[];
3408
+ }> {
3409
+ let inserted = 0;
3410
+ const rowDispositions: RuntimeSheetPreparedRowDisposition[] = [];
3411
+ for (const chunk of input.chunks) {
3412
+ const { rows } = await client.query<{
3413
+ inserted_count: number;
3414
+ row_dispositions: RuntimeSheetPreparedRowDisposition[];
3415
+ }>(
3416
+ `WITH input_rows AS (
3417
+ SELECT DISTINCT ON (key_values._key)
3418
+ key_values._key, payload_values.payload, index_values._input_index
3419
+ FROM unnest($1::text[]) WITH ORDINALITY AS key_values(_key, ord)
3420
+ JOIN unnest($2::jsonb[]) WITH ORDINALITY AS payload_values(payload, ord)
3421
+ ON payload_values.ord = key_values.ord
3422
+ JOIN unnest($3::bigint[]) WITH ORDINALITY AS index_values(_input_index, ord)
3423
+ ON index_values.ord = key_values.ord
3424
+ ORDER BY key_values._key, key_values.ord
3425
+ ),
3426
+ reclaimed_rows AS (
3427
+ UPDATE ${sheetTable(session)} AS target
3428
+ SET _status = 'pending',
3429
+ _run_id = $4::text,
3430
+ _input_index = input_rows._input_index,
3431
+ _attempt_id = $5::text,
3432
+ _attempt_owner_run_id = $6::text,
3433
+ _attempt_expires_at = $7::timestamptz,
3434
+ _attempt_seq = $8::integer,
3435
+ _write_version = $9::bigint,
3436
+ _writer_run_id = $4::text,
3437
+ _error = NULL,
3438
+ _updated_at = now(),
3439
+ _version = ${nextRuntimeSheetVersionExpression(session)}
3440
+ FROM input_rows
3441
+ WHERE target._key = input_rows._key
3442
+ AND (
3443
+ target._status = 'failed'
3444
+ OR (
3445
+ target._status IN ('pending', 'running')
3446
+ AND coalesce(target._attempt_owner_run_id, target._run_id) = $6::text
3447
+ )
3448
+ )
3449
+ RETURNING target._key
3450
+ ),
3451
+ inserted_rows AS (
3452
+ INSERT INTO ${sheetTable(session)}
3453
+ (_key, _status, _run_id, _input_index, _attempt_id, _attempt_owner_run_id,
3454
+ _attempt_expires_at, _attempt_seq, _write_version, _writer_run_id${input.physicalInsertColumnsSql})
3455
+ SELECT _key, 'pending', $4::text, _input_index, $5::text, $6::text,
3456
+ $7::timestamptz, $8::integer, $9::bigint, $4::text${input.physicalInsertValuesSql}
3457
+ FROM input_rows
3458
+ ON CONFLICT (_key) DO NOTHING
3459
+ RETURNING _key
3460
+ ),
3461
+ pending_rows AS (
3462
+ SELECT _key FROM inserted_rows
3463
+ UNION
3464
+ SELECT _key FROM reclaimed_rows
3465
+ )
3466
+ SELECT
3467
+ (SELECT count(*)::int FROM inserted_rows) AS inserted_count,
3468
+ coalesce(
3469
+ (SELECT jsonb_agg(jsonb_build_object('key', _key, 'disposition', 'pending')) FROM pending_rows),
3470
+ '[]'::jsonb
3471
+ ) AS row_dispositions`,
3472
+ [
3473
+ chunk.map((entry) => entry.key),
3474
+ chunk.map((entry) => stringifyPostgresJson(entry.row)),
3475
+ chunk.map((entry) => entry.inputIndex),
3476
+ input.runId,
3477
+ input.attemptId,
3478
+ input.attemptOwnerRunId,
3479
+ input.attemptExpiresAt,
3480
+ input.attemptSeq,
3481
+ input.writeVersion,
3482
+ ],
3483
+ );
3484
+ inserted += Number(rows[0]?.inserted_count ?? 0);
3485
+ if (Array.isArray(rows[0]?.row_dispositions)) {
3486
+ rowDispositions.push(
3487
+ ...rows[0].row_dispositions.filter(
3488
+ (row): row is RuntimeSheetPreparedRowDisposition =>
3489
+ row != null &&
3490
+ typeof row.key === 'string' &&
3491
+ row.disposition === 'pending',
3492
+ ),
3493
+ );
3494
+ }
3495
+ }
3496
+ return { inserted, rowDispositions };
3497
+ }
3498
+
3381
3499
  async function buildRuntimeSheetDatasetStartResult(
3382
3500
  client: RuntimeQueryClient,
3383
3501
  session: RuntimePostgresSession,
@@ -5561,6 +5679,7 @@ export async function startRuntimeSheetDataset(
5561
5679
  writeVersion?: number | null;
5562
5680
  inputOffset?: number;
5563
5681
  force?: boolean;
5682
+ mode?: RuntimeSheetDatasetMode;
5564
5683
  },
5565
5684
  ): Promise<PrepareRuntimeSheetResult> {
5566
5685
  if (context.dbSessionStrategy === 'gateway_only') {
@@ -5737,24 +5856,37 @@ export async function startRuntimeSheetDataset(
5737
5856
  rows: rowEntries.length,
5738
5857
  });
5739
5858
  const prepareStartedAt = Date.now();
5740
- const prepared = await prepareRuntimeSheetDatasetRows(client, session, {
5741
- chunks,
5742
- runId: input.runId,
5743
- normalizedPlayName,
5744
- normalizedTableNamespace,
5745
- attemptId,
5746
- attemptOwnerRunId,
5747
- attemptExpiresAt,
5748
- attemptSeq,
5749
- writeVersion,
5750
- physicalInsertColumnsSql,
5751
- physicalInsertValuesSql,
5752
- physicalRefreshSetSql:
5753
- input.force === true ? physicalRefreshSetSql : '',
5754
- physicalUpsertSetSql,
5755
- outputPhysicalColumns,
5756
- force: input.force === true,
5757
- });
5859
+ const prepared =
5860
+ input.mode === 'net_new'
5861
+ ? await prepareNetNewRuntimeSheetDatasetRows(client, session, {
5862
+ chunks,
5863
+ runId: input.runId,
5864
+ attemptId,
5865
+ attemptOwnerRunId,
5866
+ attemptExpiresAt,
5867
+ attemptSeq,
5868
+ writeVersion,
5869
+ physicalInsertColumnsSql,
5870
+ physicalInsertValuesSql,
5871
+ })
5872
+ : await prepareRuntimeSheetDatasetRows(client, session, {
5873
+ chunks,
5874
+ runId: input.runId,
5875
+ normalizedPlayName,
5876
+ normalizedTableNamespace,
5877
+ attemptId,
5878
+ attemptOwnerRunId,
5879
+ attemptExpiresAt,
5880
+ attemptSeq,
5881
+ writeVersion,
5882
+ physicalInsertColumnsSql,
5883
+ physicalInsertValuesSql,
5884
+ physicalRefreshSetSql:
5885
+ input.force === true ? physicalRefreshSetSql : '',
5886
+ physicalUpsertSetSql,
5887
+ outputPhysicalColumns,
5888
+ force: input.force === true,
5889
+ });
5758
5890
  timings.push({
5759
5891
  phase: 'prepare_rows_sql',
5760
5892
  ms: Date.now() - prepareStartedAt,
package/dist/cli/index.js CHANGED
@@ -625,10 +625,10 @@ var SDK_RELEASE = {
625
625
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
626
626
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
627
627
  // automatically without blocking their current command.
628
- version: "0.1.226",
628
+ version: "0.1.227",
629
629
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
630
630
  supportPolicy: {
631
- latest: "0.1.226",
631
+ latest: "0.1.227",
632
632
  minimumSupported: "0.1.53",
633
633
  deprecatedBelow: "0.1.219",
634
634
  commandMinimumSupported: [
@@ -610,10 +610,10 @@ var SDK_RELEASE = {
610
610
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
611
611
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
612
612
  // automatically without blocking their current command.
613
- version: "0.1.226",
613
+ version: "0.1.227",
614
614
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
615
615
  supportPolicy: {
616
- latest: "0.1.226",
616
+ latest: "0.1.227",
617
617
  minimumSupported: "0.1.53",
618
618
  deprecatedBelow: "0.1.219",
619
619
  commandMinimumSupported: [
package/dist/index.d.mts CHANGED
@@ -3426,6 +3426,11 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
3426
3426
  */
3427
3427
  run(options?: {
3428
3428
  description?: string;
3429
+ /**
3430
+ * `upsert` (default) returns every input row, reusing persisted work for
3431
+ * existing keys. `net_new` atomically inserts and returns only unseen keys.
3432
+ */
3433
+ mode?: 'upsert' | 'net_new';
3429
3434
  key?: (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);
3430
3435
  }): Promise<PlayDataset<OutputRow>>;
3431
3436
  };
package/dist/index.d.ts CHANGED
@@ -3426,6 +3426,11 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
3426
3426
  */
3427
3427
  run(options?: {
3428
3428
  description?: string;
3429
+ /**
3430
+ * `upsert` (default) returns every input row, reusing persisted work for
3431
+ * existing keys. `net_new` atomically inserts and returns only unseen keys.
3432
+ */
3433
+ mode?: 'upsert' | 'net_new';
3429
3434
  key?: (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);
3430
3435
  }): Promise<PlayDataset<OutputRow>>;
3431
3436
  };
package/dist/index.js CHANGED
@@ -424,10 +424,10 @@ var SDK_RELEASE = {
424
424
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
425
425
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
426
426
  // automatically without blocking their current command.
427
- version: "0.1.226",
427
+ version: "0.1.227",
428
428
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
429
429
  supportPolicy: {
430
- latest: "0.1.226",
430
+ latest: "0.1.227",
431
431
  minimumSupported: "0.1.53",
432
432
  deprecatedBelow: "0.1.219",
433
433
  commandMinimumSupported: [
package/dist/index.mjs CHANGED
@@ -354,10 +354,10 @@ var SDK_RELEASE = {
354
354
  // 0.1.175 ships loud `deepline enrich` empty-waterfall reporting.
355
355
  // 0.1.220 deprecates SDK CLI versions below 0.1.219 and updates them
356
356
  // automatically without blocking their current command.
357
- version: "0.1.226",
357
+ version: "0.1.227",
358
358
  apiContract: "2026-06-dataset-handle-results-hard-cutover",
359
359
  supportPolicy: {
360
- latest: "0.1.226",
360
+ latest: "0.1.227",
361
361
  minimumSupported: "0.1.53",
362
362
  deprecatedBelow: "0.1.219",
363
363
  commandMinimumSupported: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.226",
3
+ "version": "0.1.227",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {