deepline 0.3.284 → 0.3.286

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 (46) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +1 -2
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +26 -2
  4. package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +7 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +836 -2234
  6. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +19 -6
  7. package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +127 -789
  8. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +18 -0
  9. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +16 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +180 -14
  11. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +1 -0
  12. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +3 -22
  13. package/dist/bundling-sources/shared_libs/play-runtime/runtime-incident-drills.ts +26 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +59 -8
  15. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +122 -18
  16. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +458 -130
  17. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +60 -9
  18. package/dist/bundling-sources/shared_libs/play-runtime/test-async-batching.ts +4 -1
  19. package/dist/bundling-sources/shared_libs/play-runtime/test-runtime-seams.ts +13 -2
  20. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/receipt-cohort.ts +181 -326
  21. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +38 -151
  22. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +8 -0
  24. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +10 -1
  25. package/dist/bundling-sources/shared_libs/plays/enrich-compat-adapter.ts +2 -0
  26. package/dist/cli/index.js +30579 -34364
  27. package/dist/cli/index.mjs +30647 -34443
  28. package/dist/{client-BCXQ_Ftc.d.ts → client-B7ic-kY5.d.ts} +6 -3
  29. package/dist/{client-B0AjIkgb.d.mts → client-Dzf_KovW.d.mts} +6 -3
  30. package/dist/{compiler-manifest-B1zo3igW.d.mts → compiler-manifest-9SQtUuKb.d.mts} +1 -1
  31. package/dist/{compiler-manifest-B1zo3igW.d.ts → compiler-manifest-9SQtUuKb.d.ts} +1 -1
  32. package/dist/index.d.mts +4 -4
  33. package/dist/index.d.ts +4 -4
  34. package/dist/index.js +34 -4
  35. package/dist/index.mjs +34 -4
  36. package/dist/install-integrity.json +4 -4
  37. package/dist/plays/bundle-play-file.d.mts +2 -2
  38. package/dist/plays/bundle-play-file.d.ts +2 -2
  39. package/dist/plays/bundle-play-file.mjs +8 -1
  40. package/dist/plays/run-request.d.mts +2 -2
  41. package/dist/plays/run-request.d.ts +2 -2
  42. package/dist/release.d.mts +1 -1
  43. package/dist/release.d.ts +1 -1
  44. package/dist/release.js +1 -1
  45. package/dist/release.mjs +1 -1
  46. package/package.json +1 -1
