deepline 0.3.392 → 0.3.393

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 (145) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +156 -24
  2. package/dist/bundling-sources/sdk/src/compat.ts +9 -0
  3. package/dist/bundling-sources/sdk/src/http.ts +5 -1
  4. package/dist/bundling-sources/sdk/src/index.ts +5 -0
  5. package/dist/bundling-sources/sdk/src/plays/run-request.ts +182 -6
  6. package/dist/bundling-sources/sdk/src/release.ts +4 -1
  7. package/dist/bundling-sources/sdk/src/types.ts +13 -0
  8. package/dist/bundling-sources/shared_libs/integration-contracts/request-warnings.ts +64 -0
  9. package/dist/bundling-sources/shared_libs/play-data-plane/runtime-sheet-storage-contract.ts +315 -0
  10. package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +41 -8
  11. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +418 -44
  12. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +36 -3
  13. package/dist/bundling-sources/shared_libs/play-runtime/durable-invocation-fence.ts +36 -0
  14. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +11 -3
  15. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +1 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +19 -1
  17. package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +26 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +158 -10
  19. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +235 -2
  20. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +92 -58
  21. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +7 -1
  22. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +30 -7
  23. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1161 -316
  24. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +28 -15
  25. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +24 -3
  26. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +31 -0
  27. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +37 -4
  28. package/dist/bundling-sources/shared_libs/plays/dataset.ts +172 -79
  29. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +173 -1
  30. package/dist/cli/{auth-6JSA2ZCW.mjs → auth-5PXHA232.mjs} +4 -4
  31. package/dist/cli/auth-P3MVDTXM.js +39 -0
  32. package/dist/cli/{billing-Q5BLQHIU.js → billing-JFB2XLSO.js} +101 -74
  33. package/dist/cli/{billing-MRRO643H.mjs → billing-Z2N7ZAGM.mjs} +32 -5
  34. package/dist/cli/{chunk-CVLM3WMJ.js → chunk-2HAD745V.js} +242 -183
  35. package/dist/cli/{chunk-HGYBEAQP.mjs → chunk-2LGC4EFT.mjs} +3 -3
  36. package/dist/cli/{chunk-A3APGZFP.mjs → chunk-4PT2VA3A.mjs} +2 -2
  37. package/dist/cli/{chunk-6DBOU7UH.js → chunk-4XX6443T.js} +176 -87
  38. package/dist/cli/{chunk-2QLML6G4.mjs → chunk-5KWKZIQR.mjs} +4 -4
  39. package/dist/cli/{chunk-2G7DXFHI.mjs → chunk-5PRVMXKB.mjs} +27 -2
  40. package/dist/cli/{chunk-DSWSEHRX.mjs → chunk-6I73X254.mjs} +1 -1
  41. package/dist/cli/{chunk-ZTACINO5.mjs → chunk-7HR7BGZL.mjs} +13 -3
  42. package/dist/cli/{chunk-BB3FNYFY.js → chunk-AARX5L4B.js} +2 -2
  43. package/dist/cli/{chunk-WMS5U6YB.js → chunk-CDFH2DYL.js} +20 -19
  44. package/dist/cli/{chunk-R3O2CZ4H.mjs → chunk-FQ6VHPW4.mjs} +2 -2
  45. package/dist/cli/{chunk-MU6M3URX.mjs → chunk-HLEESTCH.mjs} +1 -1
  46. package/dist/cli/{chunk-Q6EOHNRH.mjs → chunk-JI2B4RZP.mjs} +119 -6
  47. package/dist/cli/{chunk-2Q3GEFL4.mjs → chunk-KIYU2TGL.mjs} +2 -2
  48. package/dist/cli/{chunk-Y6VOGHF2.mjs → chunk-KZZC33PY.mjs} +0 -181
  49. package/dist/cli/{chunk-UCKJQG25.js → chunk-L2SYLGRP.js} +8 -8
  50. package/dist/cli/{chunk-2GAHHSLB.mjs → chunk-LMMEAMA7.mjs} +3 -2
  51. package/dist/cli/{chunk-YAZCDBXX.mjs → chunk-LZPCDB7U.mjs} +3 -2
  52. package/dist/cli/{chunk-RV6ALZIX.js → chunk-M7Y7JYZX.js} +32 -7
  53. package/dist/cli/{chunk-QGKPTBRR.js → chunk-MS6GUPBB.js} +3 -3
  54. package/dist/cli/{chunk-4SFPVW7A.js → chunk-OXNOAD6Y.js} +26 -26
  55. package/dist/cli/{chunk-JOFPQGXW.js → chunk-PTDECJGP.js} +50 -50
  56. package/dist/cli/{chunk-5O4B6AUN.js → chunk-PWSWAN7Q.js} +25 -2
  57. package/dist/cli/{chunk-YN6YA7ZZ.mjs → chunk-Q26LUSQ2.mjs} +79 -20
  58. package/dist/cli/{chunk-3VN4FTE3.js → chunk-RDXFGPJU.js} +1 -182
  59. package/dist/cli/{chunk-7E66A24V.mjs → chunk-RKMB3N7K.mjs} +25 -2
  60. package/dist/cli/{chunk-JSLPHFP7.js → chunk-RWHWF5ZJ.js} +3 -2
  61. package/dist/cli/{chunk-3FV4GBRK.mjs → chunk-STNYIMIV.mjs} +97 -8
  62. package/dist/cli/chunk-UBKA33KY.mjs +310 -0
  63. package/dist/cli/{chunk-PAIMOGCM.js → chunk-UHNDVD3P.js} +125 -12
  64. package/dist/cli/{chunk-24VEN5PQ.mjs → chunk-UOGZUA24.mjs} +179 -17
  65. package/dist/cli/{chunk-T7OKJ2T6.js → chunk-VIQJIX2P.js} +73 -63
  66. package/dist/cli/{chunk-RIV2GS5E.js → chunk-VIWE6WRK.js} +627 -465
  67. package/dist/cli/{chunk-EY7RFC6A.js → chunk-W4WOMXR3.js} +15 -15
  68. package/dist/cli/chunk-XDVT3GOM.js +310 -0
  69. package/dist/cli/{chunk-2FQG2HU4.js → chunk-Z3OOZM5L.js} +8 -8
  70. package/dist/cli/{csv-E4QGUIGI.mjs → csv-KJWZHKCH.mjs} +3 -3
  71. package/dist/cli/{csv-26U5T24Z.js → csv-P3Z3YE7H.js} +12 -12
  72. package/dist/cli/{db-JTRJIAQW.mjs → db-NCM4CQIS.mjs} +6 -5
  73. package/dist/cli/{db-QHBFLMVL.js → db-NUZGJNQC.js} +31 -30
  74. package/dist/cli/{describe-E3DE3ZK3.mjs → describe-WT6MM2GO.mjs} +17 -16
  75. package/dist/cli/{describe-FDV2V74F.js → describe-YGSEN5WC.js} +24 -23
  76. package/dist/cli/{executions-VZYOGVQY.js → executions-PCOLHN7Q.js} +8 -7
  77. package/dist/cli/{executions-B3GTNPRE.mjs → executions-XS2IPJ4W.mjs} +6 -5
  78. package/dist/cli/{feedback-CR55FJA2.js → feedback-4MSH7TFU.js} +7 -7
  79. package/dist/cli/{feedback-4CEBR3OV.mjs → feedback-CCLTS5SV.mjs} +3 -3
  80. package/dist/cli/{git-NM2PHHZ4.mjs → git-K2DHPMPC.mjs} +4 -4
  81. package/dist/cli/git-RNM45FX4.js +23 -0
  82. package/dist/cli/index.js +96 -92
  83. package/dist/cli/index.mjs +46 -42
  84. package/dist/cli/{legacy-noops-A7MSW5ND.mjs → legacy-noops-B5S4AR3P.mjs} +4 -4
  85. package/dist/cli/{legacy-noops-ZUA4YFIH.js → legacy-noops-GPNOPFXV.js} +7 -7
  86. package/dist/cli/{monitors-NVPRQYP3.mjs → monitors-JLMAIPRG.mjs} +6 -5
  87. package/dist/cli/{monitors-ZE3TLZ4H.js → monitors-ZAWD7NSN.js} +87 -86
  88. package/dist/cli/{org-U7A6DV5D.js → org-P2KN4CES.js} +66 -65
  89. package/dist/cli/{org-KUBILNQV.mjs → org-T3JTHP4G.mjs} +6 -5
  90. package/dist/cli/{play-HRDXQ4QE.mjs → play-NK6BLRGL.mjs} +11 -10
  91. package/dist/cli/play-PDXR6JFB.js +158 -0
  92. package/dist/cli/{providers-OHIWI453.mjs → providers-3NQRA7IT.mjs} +6 -5
  93. package/dist/cli/{providers-VD6YTVQZ.js → providers-5QRZN3DT.js} +9 -8
  94. package/dist/cli/{quickstart-UBV2FONF.mjs → quickstart-PIA2JGPV.mjs} +3 -3
  95. package/dist/cli/{quickstart-ETTCWPA4.js → quickstart-R7NUZGNW.js} +5 -5
  96. package/dist/cli/run-identity-G257GQED.js +25 -0
  97. package/dist/cli/run-identity-MRNCEGV2.mjs +25 -0
  98. package/dist/cli/{search-F44IZRT7.mjs → search-AIDIIGLY.mjs} +7 -6
  99. package/dist/cli/search-QRWL7MFM.js +27 -0
  100. package/dist/cli/{secrets-CAEMRORF.js → secrets-BT3PGMWN.js} +15 -14
  101. package/dist/cli/{secrets-R6XOJZKU.mjs → secrets-OQNHL45O.mjs} +6 -5
  102. package/dist/cli/{sessions-6GC7OFTT.js → sessions-EYXWOSWK.js} +5 -5
  103. package/dist/cli/{sessions-RLN6UQS3.mjs → sessions-SF7WJPJP.mjs} +4 -4
  104. package/dist/cli/{settings-DPNVUAV2.js → settings-BA3GHBOD.js} +76 -75
  105. package/dist/cli/{settings-KIPAMRPP.mjs → settings-Y57JTFDD.mjs} +9 -8
  106. package/dist/cli/{setup-TU2F7PU5.mjs → setup-7GR5GBHA.mjs} +6 -6
  107. package/dist/cli/{setup-DTL3L5SG.js → setup-RERXYC66.js} +41 -41
  108. package/dist/cli/{skills-UV5MYJ46.mjs → skills-SJWIQAV5.mjs} +4 -4
  109. package/dist/cli/skills-V7EY75HB.js +28 -0
  110. package/dist/cli/tools-H5HLBCHH.js +41 -0
  111. package/dist/cli/{tools-QRRNDCQ7.mjs → tools-ZF3JXPNL.mjs} +10 -9
  112. package/dist/cli/update-472HA3LT.js +28 -0
  113. package/dist/cli/{update-4426TF35.mjs → update-FFFYQAOJ.mjs} +6 -6
  114. package/dist/cli/{workflow-HNLOVISB.js → workflow-SZJVQH4T.js} +17 -16
  115. package/dist/cli/{workflow-N4U5VLZ3.mjs → workflow-XVFEEGAS.mjs} +7 -6
  116. package/dist/{client-e6Qh5ZIg.d.mts → client-DU5dtHDy.d.mts} +153 -4
  117. package/dist/{client-Uxuy-xQ2.d.ts → client-mCE88hC4.d.ts} +153 -4
  118. package/dist/{compiler-manifest-CY2v8ab0.d.ts → compiler-manifest-DE4RdAiY.d.mts} +61 -1
  119. package/dist/{compiler-manifest-CY2v8ab0.d.mts → compiler-manifest-DE4RdAiY.d.ts} +61 -1
  120. package/dist/{index-B9fBZH1g.d.mts → index-BI6nWgaV.d.mts} +1 -1
  121. package/dist/{index-BZ6uzZJl.d.ts → index-Bi-f7HFT.d.ts} +1 -1
  122. package/dist/index.d.mts +5 -5
  123. package/dist/index.d.ts +5 -5
  124. package/dist/index.js +120 -29
  125. package/dist/index.mjs +111 -20
  126. package/dist/install-integrity.json +91 -86
  127. package/dist/plays/bundle-play-file.d.mts +3 -3
  128. package/dist/plays/bundle-play-file.d.ts +3 -3
  129. package/dist/plays/bundle-play-file.mjs +2 -2
  130. package/dist/plays/run-request.d.mts +2 -2
  131. package/dist/plays/run-request.d.ts +2 -2
  132. package/dist/plays/run-request.js +5 -2
  133. package/dist/plays/run-request.mjs +4 -1
  134. package/dist/release.d.mts +4 -1
  135. package/dist/release.d.ts +4 -1
  136. package/dist/release.js +2 -2
  137. package/dist/release.mjs +1 -1
  138. package/package.json +1 -1
  139. package/dist/cli/auth-67TAELQL.js +0 -39
  140. package/dist/cli/git-I5JTK6YU.js +0 -23
  141. package/dist/cli/play-A2V7YDU7.js +0 -157
  142. package/dist/cli/search-WWXMBVPH.js +0 -26
  143. package/dist/cli/skills-SWOSEZLB.js +0 -28
  144. package/dist/cli/tools-IL2YCDQ6.js +0 -40
  145. package/dist/cli/update-BERDPEYR.js +0 -28