@@ -48,7 +48,7 @@ export type RuntimeStepToolResultReceiptsInput = {
48
48
  */
49
49
  executionLock?: {
50
50
  required?: boolean;
51
- /** Provider-operation identity; defaults to the result-cache receipt. */
51
+ /** Invocation/provider-operation identity; never the shared cache key. */
52
52
  key?: string;
53
53
  ttlMs?: number;
54
54
  waitMs?: number;
@@ -101,7 +101,6 @@ type OwnedReceipt = {
101
101
  executionLockOwnerId: string | null;
102
102
  executionLockKey: string | null;
103
103
  lockLost: boolean;
104
- completedByAnotherOwner: ToolExecuteResult | null;
105
104
  heartbeatTimer: ReturnType<typeof setTimeout> | null;
106
105
  heartbeatInFlight: Promise<void> | null;
107
106
  };
@@ -157,14 +156,13 @@ export function createRuntimeStepToolResultReceipts(
157
156
  resultReceiptKey: string,
158
157
  source: ToolCallReceiptRecoverySource | 'owner' = 'cache',
159
158
  ): Promise<ToolExecuteResult | null> => {
160
- const receipt =
161
- initialReceipt?.key === resultReceiptKey
162
- ? (() => {
163
- const value = initialReceipt!.receipt;
164
- initialReceipt = undefined;
165
- return value;
166
- })()
167
- : await hydrate(() => input.store.get(resultReceiptKey));
159
+ let receipt: RuntimeStepReceipt | null;
160
+ if (initialReceipt?.key === resultReceiptKey) {
161
+ receipt = initialReceipt.receipt;
162
+ initialReceipt = undefined;
163
+ } else {
164
+ receipt = await hydrate(() => input.store.get(resultReceiptKey));
165
+ }
168
166
  const result = completedResult(receipt, resultReceiptKey);
169
167
  return result
170
168
  ? (input.recover?.({ result, resultReceiptKey, source }) ?? result)
@@ -184,7 +182,7 @@ export function createRuntimeStepToolResultReceipts(
184
182
 
185
183
  const renewExecutionLock = async (
186
184
  record: OwnedReceipt,
187
- ): Promise<'active' | 'completed' | 'lost'> => {
185
+ ): Promise<'active' | 'lost'> => {
188
186
  if (!record.executionLockOwnerId || !input.store.acquireExecutionLock) {
189
187
  return 'active';
190
188
  }
@@ -196,16 +194,6 @@ export function createRuntimeStepToolResultReceipts(
196
194
  if (renewed?.ownerExecutionId === record.executionLockOwnerId) {
197
195
  return 'active';
198
196
  }
199
- if (!record.force && input.store.enabled) {
200
- const completed = await readCompleted(
201
- record.resultReceiptKey,
202
- 'in_flight',
203
- );
204
- if (completed) {
205
- record.completedByAnotherOwner = completed;
206
- return 'completed';
207
- }
208
- }
209
197
  record.lockLost = true;
210
198
  return 'lost';
211
199
  };
@@ -226,11 +214,7 @@ export function createRuntimeStepToolResultReceipts(
226
214
  .then(() => undefined)
227
215
  .finally(() => {
228
216
  record.heartbeatInFlight = null;
229
- if (
230
- !record.lockLost &&
231
- !record.completedByAnotherOwner &&
232
- record.executionLockOwnerId
233
- ) {
217
+ if (!record.lockLost && record.executionLockOwnerId) {
234
218
  schedule();
235
219
  }
236
220
  });
@@ -249,7 +233,6 @@ export function createRuntimeStepToolResultReceipts(
249
233
 
250
234
  const acquireExecutionLock = async (
251
235
  resultReceiptKey: string,
252
- force: boolean,
253
236
  ): Promise<{ ownerId: string; key: string } | null> => {
254
237
  if (!lock.required || !input.store.enabled) return null;
255
238
  if (
@@ -261,8 +244,9 @@ export function createRuntimeStepToolResultReceipts(
261
244
  );
262
245
  }
263
246
 
264
- const executionLockKey = lock.key || resultReceiptKey;
265
247
  const ownerExecutionId = `tool-result:${crypto.randomUUID()}`;
248
+ const executionLockKey =
249
+ lock.key || `${resultReceiptKey}:invocation:${ownerExecutionId}`;
266
250
  const deadline = Date.now() + lock.waitMs;
267
251
  while (true) {
268
252
  const acquired = await input.store.acquireExecutionLock({
@@ -271,26 +255,9 @@ export function createRuntimeStepToolResultReceipts(
271
255
  ttlMs: lock.ttlMs,
272
256
  });
273
257
  if (acquired?.ownerExecutionId === ownerExecutionId) {
274
- // Recheck after acquiring: a prior holder may have completed between
275
- // our cache read and lock acquisition. A forced call intentionally
276
- // bypasses that immutable completed fact, matching the legacy path.
277
- if (!force) {
278
- const cached = await readCompleted(resultReceiptKey, 'in_flight');
279
- if (cached) {
280
- await input.store.releaseExecutionLock({
281
- receiptKey: executionLockKey,
282
- ownerExecutionId,
283
- });
284
- return null;
285
- }
286
- }
287
258
  return { ownerId: ownerExecutionId, key: executionLockKey };
288
259
  }
289
260
 
290
- if (!force) {
291
- const cached = await readCompleted(resultReceiptKey, 'in_flight');
292
- if (cached) return null;
293
- }
294
261
  if (Date.now() >= deadline) {
295
262
  throw new RuntimeReceiptWaitTimeoutError(resultReceiptKey);
296
263
  }
@@ -312,16 +279,7 @@ export function createRuntimeStepToolResultReceipts(
312
279
  if (cached) return { kind: 'completed', result: cached };
313
280
  }
314
281
 
315
- const executionLock = await acquireExecutionLock(
316
- resultReceiptKey,
317
- candidate.force,
318
- );
319
- if (input.store.enabled && !candidate.force && !executionLock) {
320
- const cached = await readCompleted(resultReceiptKey, 'cache');
321
- if (cached) return { kind: 'completed', result: cached };
322
- // The only non-lock reason to get here is a store without a lock
323
- // requirement. In that case this caller is still the cache publisher.
324
- }
282
+ const executionLock = await acquireExecutionLock(resultReceiptKey);
325
283
 
326
284
  const leaseId =
327
285
  executionLock?.ownerId ?? `tool-result:${crypto.randomUUID()}`;
@@ -332,7 +290,6 @@ export function createRuntimeStepToolResultReceipts(
332
290
  executionLockOwnerId: executionLock?.ownerId ?? null,
333
291
  executionLockKey: executionLock?.key ?? null,
334
292
  lockLost: false,
335
- completedByAnotherOwner: null,
336
293
  heartbeatTimer: null,
337
294
  heartbeatInFlight: null,
338
295
  });
@@ -350,30 +307,9 @@ export function createRuntimeStepToolResultReceipts(
350
307
  if (candidate.ownerRunId !== ownerRunId) {
351
308
  throw new ToolCallReceiptLeaseLostError(resultReceiptKey);
352
309
  }
353
- const maxWaitMs = candidate.source === 'in_flight' ? lock.waitMs : 0;
354
- const deadline = Date.now() + maxWaitMs;
355
- while (true) {
356
- const receipt = await hydrate(() => input.store.get(resultReceiptKey));
357
- const completed = completedResult(receipt, resultReceiptKey);
358
- if (completed) {
359
- return (
360
- input.recover?.({
361
- result: completed,
362
- resultReceiptKey,
363
- source: candidate.source,
364
- }) ?? completed
365
- );
366
- }
367
- if (receipt?.status === 'failed') {
368
- throw new Error(
369
- `Tool Result Receipt ${resultReceiptKey} failed: ${receipt.error ?? 'unknown error'}.`,
370
- );
371
- }
372
- if (Date.now() >= deadline) {
373
- throw new RuntimeReceiptWaitTimeoutError(resultReceiptKey);
374
- }
375
- await sleep(lock.pollMs);
376
- }
310
+ const completed = await readCompleted(resultReceiptKey, candidate.source);
311
+ if (completed) return completed;
312
+ throw new RuntimeReceiptWaitTimeoutError(resultReceiptKey);
377
313
  },
378
314
 
379
315
  async heartbeat(candidate) {
@@ -381,13 +317,6 @@ export function createRuntimeStepToolResultReceipts(
381
317
  resultReceiptKey: candidate.resultReceiptKey,
382
318
  lease: candidate.lease,
383
319
  });
384
- if (!record.force && input.store.enabled) {
385
- const cached = await readCompleted(
386
- record.resultReceiptKey,
387
- 'in_flight',
388
- );
389
- if (cached) return 'completed';
390
- }
391
320
  if (!record.executionLockOwnerId) return 'active';
392
321
  if (record.lockLost) return 'lost';
393
322
  const renewal = await renewExecutionLock(record);
@@ -398,11 +327,6 @@ export function createRuntimeStepToolResultReceipts(
398
327
  startHeartbeat(record);
399
328
  return 'active';
400
329
  }
401
- if (renewal === 'completed') return 'completed';
402
- if (!record.force && input.store.enabled) {
403
- const cached = await readCompleted(record.resultReceiptKey);
404
- if (cached) return 'completed';
405
- }
406
330
  return 'lost';
407
331
  },
408
332
 
@@ -413,9 +337,6 @@ export function createRuntimeStepToolResultReceipts(
413
337
  });
414
338
  try {
415
339
  await stopHeartbeat(record);
416
- if (record.completedByAnotherOwner) {
417
- return { kind: 'existing', result: record.completedByAnotherOwner };
418
- }
419
340
  if (record.lockLost) {
420
341
  throw new ToolCallReceiptLeaseLostError(record.resultReceiptKey);
421
342
  }
@@ -434,53 +355,24 @@ export function createRuntimeStepToolResultReceipts(
434
355
  serializeToolExecuteResult(candidate.result),
435
356
  COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
436
357
  );
437
- // Completion transports may acknowledge only status/ownership. A
438
- // compact acknowledgement that says this insert won is complete
439
- // information: the immutable stored result is the one just written, so
440
- // the caller's own result is it. Nothing is read back and nothing
441
- // hydrates.
442
- if (
443
- (persisted?.status === 'completed' ||
444
- persisted?.status === 'skipped') &&
445
- persisted.output === undefined &&
446
- persisted.completionDisposition === 'persisted'
447
- ) {
448
- return { kind: 'stored', result: candidate.result };
449
- }
450
- // Anything else — a concurrent winner, or a gateway too old to say who
451
- // won — really does need the durable row, so it decodes under the gate
452
- // or is read back below.
453
- const result =
454
- persisted?.output === undefined
455
- ? null
456
- : await hydrate(async () =>
457
- completedResult(persisted, record.resultReceiptKey),
458
- );
459
- if (result) {
460
- await input.onStored?.({
461
- result,
462
- resultReceiptKey: record.resultReceiptKey,
463
- });
464
- return {
465
- // The legacy storage operation returns the converged completed row
466
- // for both an insert and a concurrent winner. The exact distinction
467
- // is intentionally unobservable; the public invariant is the
468
- // converged immutable ToolExecuteResult.
469
- kind: 'stored',
470
- result,
471
- };
472
- }
473
- const converged = await readCompleted(record.resultReceiptKey, 'owner');
474
- if (converged) {
475
- await input.onStored?.({
476
- result: converged,
477
- resultReceiptKey: record.resultReceiptKey,
478
- });
479
- return { kind: 'existing', result: converged };
358
+ // Publication is immutable, but an independent miss keeps the
359
+ // result it actually executed. Another caller winning this cache key
360
+ // only affects a later cache lookup.
361
+ const completed =
362
+ persisted?.status === 'completed' || persisted?.status === 'skipped';
363
+ if (!completed) {
364
+ const stored = await input.store.get(record.resultReceiptKey);
365
+ if (stored?.status !== 'completed' && stored?.status !== 'skipped') {
366
+ throw new Error(
367
+ `Tool Result Receipt ${record.resultReceiptKey} could not publish a completed result.`,
368
+ );
369
+ }
480
370
  }
481
- throw new Error(
482
- `Tool Result Receipt ${record.resultReceiptKey} could not publish a completed result.`,
483
- );
371
+ await input.onStored?.({
372
+ result: candidate.result,
373
+ resultReceiptKey: record.resultReceiptKey,
374
+ });
375
+ return { kind: 'stored', result: candidate.result };
484
376
  } finally {
485
377
  await stopHeartbeat(record);
486
378
  await releaseExecutionLock(record);
@@ -493,16 +385,11 @@ export function createRuntimeStepToolResultReceipts(
493
385
  resultReceiptKey: candidate.resultReceiptKey,
494
386
  lease: candidate.lease,
495
387
  });
496
- try {
497
- // Completed-cache-only mode has no owned in-flight row to mark failed.
498
- // Recording a terminal failure would let an old owner overwrite a
499
- // concurrent successful publisher. Releasing just our lock is safe;
500
- // the original provider error remains the caller-visible fact.
501
- } finally {
502
- await stopHeartbeat(record);
503
- await releaseExecutionLock(record);
504
- owned.delete(candidate.lease.leaseId);
505
- }
388
+ // A failed invocation releases only its own lock. It never publishes a
389
+ // failure over a concurrent successful cache entry.
390
+ await stopHeartbeat(record);
391
+ await releaseExecutionLock(record);
392
+ owned.delete(candidate.lease.leaseId);
506
393
  },
507
394
  };
508
395
  }
@@ -2159,3 +2159,71 @@ export function cloneToolExecuteResultWithExecution<TResult>(
2159
2159
  execution,
2160
2160
  }) as ToolExecuteResult<TResult>;
2161
2161
  }
2162
+
2163
+ /**
2164
+ * Native splitters return the logical source response; the historical adapter
2165
+ * stores it inside an extra `{ data }` layer. Keep that immutable receipt and
2166
+ * its legacy raw/getter projections, and select the source response only at a
2167
+ * newly admitted Play's boundary. This is one provenance-backed projection,
2168
+ * never recursive provider-envelope unwrapping.
2169
+ */
2170
+ export function projectNativeBatchToolResponse(
2171
+ value: ToolExecuteResult,
2172
+ policy: 'legacy_envelope' | 'logical_response' = 'logical_response',
2173
+ ): ToolExecuteResult {
2174
+ const stored = value.toolResponse.rawV2;
2175
+ const materialized = value.meta?.nativeBatchResponseMaterialized === true;
2176
+ if (policy === 'legacy_envelope' && !materialized) return value;
2177
+ if (policy === 'logical_response' && materialized) return value;
2178
+ if (!materialized && (!isRecord(stored) || !Object.hasOwn(stored, 'data')))
2179
+ return value;
2180
+ const recordedView = value.meta?.nativeBatchResponseView;
2181
+ let view: ToolResponseView | undefined =
2182
+ recordedView === 'data' || recordedView === 'rawV2'
2183
+ ? recordedView
2184
+ : undefined;
2185
+ if (view !== 'data' && view !== 'rawV2') {
2186
+ // Retained BounceBan receipts predate the provenance fact. Its scalar
2187
+ // action returns `{ data: verdict }`; only the old batch adapter adds
2188
+ // `{ data: { data: verdict } }`. Do not apply this repair to other tools
2189
+ // or infer it from a provider's arbitrary `data` field.
2190
+ const source = (stored as Record<string, unknown>).data;
2191
+ const verdict = isRecord(source) ? source.data : undefined;
2192
+ if (
2193
+ value._metadata.toolId !== 'bounceban_verify_single' ||
2194
+ !isRecord(source) ||
2195
+ !isRecord(verdict) ||
2196
+ typeof verdict.email !== 'string' ||
2197
+ (verdict.status !== undefined && verdict.status !== 'success') ||
2198
+ !['deliverable', 'undeliverable', 'risky', 'unknown'].includes(
2199
+ String(verdict.result),
2200
+ )
2201
+ ) {
2202
+ return value;
2203
+ }
2204
+ view = 'data';
2205
+ }
2206
+ const response = {
2207
+ ...value.toolResponse,
2208
+ rawV2: materialized
2209
+ ? { data: stored }
2210
+ : (stored as Record<string, unknown>).data,
2211
+ view: materialized ? ('data' as const) : view,
2212
+ };
2213
+ // Preserve non-enumerable metadata/aliases and live list/getter accessors.
2214
+ // Do not mutate a result shared with another artifact or the receipt cache.
2215
+ const descriptors = Object.getOwnPropertyDescriptors(value);
2216
+ descriptors.toolResponse = { ...descriptors.toolResponse, value: response };
2217
+ descriptors.toolOutput = { ...descriptors.toolOutput, value: response };
2218
+ descriptors.meta = {
2219
+ value: {
2220
+ ...value.meta,
2221
+ nativeBatchResponseView: view,
2222
+ nativeBatchResponseMaterialized: !materialized,
2223
+ },
2224
+ writable: true,
2225
+ enumerable: true,
2226
+ configurable: true,
2227
+ };
2228
+ return Object.create(Object.getPrototypeOf(value), descriptors);
2229
+ }
@@ -1,4 +1,5 @@
1
1
  import type { PlayDocflowNodeIoState } from './docflow-node-io';
2
+ import type { PlayMapRowActivitySource } from './run-ledger';
2
3
 
3
4
  export type PlayStagedFileRef = {
4
5
  storageKind: 'r2';
@@ -17,6 +18,13 @@ export type PlayVisualNodeProgress = {
17
18
  startedRows?: number;
18
19
  activeRows?: number;
19
20
  waitingRows?: number;
21
+ /** Internal row-sample authority; omitted by legacy progress writers. */
22
+ rowActivitySource?: PlayMapRowActivitySource;
23
+ /** Existing executor identity; stable through source pages and replay. */
24
+ mapInvocationId?: string;
25
+ /** Runner-owned source times, never gateway ingestion times. */
26
+ rowActivityStartedAt?: number;
27
+ rowActivityObservedAt?: number;
20
28
  completedRows?: number;
21
29
  supersededRows?: number;
22
30
  message?: string;
@@ -7,10 +7,12 @@ import type { ToolExecutionErrorSchemaVersion } from './tool-execution-error';
7
7
  import type { PlayDataset, PlayDatasetInput, PlayDatasetRow } from './dataset';
8
8
 
9
9
  export const LEGACY_PLAY_AUTHORING_CONTRACT_EDITION = 1 as const;
10
+ // Reader-first rollout: accept edition 9, but keep new bundles on edition 8
11
+ // until the edition-9 runtime reader has completed deployment.
10
12
  export const PLAY_AUTHORING_CONTRACT_EDITION = 8 as const;
11
13
  export const PLAY_AUTHORING_INPUT_SCHEMA_SNAPSHOT_EDITION = 3 as const;
12
14
  export const SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS = [
13
- 1, 2, 3, 4, 5, 6, 7, 8,
15
+ 1, 2, 3, 4, 5, 6, 7, 8, 9,
14
16
  ] as const;
15
17
 
16
18
  export type PlayAuthoringContractEdition =
@@ -75,6 +77,13 @@ const PLAY_AUTHORING_CONTRACT_CHANGELOG_DETAILS = [
75
77
  newWritesEnd: null,
76
78
  readerRemoval: null,
77
79
  },
80
+ {
81
+ changed:
82
+ 'Projects native-batch rawV2 to the logical source response at the Play boundary. Editions 1–8 retain their admitted response nesting; completed tool receipts and provider identities are unchanged.',
83
+ compatibilityOwner: 'Plays Runtime',
84
+ newWritesEnd: null,
85
+ readerRemoval: null,
86
+ },
78
87
  ] as const;
79
88
 
80
89
  export const PLAY_AUTHORING_CONTRACT_CHANGELOG =
@@ -1,5 +1,7 @@
1
1
  export const ENRICH_COMPAT_DEFAULT_PLAY_NAME = 'deepline-enrich-v1-compat';
2
2
  export const ENRICH_COMPAT_DEFAULT_MAP_NAME = 'deepline_enrich_rows';
3
+ export const ENRICH_COMMAND_DEPRECATION_MESSAGE =
4
+ 'The `deepline enrich` command is deprecated. Deepline Plays gives much better results. Run `deepline plays --help` to get started. If you still need the legacy command, submit feedback explaining why.';
3
5
 
4
6
  export type EnrichCompatibilityOptions = {
5
7
  playName?: string;