@@ -345,10 +345,12 @@ export interface RuntimeDatasetOptions<TItem = Record<string, unknown>> {
345
345
  * row. Use this to pin identity to primary columns (e.g. `{ key: "email" }`
346
346
  * or `{ key: ["first_name", "last_name", "domain"] }`) so harmless
347
347
  * mutations to other columns don't invalidate the cache.
348
+ * Providing a key selects key identity for non-CSV sources and retained
349
+ * authoring editions.
348
350
  *
349
351
  * Must return a non-empty string (numbers are coerced). Empty keys throw
350
352
  * loudly before any provider work runs. Rows whose keys duplicate an earlier
351
- * row are silently deduped (first occurrence wins) and logged, never failing
353
+ * row are deduped (first occurrence wins) and counted, never failing
352
354
  * the run. The key function should reference *input*
353
355
  * columns only — values produced by this dataset's field resolvers are stripped
354
356
  * before identity is computed.
@@ -357,6 +359,14 @@ export interface RuntimeDatasetOptions<TItem = Record<string, unknown>> {
357
359
  | (keyof TItem & string)
358
360
  | readonly (keyof TItem & string)[]
359
361
  | ((row: TItem, index: number) => string | number | readonly unknown[]);
362
+ /**
363
+ * Edition 11 CSV inputs always use source_row: the original CSV record
364
+ * ordinal distinguishes equal records and every occurrence is processed,
365
+ * even when key or rowIdentity: 'key' is supplied. For non-CSV sources and
366
+ * retained editions, rowIdentity: 'key' keeps the existing first-wins
367
+ * dedupe behavior.
368
+ */
369
+ rowIdentity?: 'source_row' | 'key';
360
370
  /**
361
371
  * Row failure policy for this dataset run.
362
372
  *
@@ -400,7 +410,12 @@ export type RuntimeDatasetDefinitionOptions<TItem = Record<string, unknown>> =
400
410
 
401
411
  export type RuntimeDatasetRunOptions<TItem = Record<string, unknown>> = Pick<
402
412
  RuntimeDatasetOptions<TItem>,
403
- 'description' | 'key' | 'onRowError' | 'mode' | 'undrawnColumns'
413
+ | 'description'
414
+ | 'key'
415
+ | 'rowIdentity'
416
+ | 'onRowError'
417
+ | 'mode'
418
+ | 'undrawnColumns'
404
419
  >;
405
420
 
406
421
  /** Runtime-only receipt recovery bit; never part of the customer authoring API. */
@@ -500,6 +515,11 @@ export interface MapExecutionFrame {
500
515
  suspendedInputRows?: Array<{
501
516
  inputIndex: number;
502
517
  data: Record<string, unknown>;
518
+ /** Original admission input, separate from restored execution/output cells. */
519
+ authoredInput?: {
520
+ data: Record<string, unknown>;
521
+ source?: Readonly<{ datasetId: string; rowIndex: number }>;
522
+ };
503
523
  /** Digest of the authored input before Runtime Sheet outputs were merged. */
504
524
  authoredInputDigest?: string;
505
525
  }>;
@@ -507,6 +527,13 @@ export interface MapExecutionFrame {
507
527
  completedRowsCount?: number;
508
528
  pendingRowsCount?: number;
509
529
  failedRowsCount?: number;
530
+ /** Original source cardinality retained across pending-only checkpoint replay. */
531
+ inputCardinality?: {
532
+ sourceRows: number;
533
+ uniqueInputRows: number;
534
+ duplicateOccurrences: number;
535
+ rowIdentity: 'source_row' | 'key';
536
+ };
510
537
  /** Bounded final-row causes, additive for checkpoint compatibility. */
511
538
  failureCounts?: import('./row-failure-summary').RowFailureCounts;
512
539
  /** Terminal rows omitted from this run's latest view because a newer write won. */
@@ -988,7 +1015,9 @@ export interface ContextOptions {
988
1015
  tableNamespace: string;
989
1016
  succeededRows: number;
990
1017
  failedRows: number;
991
- }) => void;
1018
+ expectedRows?: number;
1019
+ staticPipeline?: PlayStaticPipeline | null;
1020
+ }) => void | Promise<void>;
992
1021
  /**
993
1022
  * Persists coalesced, non-terminal row patches before a map suspends.
994
1023
  * Terminal rows use onMapRowsCompleted; this narrow seam only preserves
@@ -1071,6 +1100,10 @@ export interface ContextOptions {
1071
1100
  toolId: string,
1072
1101
  ) => Promise<string[]> | string[];
1073
1102
  getToolAuthScopeDigest?: (toolId: string) => Promise<string> | string;
1103
+ /** Absent on older app readers; only the declared async boundary may opt in. */
1104
+ getToolAsyncExecutionRecovery?: (
1105
+ toolId: string,
1106
+ ) => Promise<{ origin: string } | null>;
1074
1107
  /** Original run for an explicit customer rerun, never an automatic retry. */
1075
1108
  replayedFromRunId?: string | null;
1076
1109
  getToolRateScope?: (
@@ -1,3 +1,39 @@
1
+ import { sha256Hex } from '../plays/row-identity';
2
+
3
+ export const RUNTIME_EXECUTION_RECOVERY_KEY_PREFIX = 'runtime-gateway:';
4
+ export const RUNTIME_EXECUTION_RECOVERY_SUPPORTED_HEADER =
5
+ 'x-deepline-idempotency-supported';
6
+ export const RUNTIME_EXECUTION_RECOVERY_ONLY_HEADER =
7
+ 'x-deepline-execution-recovery-only';
8
+ /** Gateway-owned support for forwarding a keyed async call and its app ACK. */
9
+ export const RUNTIME_ASYNC_EXECUTION_RECOVERY_TRANSPORT_HEADER =
10
+ 'x-deepline-async-execution-recovery-transport';
11
+
12
+ /** Same-run delivery recovery must not coalesce separate Play runs. */
13
+ export function buildRuntimeExecutionRecoveryKey(input: {
14
+ parentRunId: string;
15
+ invocationKey: string;
16
+ }): string {
17
+ if (!input.parentRunId.trim() || !input.invocationKey.trim()) {
18
+ throw new Error(
19
+ 'Runtime execution recovery requires run and invocation identities.',
20
+ );
21
+ }
22
+ return `${RUNTIME_EXECUTION_RECOVERY_KEY_PREFIX}${sha256Hex(input.parentRunId)}:${sha256Hex(input.invocationKey)}`;
23
+ }
24
+
25
+ /** A known key is not authority to read another executor run's response. */
26
+ export function isRuntimeExecutionRecoveryKeyForRun(
27
+ key: string,
28
+ runId: string,
29
+ ): boolean {
30
+ if (!runId.trim()) return false;
31
+ const prefix = `${RUNTIME_EXECUTION_RECOVERY_KEY_PREFIX}${sha256Hex(runId)}:`;
32
+ return (
33
+ key.startsWith(prefix) && /^[a-f0-9]{64}$/.test(key.slice(prefix.length))
34
+ );
35
+ }
36
+
1
37
  /**
2
38
  * `execute-fenced-v2` is the first operation that requires semantic provider
3
39
  * identity. It is intentionally a named operation rather than a runtime-wide
@@ -24,10 +24,18 @@ export function cloneMapFrame(frame: MapExecutionFrame): MapExecutionFrame {
24
24
  ...(frame.suspendedInputRows
25
25
  ? {
26
26
  suspendedInputRows: frame.suspendedInputRows.map((row) => ({
27
- inputIndex: row.inputIndex,
27
+ ...row,
28
28
  data: cloneCsvAliasedRow(row.data),
29
- ...(row.authoredInputDigest
30
- ? { authoredInputDigest: row.authoredInputDigest }
29
+ ...(row.authoredInput
30
+ ? {
31
+ authoredInput: {
32
+ ...row.authoredInput,
33
+ data: cloneCsvAliasedRow(row.authoredInput.data),
34
+ ...(row.authoredInput.source
35
+ ? { source: { ...row.authoredInput.source } }
36
+ : {}),
37
+ },
38
+ }
31
39
  : {}),
32
40
  })),
33
41
  }
@@ -35,6 +35,7 @@ export function createGatewayRunLedgerProjectionAdapter(
35
35
  runId: input.runId,
36
36
  runtimeApi: input.runtimeApi,
37
37
  tokenAuthority: 'gateway' as const,
38
+ ...(input.runtimeApi.signal ? { signal: input.runtimeApi.signal } : {}),
38
39
  };
39
40
  return {
40
41
  async deliver(request) {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  normalizePlayAuthoringContractEdition,
3
3
  playToolListGetterMode,
4
+ playCsvDatasetRowIdentity,
4
5
  type PlayAuthoringContractEdition,
5
6
  } from '../plays/authoring-contract';
6
7
 
@@ -50,11 +51,12 @@ export type PlayRuntimeSemantics = Readonly<{
50
51
  slackHitl: Readonly<{
51
52
  defaultTimeoutMs: number;
52
53
  }>;
54
+ datasets: Readonly<{ csvRowIdentity: 'source_row' | 'key' }>;
53
55
  }>;
54
56
 
55
57
  type PlayRuntimeSemanticsTemplate = Omit<
56
58
  PlayRuntimeSemantics,
57
- 'authoringContractEdition' | 'tools'
59
+ 'authoringContractEdition' | 'tools' | 'datasets'
58
60
  > & { tools: Omit<PlayRuntimeSemantics['tools'], 'listGetters'> };
59
61
 
60
62
  function definePlayRuntimeSemantics(
@@ -72,6 +74,11 @@ function definePlayRuntimeSemantics(
72
74
  listGetters: playToolListGetterMode(semantics.authoringContractEdition),
73
75
  }),
74
76
  slackHitl: Object.freeze({ ...semantics.slackHitl }),
77
+ datasets: Object.freeze({
78
+ csvRowIdentity: playCsvDatasetRowIdentity(
79
+ semantics.authoringContractEdition,
80
+ ),
81
+ }),
75
82
  });
76
83
  }
77
84
 
@@ -198,6 +205,17 @@ const PLAY_RUNTIME_SEMANTICS_BY_AUTHORING = {
198
205
  tools: { nativeBatchResponse: 'logical_response' },
199
206
  slackHitl: WEEK_LONG_SLACK_HITL_SEMANTICS,
200
207
  },
208
+ 11: {
209
+ ...PLAINTEXT_SECRET_SEMANTICS,
210
+ fetch: {
211
+ httpFailure: 'throw_typed_error',
212
+ persistHttpFailure: false,
213
+ receiptIdentityEdition: FETCH_RECEIPT_IDENTITIES.typedHttpFailure,
214
+ },
215
+ inlineChild: { runtimeOnly: true },
216
+ tools: { nativeBatchResponse: 'logical_response' },
217
+ slackHitl: WEEK_LONG_SLACK_HITL_SEMANTICS,
218
+ },
201
219
  } satisfies Record<PlayAuthoringContractEdition, PlayRuntimeSemanticsTemplate>;
202
220
 
203
221
  const PLAY_RUNTIME_SEMANTICS = Object.fromEntries(
@@ -90,6 +90,32 @@ export function buildPlayWorkflowId(
90
90
  return `play/${familyKey}/run/${buildPlayWorkflowRunSuffix(timestamp)}${entropy}`;
91
91
  }
92
92
 
93
+ /** Caller-owned identity binds the complete submitted JSON before admission. */
94
+ export function buildCallerPlayWorkflowId(
95
+ playName: string,
96
+ request: Record<string, unknown>,
97
+ now?: Date,
98
+ ): string {
99
+ const { workflowId: _ignored, ...intent } = request;
100
+ const wireIntent = JSON.parse(JSON.stringify(intent));
101
+ return `${buildPlayWorkflowId(playName, now, { unique: true })}-caller-${sha256Hex(stableStringify(wireIntent))}`;
102
+ }
103
+
104
+ export function callerPlayWorkflowIdMatchesRequest(
105
+ workflowId: string,
106
+ request: Record<string, unknown>,
107
+ ): boolean {
108
+ const match =
109
+ /^play\/[a-z0-9/-]+\/run\/\d{8}t\d{6}-\d{3}-[a-z0-9]{8}-caller-([a-f0-9]{64})$/.exec(
110
+ workflowId,
111
+ );
112
+ if (!match || canonicalPlayRunStartedAtMs(workflowId) === null) return false;
113
+ const { workflowId: _ignored, ...intent } = request;
114
+ return (
115
+ match[1] === sha256Hex(stableStringify(JSON.parse(JSON.stringify(intent))))
116
+ );
117
+ }
118
+
93
119
  export type PlayRunIdentity = {
94
120
  workflowId: string;
95
121
  runId: string;
@@ -19,6 +19,7 @@ import { csvInputTextOptions } from '../../plays/csv-text-repair';
19
19
  import { install as installSourceMapSupport } from 'source-map-support';
20
20
  import {
21
21
  createDeferredPlayDataset,
22
+ setPlayDatasetSourceRowIdentity,
22
23
  isPlayDataset,
23
24
  isSerializedPlayDataset,
24
25
  iteratePlayDatasetInputPages,
@@ -128,6 +129,7 @@ import {
128
129
  import {
129
130
  resolveRuntimeReferencedPlay,
130
131
  createCompleteRuntimeMapRowsOperation,
132
+ finishRuntimeSheetRunMembership,
131
133
  createRuntimeSheetDatasetPageReader,
132
134
  type RuntimeSheetDatasetPageReader,
133
135
  readRuntimeSheetDatasetRowKeys,
@@ -140,6 +142,7 @@ import {
140
142
  } from '../runtime-api';
141
143
  import { installPgRuntimePoolDriver } from '../runtime-pg-driver-pg';
142
144
  import { createRuntimeToolAuthScopeDigestResolver } from '../auth-scope-resolver';
145
+ import { RUNTIME_ASYNC_EXECUTION_RECOVERY_TRANSPORT_HEADER } from '../durable-invocation-fence';
143
146
  import { createRunScopedChildPlayResolver } from './child-play-resolver';
144
147
  import { normalizePlayContractCompatibility } from '../../plays/contracts';
145
148
  import { ToolExecutionError } from '../../plays/tool-execution-error';
@@ -379,6 +382,8 @@ type RuntimeToolMetadata = {
379
382
 
380
383
  type RuntimeToolAuthScope = {
381
384
  digest?: unknown;
385
+ managedAsyncExecutionRecovery?: unknown;
386
+ managedAsyncExecutionRecoveryOrigin?: unknown;
382
387
  rateScope?: {
383
388
  bucketId?: unknown;
384
389
  provider?: unknown;
@@ -534,7 +539,7 @@ export function runtimeSheetTerminalMapCounts(
534
539
  }
535
540
 
536
541
  async function vercelBypassHeaders(
537
- config: PlayRunnerExecutionConfig,
542
+ config: Pick<PlayRunnerExecutionConfig, 'context'>,
538
543
  ): Promise<Record<string, string>> {
539
544
  return vercelProtectionBypassHeader(
540
545
  config.context.vercelProtectionBypassToken,
@@ -723,9 +728,14 @@ function createRuntimeToolMetadataResolver(config: PlayRunnerExecutionConfig) {
723
728
  };
724
729
  }
725
730
 
726
- function createAppRuntimeToolAuthScopeDigestResolver(
727
- config: PlayRunnerExecutionConfig,
731
+ export function createAppRuntimeToolAuthScopeDigestResolver(
732
+ config: Pick<PlayRunnerExecutionConfig, 'context'>,
728
733
  ) {
734
+ const asyncRecoveryOrigins = new Map<string, string>();
735
+ const executionRelayRecovery = new Map<
736
+ string,
737
+ Promise<{ origin: string } | null>
738
+ >();
729
739
  const rateScopes = new Map<
730
740
  string,
731
741
  { bucketId: string; provider: string; token: string }
@@ -783,6 +793,18 @@ function createAppRuntimeToolAuthScopeDigestResolver(
783
793
  );
784
794
  }
785
795
  rateScopes.set(normalizedToolId, { bucketId, provider, token });
796
+ if (
797
+ response.headers.get(
798
+ RUNTIME_ASYNC_EXECUTION_RECOVERY_TRANSPORT_HEADER,
799
+ ) === 'true' &&
800
+ body.managedAsyncExecutionRecovery === true &&
801
+ typeof body.managedAsyncExecutionRecoveryOrigin === 'string'
802
+ ) {
803
+ asyncRecoveryOrigins.set(
804
+ normalizedToolId,
805
+ body.managedAsyncExecutionRecoveryOrigin,
806
+ );
807
+ }
786
808
  return digest;
787
809
  },
788
810
  });
@@ -793,6 +815,10 @@ function createAppRuntimeToolAuthScopeDigestResolver(
793
815
  const normalized = String(toolId || '').trim();
794
816
  if (normalized) rateScopes.delete(normalized);
795
817
  else rateScopes.clear();
818
+ if (normalized) asyncRecoveryOrigins.delete(normalized);
819
+ else asyncRecoveryOrigins.clear();
820
+ if (normalized) executionRelayRecovery.delete(normalized);
821
+ else executionRelayRecovery.clear();
796
822
  },
797
823
  rateScope: async (toolId: string, expectedProvider: string) => {
798
824
  await digestResolver(toolId);
@@ -804,6 +830,60 @@ function createAppRuntimeToolAuthScopeDigestResolver(
804
830
  }
805
831
  return { bucketId: scope.bucketId, token: scope.token };
806
832
  },
833
+ asyncExecutionRecovery: async (toolId: string) => {
834
+ const normalized = toolId.trim();
835
+ const digest = await digestResolver(normalized);
836
+ const receiptBase = config.context.baseUrl!.trim().replace(/\/$/, '');
837
+ const executionBase =
838
+ config.context.executionGatewayBaseUrl?.trim().replace(/\/$/, '') ||
839
+ receiptBase;
840
+ if (executionBase === receiptBase) {
841
+ const origin = asyncRecoveryOrigins.get(normalized);
842
+ return origin ? { origin } : null;
843
+ }
844
+ // Receipt and execution fleets roll independently. Keep canonical
845
+ // digest/rate truth on the receipt owner, but never treat its transport
846
+ // capability as proof that the execution relay forwards the app ACK.
847
+ let proof = executionRelayRecovery.get(normalized);
848
+ if (!proof) {
849
+ proof = (async () => {
850
+ const response = await fetchRunnerRuntimeControl({
851
+ url: `${executionBase}/api/v2/plays/runtime-tools/${encodeURIComponent(normalized)}/auth-scope`,
852
+ description: `Runtime async recovery capability for tool ${normalized}`,
853
+ retrySafe: true,
854
+ init: {
855
+ method: 'GET',
856
+ headers: {
857
+ Authorization: `Bearer ${config.context.executorToken}`,
858
+ ...(await vercelBypassHeaders(config)),
859
+ },
860
+ },
861
+ });
862
+ if (
863
+ !response.ok ||
864
+ response.headers.get(
865
+ RUNTIME_ASYNC_EXECUTION_RECOVERY_TRANSPORT_HEADER,
866
+ ) !== 'true'
867
+ )
868
+ return null;
869
+ const body = (await response.json()) as RuntimeToolAuthScope;
870
+ return body.digest === digest &&
871
+ body.managedAsyncExecutionRecovery === true &&
872
+ typeof body.managedAsyncExecutionRecoveryOrigin === 'string'
873
+ ? { origin: body.managedAsyncExecutionRecoveryOrigin }
874
+ : null;
875
+ })().catch(() => {
876
+ // This optional capability cannot authorize replay on an unavailable
877
+ // hop. Preserve the existing unkeyed path and allow later discovery.
878
+ if (executionRelayRecovery.get(normalized) === proof) {
879
+ executionRelayRecovery.delete(normalized);
880
+ }
881
+ return null;
882
+ });
883
+ executionRelayRecovery.set(normalized, proof);
884
+ }
885
+ return await proof;
886
+ },
807
887
  });
808
888
  }
809
889
 
@@ -1122,11 +1202,19 @@ async function createStreamingCsvDataset(input: {
1122
1202
  options?: CsvRenameOptions;
1123
1203
  }): Promise<PlayDataset<Record<string, unknown>>> {
1124
1204
  let cachedCount: number | null = null;
1205
+ const datasetId = createRuntimeDatasetId(
1206
+ input.playName,
1207
+ input.tableNamespace,
1208
+ );
1125
1209
  const iterateProjected = () =>
1126
1210
  ({
1127
1211
  async *[Symbol.asyncIterator]() {
1212
+ let rowIndex = 0;
1128
1213
  for await (const row of input.source.iterate()) {
1129
- yield applyCsvRenameProjection([row], input.options)[0]!;
1214
+ yield setPlayDatasetSourceRowIdentity(
1215
+ applyCsvRenameProjection([row], input.options)[0]!,
1216
+ { datasetId, rowIndex: rowIndex++ },
1217
+ );
1130
1218
  }
1131
1219
  },
1132
1220
  }) as AsyncIterable<Record<string, unknown>>;
@@ -1142,7 +1230,7 @@ async function createStreamingCsvDataset(input: {
1142
1230
  const previewRows = await collect(10);
1143
1231
  return createDeferredPlayDataset({
1144
1232
  datasetKind: 'csv',
1145
- datasetId: createRuntimeDatasetId(input.playName, input.tableNamespace),
1233
+ datasetId,
1146
1234
  count: 0,
1147
1235
  knownCount: null,
1148
1236
  previewRows,
@@ -2204,6 +2292,7 @@ async function run(
2204
2292
  writeVersion?: number;
2205
2293
  }
2206
2294
  >();
2295
+ const membershipCapturingMaps = new Set<string>();
2207
2296
  const runtimeMapDatasetSettlements = new Map<
2208
2297
  string,
2209
2298
  {
@@ -2449,6 +2538,8 @@ async function run(
2449
2538
  ];
2450
2539
  },
2451
2540
  getToolAuthScopeDigest: getRuntimeToolAuthScopeDigest,
2541
+ getToolAsyncExecutionRecovery:
2542
+ getRuntimeToolAuthScopeDigest.asyncExecutionRecovery,
2452
2543
  getToolRateScope: getRuntimeToolAuthScopeDigest.rateScope,
2453
2544
  invalidateToolAuthScopeDigest: (toolId) =>
2454
2545
  getRuntimeToolAuthScopeDigest.invalidate?.(toolId),
@@ -2734,6 +2825,8 @@ async function run(
2734
2825
  },
2735
2826
  ),
2736
2827
  });
2828
+ if (mapStartResult.membershipCapture === true)
2829
+ membershipCapturingMaps.add(attemptKey);
2737
2830
  const datasetId = createRuntimeDatasetId(playName, tableNamespace);
2738
2831
  const datasetPath = `datasets.${tableNamespace}`;
2739
2832
  const settlement = runtimeMapDatasetSettlements.get(datasetId) ?? {
@@ -2889,11 +2982,66 @@ async function run(
2889
2982
  ),
2890
2983
  });
2891
2984
  },
2892
- onMapRowsSettled: (input) => {
2893
- const datasetId = createRuntimeDatasetId(
2894
- input.playName ?? config.context.playName!,
2895
- input.tableNamespace,
2896
- );
2985
+ onMapRowsSettled: async (input) => {
2986
+ const playName = input.playName ?? config.context.playName!;
2987
+ const runId =
2988
+ input.runId ??
2989
+ config.context.runId ??
2990
+ config.context.workflowId ??
2991
+ 'anonymous-run';
2992
+ const mapKey = runtimeSheetAttemptKey({
2993
+ playName,
2994
+ runId,
2995
+ tableNamespace: input.tableNamespace,
2996
+ });
2997
+ if (
2998
+ membershipCapturingMaps.has(mapKey) &&
2999
+ input.expectedRows !== undefined
3000
+ ) {
3001
+ const sheetContract = resolveSheetContractForTableNamespace(
3002
+ input.staticPipeline ?? config.context.staticPipeline,
3003
+ input.tableNamespace,
3004
+ );
3005
+ if (!sheetContract)
3006
+ throw new Error(
3007
+ `Missing sheet contract while finishing membership for ${input.tableNamespace}.`,
3008
+ );
3009
+ const canUseRoot =
3010
+ runId === (config.context.runId ?? config.context.workflowId);
3011
+ await retryRuntimeSheetPersistence({
3012
+ description: `runtime membership finish table=${input.tableNamespace}`,
3013
+ log: (line) => emitWorkerLog(line, options),
3014
+ signal: persistenceSignal,
3015
+ run: () =>
3016
+ finishRuntimeSheetRunMembership(
3017
+ {
3018
+ executorToken: config.context.executorToken,
3019
+ baseUrl: config.context.baseUrl!,
3020
+ orgId: config.context.orgId!,
3021
+ playName,
3022
+ runId,
3023
+ userEmail: config.context.userEmail,
3024
+ vercelProtectionBypassToken:
3025
+ config.context.vercelProtectionBypassToken,
3026
+ dbSessionStrategy: config.context.dbSessionStrategy,
3027
+ preloadedDbSessions: canUseRoot
3028
+ ? runtimePreloadedDbSessions
3029
+ : undefined,
3030
+ postgresSessionUnwrapKey: canUseRoot
3031
+ ? runtimePostgresSessionUnwrapKey
3032
+ : config.context.executorToken,
3033
+ abortSignal: persistenceSignal,
3034
+ },
3035
+ {
3036
+ runId,
3037
+ tableNamespace: input.tableNamespace,
3038
+ sheetContract,
3039
+ expectedRows: input.expectedRows!,
3040
+ },
3041
+ ),
3042
+ });
3043
+ }
3044
+ const datasetId = createRuntimeDatasetId(playName, input.tableNamespace);
2897
3045
  const settlement = runtimeMapDatasetSettlements.get(datasetId);
2898
3046
  if (settlement && (!input.runId || settlement.runId === input.runId)) {
2899
3047
  Object.assign(