deepline 0.3.393 → 0.3.394

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 (122) hide show
  1. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  2. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +477 -10
  3. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +5 -1
  4. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-policy.ts +28 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/freshness-receipt-execution.ts +251 -0
  6. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +12 -1
  7. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +10 -0
  8. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +25 -16
  9. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -0
  10. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +69 -22
  11. package/dist/bundling-sources/shared_libs/plays/core.ts +1 -1
  12. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +18 -0
  13. package/dist/cli/auth-IHKISLJQ.js +39 -0
  14. package/dist/cli/{auth-5PXHA232.mjs → auth-OSXDAVCF.mjs} +4 -4
  15. package/dist/cli/{billing-JFB2XLSO.js → billing-EKLYOXUY.js} +75 -75
  16. package/dist/cli/{billing-Z2N7ZAGM.mjs → billing-ZNNKX3UR.mjs} +4 -4
  17. package/dist/cli/{chunk-OXNOAD6Y.js → chunk-2MDDA7A4.js} +26 -26
  18. package/dist/cli/{chunk-PTDECJGP.js → chunk-354JCXA2.js} +50 -50
  19. package/dist/cli/{chunk-RKMB3N7K.mjs → chunk-3HU47EGZ.mjs} +48 -9
  20. package/dist/cli/{chunk-6I73X254.mjs → chunk-3HWALOL2.mjs} +1 -1
  21. package/dist/cli/{chunk-VIQJIX2P.js → chunk-4P27AXEA.js} +65 -65
  22. package/dist/cli/{chunk-UOGZUA24.mjs → chunk-5BB2RGAX.mjs} +18 -8
  23. package/dist/cli/{chunk-RWHWF5ZJ.js → chunk-6YAUMG27.js} +1 -1
  24. package/dist/cli/{chunk-HLEESTCH.mjs → chunk-BP33SIZF.mjs} +1 -1
  25. package/dist/cli/{chunk-CDFH2DYL.js → chunk-CS6RAIVY.js} +4 -4
  26. package/dist/cli/{chunk-2HAD745V.js → chunk-E23BQ5SC.js} +9 -9
  27. package/dist/cli/{chunk-LMMEAMA7.mjs → chunk-FJGW5GXU.mjs} +1 -1
  28. package/dist/cli/{chunk-Q26LUSQ2.mjs → chunk-JHIJIHFB.mjs} +1 -1
  29. package/dist/cli/{chunk-PWSWAN7Q.js → chunk-KB4SDX5S.js} +48 -9
  30. package/dist/cli/{chunk-M7Y7JYZX.js → chunk-KGNTKMRG.js} +6 -6
  31. package/dist/cli/{chunk-4XX6443T.js → chunk-KI635JN4.js} +59 -59
  32. package/dist/cli/{chunk-Z3OOZM5L.js → chunk-KIUMEOM5.js} +8 -8
  33. package/dist/cli/{chunk-KIYU2TGL.mjs → chunk-M57J2SK4.mjs} +2 -2
  34. package/dist/cli/{chunk-FQ6VHPW4.mjs → chunk-MFB2VKBO.mjs} +2 -2
  35. package/dist/cli/{chunk-MS6GUPBB.js → chunk-MH73HMPV.js} +3 -3
  36. package/dist/cli/{chunk-AARX5L4B.js → chunk-MLPU5SMZ.js} +2 -2
  37. package/dist/cli/{chunk-5PRVMXKB.mjs → chunk-NR3DFKNF.mjs} +1 -1
  38. package/dist/cli/{chunk-W4WOMXR3.js → chunk-QIQBYKLB.js} +15 -15
  39. package/dist/cli/{chunk-VIWE6WRK.js → chunk-RBJYWRCF.js} +260 -250
  40. package/dist/cli/{chunk-LZPCDB7U.mjs → chunk-TJI2EN6K.mjs} +1 -1
  41. package/dist/cli/{chunk-4TCT77UF.js → chunk-URWF7NGQ.js} +6 -1
  42. package/dist/cli/{chunk-5KWKZIQR.mjs → chunk-UXFSWG3Q.mjs} +4 -4
  43. package/dist/cli/{chunk-L2SYLGRP.js → chunk-VK6FYZLP.js} +8 -8
  44. package/dist/cli/{chunk-7HR7BGZL.mjs → chunk-X63FDZJZ.mjs} +3 -3
  45. package/dist/cli/{chunk-4PT2VA3A.mjs → chunk-XRQ2AQL7.mjs} +2 -2
  46. package/dist/cli/{chunk-STNYIMIV.mjs → chunk-XXUDVEXK.mjs} +6 -6
  47. package/dist/cli/{chunk-PQSA443V.mjs → chunk-ZI2ZNDJK.mjs} +6 -1
  48. package/dist/cli/{chunk-2LGC4EFT.mjs → chunk-ZYBGA4AR.mjs} +3 -3
  49. package/dist/cli/command-docs.js +2 -2
  50. package/dist/cli/command-docs.mjs +1 -1
  51. package/dist/cli/{csv-KJWZHKCH.mjs → csv-5AXRB35H.mjs} +3 -3
  52. package/dist/cli/{csv-P3Z3YE7H.js → csv-LU7O75WE.js} +12 -12
  53. package/dist/cli/{db-NCM4CQIS.mjs → db-7XP5UWEW.mjs} +4 -4
  54. package/dist/cli/{db-NUZGJNQC.js → db-F7YTFKVM.js} +29 -29
  55. package/dist/cli/{describe-YGSEN5WC.js → describe-FRH32RDV.js} +20 -20
  56. package/dist/cli/{describe-WT6MM2GO.mjs → describe-K4ES5LIL.mjs} +10 -10
  57. package/dist/cli/{executions-XS2IPJ4W.mjs → executions-IEHEHJUO.mjs} +4 -4
  58. package/dist/cli/{executions-PCOLHN7Q.js → executions-RZV5XPT2.js} +6 -6
  59. package/dist/cli/{feedback-4MSH7TFU.js → feedback-3P65QTX3.js} +7 -7
  60. package/dist/cli/{feedback-CCLTS5SV.mjs → feedback-FPUVZBAN.mjs} +3 -3
  61. package/dist/cli/{git-K2DHPMPC.mjs → git-GLVVP7TY.mjs} +4 -4
  62. package/dist/cli/git-MFFAWVVX.js +23 -0
  63. package/dist/cli/index.js +95 -95
  64. package/dist/cli/index.mjs +33 -33
  65. package/dist/cli/{legacy-noops-B5S4AR3P.mjs → legacy-noops-3MOI6NBM.mjs} +4 -4
  66. package/dist/cli/{legacy-noops-GPNOPFXV.js → legacy-noops-XKSGZR7Q.js} +7 -7
  67. package/dist/cli/{monitors-JLMAIPRG.mjs → monitors-FTBGNEFG.mjs} +4 -4
  68. package/dist/cli/{monitors-ZAWD7NSN.js → monitors-NZ2DYNST.js} +85 -85
  69. package/dist/cli/{org-P2KN4CES.js → org-MCKDQXIV.js} +64 -64
  70. package/dist/cli/{org-T3JTHP4G.mjs → org-V2J5RBCN.mjs} +4 -4
  71. package/dist/cli/{play-NK6BLRGL.mjs → play-AJFGME3B.mjs} +9 -9
  72. package/dist/cli/play-WLSN2EO2.js +158 -0
  73. package/dist/cli/{providers-5QRZN3DT.js → providers-2JEZ2264.js} +7 -7
  74. package/dist/cli/{providers-3NQRA7IT.mjs → providers-I6RQJZFZ.mjs} +4 -4
  75. package/dist/cli/{quickstart-PIA2JGPV.mjs → quickstart-BIDQDMO4.mjs} +3 -3
  76. package/dist/cli/{quickstart-R7NUZGNW.js → quickstart-GJ2RPDU3.js} +5 -5
  77. package/dist/cli/search-5VN4YD4I.js +27 -0
  78. package/dist/cli/{search-AIDIIGLY.mjs → search-ELRXFWG5.mjs} +5 -5
  79. package/dist/cli/{secrets-BT3PGMWN.js → secrets-53IT6VXD.js} +13 -13
  80. package/dist/cli/{secrets-OQNHL45O.mjs → secrets-SQXGZXIC.mjs} +4 -4
  81. package/dist/cli/{sessions-SF7WJPJP.mjs → sessions-2X5QF7SL.mjs} +4 -4
  82. package/dist/cli/{sessions-EYXWOSWK.js → sessions-CAV5M77Y.js} +5 -5
  83. package/dist/cli/{settings-BA3GHBOD.js → settings-532WNAZO.js} +74 -74
  84. package/dist/cli/{settings-Y57JTFDD.mjs → settings-7P67VGCJ.mjs} +7 -7
  85. package/dist/cli/{setup-RERXYC66.js → setup-6L2VSUWJ.js} +41 -41
  86. package/dist/cli/{setup-7GR5GBHA.mjs → setup-QGGBV2LE.mjs} +6 -6
  87. package/dist/cli/{skills-SJWIQAV5.mjs → skills-3FE62WBG.mjs} +4 -4
  88. package/dist/cli/skills-S6NPZAN7.js +28 -0
  89. package/dist/cli/tools-647RLXQS.js +41 -0
  90. package/dist/cli/{tools-ZF3JXPNL.mjs → tools-DMR25DEF.mjs} +7 -7
  91. package/dist/cli/{update-FFFYQAOJ.mjs → update-6RZMHJZP.mjs} +6 -6
  92. package/dist/cli/update-WVTSC3EM.js +28 -0
  93. package/dist/cli/{workflow-XVFEEGAS.mjs → workflow-L2DQRE6R.mjs} +5 -5
  94. package/dist/cli/{workflow-SZJVQH4T.js → workflow-TX4F2PIX.js} +15 -15
  95. package/dist/{client-DU5dtHDy.d.mts → client-BktIPdIj.d.mts} +1 -1
  96. package/dist/{client-mCE88hC4.d.ts → client-C3O1Ipf3.d.ts} +1 -1
  97. package/dist/{compiler-manifest-DE4RdAiY.d.ts → compiler-manifest-DLxUY5kI.d.mts} +65 -11
  98. package/dist/{compiler-manifest-DE4RdAiY.d.mts → compiler-manifest-DLxUY5kI.d.ts} +65 -11
  99. package/dist/{index-Bi-f7HFT.d.ts → index-B_GM9E3q.d.ts} +1 -1
  100. package/dist/{index-BI6nWgaV.d.mts → index-SCb0NvDb.d.mts} +1 -1
  101. package/dist/index.d.mts +5 -5
  102. package/dist/index.d.ts +5 -5
  103. package/dist/index.js +7 -7
  104. package/dist/index.mjs +4 -4
  105. package/dist/install-integrity.json +85 -84
  106. package/dist/plays/bundle-play-file.d.mts +3 -3
  107. package/dist/plays/bundle-play-file.d.ts +3 -3
  108. package/dist/plays/bundle-play-file.mjs +2 -2
  109. package/dist/plays/run-request.d.mts +2 -2
  110. package/dist/plays/run-request.d.ts +2 -2
  111. package/dist/release.d.mts +1 -1
  112. package/dist/release.d.ts +1 -1
  113. package/dist/release.js +2 -2
  114. package/dist/release.mjs +1 -1
  115. package/package.json +1 -1
  116. package/dist/cli/auth-P3MVDTXM.js +0 -39
  117. package/dist/cli/git-RNM45FX4.js +0 -23
  118. package/dist/cli/play-PDXR6JFB.js +0 -158
  119. package/dist/cli/search-QRWL7MFM.js +0 -27
  120. package/dist/cli/skills-V7EY75HB.js +0 -28
  121. package/dist/cli/tools-H5HLBCHH.js +0 -41
  122. package/dist/cli/update-472HA3LT.js +0 -28
@@ -13,6 +13,34 @@ export type DurableCallCachePolicy = {
13
13
  staleAfterSeconds: number | null;
14
14
  };
15
15
 
16
+ /** Future artifact semantics; legacy optional callers retain the resolver below. */
17
+ export function resolveExplicitDurableCallPolicy(input: {
18
+ staleAfterSeconds?: number | null;
19
+ force?: boolean;
20
+ }): { requested: number | null; effective: number | null } {
21
+ if (input.staleAfterSeconds === undefined) {
22
+ throw new Error(
23
+ 'Choose staleAfterSeconds explicitly: 0, a positive whole number of seconds, or null.',
24
+ );
25
+ }
26
+ resolveDurableCallCachePolicy(input.staleAfterSeconds);
27
+ if (
28
+ input.staleAfterSeconds !== null &&
29
+ !Number.isSafeInteger(input.staleAfterSeconds)
30
+ ) {
31
+ throw new Error(DURABLE_CALL_STALE_AFTER_SECONDS_ERROR);
32
+ }
33
+ if (input.force === true && input.staleAfterSeconds !== 0) {
34
+ throw new Error(
35
+ 'force: true contradicts reusable freshness. Use staleAfterSeconds: 0.',
36
+ );
37
+ }
38
+ return {
39
+ requested: input.staleAfterSeconds,
40
+ effective: input.staleAfterSeconds,
41
+ };
42
+ }
43
+
16
44
  /** Normalize the public freshness contract shared by durable calls. */
17
45
  export function resolveDurableCallCachePolicy(
18
46
  staleAfterSeconds?: number | null,
@@ -0,0 +1,251 @@
1
+ import { sha256Hex, stableStringify } from '../plays/row-identity';
2
+ import type { CallFreshnessMetadata } from '../plays/tool-result-types';
3
+ import type { RuntimeStepReceipt } from './ctx-types';
4
+ import {
5
+ COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
6
+ type DurableReceiptExecutionStore,
7
+ } from './durable-receipt-execution';
8
+ import { assertNoSecretTaint } from './secret-capability';
9
+ import {
10
+ deserializeToolExecuteResult,
11
+ isSerializedToolExecuteResult,
12
+ isToolExecuteResult,
13
+ relayPublishedToolResultReference,
14
+ serializeToolExecuteResult,
15
+ } from './tool-result';
16
+
17
+ type CompletedCall = {
18
+ completedAt: number;
19
+ value?: unknown;
20
+ /**
21
+ * Large tool results already live in their canonical result receipt. Keep a
22
+ * receipt-key indirection here instead of copying the full result into each
23
+ * operation and reusable freshness receipt.
24
+ */
25
+ valueReceiptKey?: string;
26
+ undefinedValue?: true;
27
+ };
28
+
29
+ type FreshnessStore = Pick<
30
+ DurableReceiptExecutionStore,
31
+ 'enabled' | 'get' | 'complete'
32
+ >;
33
+
34
+ function completedCall(
35
+ receipt: RuntimeStepReceipt | null,
36
+ ): CompletedCall | null {
37
+ if (!receipt || !['completed', 'skipped'].includes(receipt.status)) {
38
+ return null;
39
+ }
40
+ const output = receipt.output;
41
+ if (!output || typeof output !== 'object') {
42
+ throw new Error('Invalid completed freshness receipt.');
43
+ }
44
+ const candidate = output as Partial<CompletedCall>;
45
+ const hasInlineValue = 'value' in candidate;
46
+ const hasReceiptReference =
47
+ typeof candidate.valueReceiptKey === 'string' &&
48
+ candidate.valueReceiptKey.trim().length > 0;
49
+ if (
50
+ !Number.isSafeInteger(candidate.completedAt) ||
51
+ candidate.completedAt! < 0 ||
52
+ hasInlineValue === hasReceiptReference ||
53
+ (candidate.valueReceiptKey !== undefined && !hasReceiptReference) ||
54
+ (candidate.undefinedValue !== undefined &&
55
+ candidate.undefinedValue !== true) ||
56
+ (candidate.undefinedValue === true &&
57
+ (hasReceiptReference || candidate.value !== null))
58
+ ) {
59
+ throw new Error('Invalid completed freshness receipt.');
60
+ }
61
+ return candidate as CompletedCall;
62
+ }
63
+
64
+ /**
65
+ * Operation recovery is run-local. Reusable results are a separate completed
66
+ * cache. Adjacent partitions bound lookup, while admission checks actual age.
67
+ * A concurrent miss executes independently and publishes its own operation.
68
+ */
69
+ export async function executeWithFreshnessReceipt<T>(input: {
70
+ semanticKey: string;
71
+ orgId: string;
72
+ invocationId: string;
73
+ runId: string;
74
+ requestedStaleAfterSeconds: number | null;
75
+ effectiveStaleAfterSeconds: number | null;
76
+ store: FreshnessStore;
77
+ execute: () => Promise<T>;
78
+ completedAt?: (value: T) => number | undefined;
79
+ onOutcome: (value: T, metadata: CallFreshnessMetadata) => T;
80
+ withHydration: <Result>(
81
+ read: (
82
+ measure?: (receipt: RuntimeStepReceipt | null) => void,
83
+ ) => Promise<Result>,
84
+ ) => Promise<Result>;
85
+ now?: () => number;
86
+ }): Promise<T> {
87
+ if (!input.store.enabled) {
88
+ throw new Error(
89
+ 'Durable operation recovery is unavailable in this runtime.',
90
+ );
91
+ }
92
+ const now = input.now ?? Date.now;
93
+ const cachePrefix = `ctx:${input.orgId}:freshness:${sha256Hex(input.semanticKey)}`;
94
+ const operationKey = `ctx:${input.orgId}:freshness:operation:${sha256Hex(
95
+ stableStringify({ runId: input.runId, invocationId: input.invocationId }),
96
+ )}`;
97
+ const read = (key: string) =>
98
+ input.withHydration(async (measure) => {
99
+ const receipt = await input.store.get(key);
100
+ measure?.(receipt);
101
+ return completedCall(receipt);
102
+ });
103
+ const readReferencedResult = (key: string) =>
104
+ input.withHydration(async (measure) => {
105
+ const receipt = await input.store.get(key);
106
+ measure?.(receipt);
107
+ if (
108
+ !receipt ||
109
+ !['completed', 'skipped'].includes(receipt.status) ||
110
+ receipt.output === undefined
111
+ ) {
112
+ throw new Error(
113
+ 'Completed freshness result receipt could not be recovered.',
114
+ );
115
+ }
116
+ const value = receipt.output;
117
+ if (
118
+ !isSerializedToolExecuteResult(value) &&
119
+ !isToolExecuteResult(value)
120
+ ) {
121
+ throw new Error(
122
+ 'Completed freshness result receipt has an invalid tool result.',
123
+ );
124
+ }
125
+ return value;
126
+ });
127
+ const outcome = async (
128
+ call: CompletedCall,
129
+ source: CallFreshnessMetadata['source'],
130
+ liveResult?: { value: T },
131
+ ): Promise<T> => {
132
+ let value: unknown;
133
+ if (liveResult !== undefined) {
134
+ value = liveResult.value;
135
+ } else if (call.undefinedValue) {
136
+ value = undefined;
137
+ } else if (call.valueReceiptKey) {
138
+ value = await readReferencedResult(call.valueReceiptKey);
139
+ } else {
140
+ value = call.value;
141
+ }
142
+ value = isSerializedToolExecuteResult(value)
143
+ ? deserializeToolExecuteResult(value)
144
+ : value;
145
+ return input.onOutcome(value as T, {
146
+ requestedStaleAfterSeconds: input.requestedStaleAfterSeconds,
147
+ effectiveStaleAfterSeconds: input.effectiveStaleAfterSeconds,
148
+ source,
149
+ resultAgeMs: Math.max(0, now() - call.completedAt),
150
+ completedAt: call.completedAt,
151
+ operationReceiptKey: operationKey,
152
+ });
153
+ };
154
+ const publish = async (
155
+ key: string,
156
+ call: CompletedCall,
157
+ ): Promise<CompletedCall> => {
158
+ const receipt = await input.store.complete(
159
+ key,
160
+ input.runId,
161
+ call,
162
+ COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
163
+ );
164
+ const stored =
165
+ receipt?.output != null ? completedCall(receipt) : await read(key);
166
+ if (!stored) {
167
+ throw new Error('Completed freshness receipt publication failed.');
168
+ }
169
+ return stored;
170
+ };
171
+
172
+ const recovered = await read(operationKey);
173
+ if (recovered) return outcome(recovered, 'operation_recovery');
174
+
175
+ const ttl = input.effectiveStaleAfterSeconds;
176
+ const admissionTime = now();
177
+ const currentPartition =
178
+ ttl === null ? null : Math.floor(admissionTime / (ttl * 1000));
179
+ const reusableKey =
180
+ ttl === null
181
+ ? `${cachePrefix}:reusable:indefinite`
182
+ : `${cachePrefix}:reusable:${ttl}:${currentPartition}`;
183
+
184
+ if (ttl !== 0) {
185
+ const candidates =
186
+ ttl === null
187
+ ? [reusableKey]
188
+ : [
189
+ reusableKey,
190
+ `${cachePrefix}:reusable:${ttl}:${currentPartition! - 1}`,
191
+ ];
192
+ for (const key of candidates) {
193
+ const cached = await read(key);
194
+ if (!cached) continue;
195
+ const age = now() - cached.completedAt;
196
+ if (age < 0 || (ttl !== null && age >= ttl * 1000)) continue;
197
+ const own = await publish(operationKey, cached);
198
+ return outcome(own, 'reusable_result');
199
+ }
200
+ }
201
+
202
+ const value = await input.execute();
203
+ assertNoSecretTaint(value, 'Freshness call result');
204
+ const completedAt = input.completedAt?.(value) ?? now();
205
+ if (!Number.isSafeInteger(completedAt) || completedAt < 0) {
206
+ throw new Error('Freshness call returned an invalid completion time.');
207
+ }
208
+ const relayReference = isToolExecuteResult(value)
209
+ ? relayPublishedToolResultReference(value)
210
+ : null;
211
+ const resultReceiptKey =
212
+ isToolExecuteResult(value) && relayReference
213
+ ? value._metadata.execution.resultReceiptKey?.trim() ||
214
+ value._metadata.execution.receiptKey?.trim() ||
215
+ ''
216
+ : '';
217
+ if (relayReference && !resultReceiptKey) {
218
+ throw new Error(
219
+ 'Referenced freshness result has no canonical result receipt identity.',
220
+ );
221
+ }
222
+ const completed: CompletedCall = {
223
+ completedAt,
224
+ ...(value === undefined
225
+ ? { value: null, undefinedValue: true as const }
226
+ : resultReceiptKey
227
+ ? { valueReceiptKey: resultReceiptKey }
228
+ : {
229
+ value: isToolExecuteResult(value)
230
+ ? serializeToolExecuteResult(value)
231
+ : value,
232
+ }),
233
+ };
234
+
235
+ // Recover this exact operation even if reusable publication fails afterwards.
236
+ await publish(operationKey, completed);
237
+ if (ttl !== 0) {
238
+ const completionPartition =
239
+ ttl === null
240
+ ? 'indefinite'
241
+ : Math.floor(completed.completedAt / (ttl * 1000));
242
+ const resultKey =
243
+ ttl === null
244
+ ? `${cachePrefix}:reusable:indefinite`
245
+ : `${cachePrefix}:reusable:${ttl}:${completionPartition}`;
246
+ await publish(resultKey, completed);
247
+ }
248
+ // Each overlapping miss owns its live result even if the immutable receipt
249
+ // insert loses to a sibling. The stored winner is for later recovery.
250
+ return outcome(completed, 'executed', { value });
251
+ }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ EXPLICIT_FRESHNESS_PLAY_AUTHORING_THRESHOLD,
2
3
  normalizePlayAuthoringContractEdition,
3
4
  playToolListGetterMode,
4
5
  playCsvDatasetRowIdentity,
@@ -12,6 +13,9 @@ export type PlayRuntimeSecretValueSemantics =
12
13
  export type PlayRuntimeFetchHttpFailureSemantics =
13
14
  | 'return_response'
14
15
  | 'throw_typed_error';
16
+ export type PlayRuntimeFreshnessSemantics =
17
+ | 'legacy_bucket'
18
+ | 'actual_age_with_operation_recovery';
15
19
 
16
20
  /**
17
21
  * Immutable worker-side projection of the semantics pinned by a Play artifact.
@@ -36,6 +40,7 @@ export type PlayRuntimeSemantics = Readonly<{
36
40
  */
37
41
  receiptIdentityEdition: PlayAuthoringContractEdition | null;
38
42
  }>;
43
+ freshness: PlayRuntimeFreshnessSemantics;
39
44
  inlineChild: Readonly<{
40
45
  /**
41
46
  * Whether a scalar child may rely solely on its execution-time
@@ -56,7 +61,7 @@ export type PlayRuntimeSemantics = Readonly<{
56
61
 
57
62
  type PlayRuntimeSemanticsTemplate = Omit<
58
63
  PlayRuntimeSemantics,
59
- 'authoringContractEdition' | 'tools' | 'datasets'
64
+ 'authoringContractEdition' | 'freshness' | 'tools' | 'datasets'
60
65
  > & { tools: Omit<PlayRuntimeSemantics['tools'], 'listGetters'> };
61
66
 
62
67
  function definePlayRuntimeSemantics(
@@ -68,6 +73,11 @@ function definePlayRuntimeSemantics(
68
73
  ...semantics,
69
74
  secrets: Object.freeze({ ...semantics.secrets }),
70
75
  fetch: Object.freeze({ ...semantics.fetch }),
76
+ freshness:
77
+ semantics.authoringContractEdition >=
78
+ EXPLICIT_FRESHNESS_PLAY_AUTHORING_THRESHOLD
79
+ ? 'actual_age_with_operation_recovery'
80
+ : 'legacy_bucket',
71
81
  inlineChild: Object.freeze({ ...semantics.inlineChild }),
72
82
  tools: Object.freeze({
73
83
  ...semantics.tools,
@@ -210,6 +220,7 @@ const PLAY_RUNTIME_SEMANTICS_BY_AUTHORING = {
210
220
  fetch: {
211
221
  httpFailure: 'throw_typed_error',
212
222
  persistHttpFailure: false,
223
+ // Freshness policy does not create another fetch receipt identity.
213
224
  receiptIdentityEdition: FETCH_RECEIPT_IDENTITIES.typedHttpFailure,
214
225
  },
215
226
  inlineChild: { runtimeOnly: true },
@@ -357,6 +357,7 @@ export function playRunnerReceiptStoreAdapterOptions() {
357
357
  type RuntimeStepReceiptHandlers = RuntimeReceiptStoreHandlers;
358
358
 
359
359
  type RuntimeToolMetadata = {
360
+ reusableResult?: { supported: boolean; unavailableReason: string | null };
360
361
  provider?: string | null;
361
362
  operation?: string | null;
362
363
  extractors?: ToolResultMetadataInput['extractors'];
@@ -2510,6 +2511,15 @@ async function run(
2510
2511
  ? 'invocation'
2511
2512
  : null;
2512
2513
  },
2514
+ getToolReusableResultSupport: async (toolId) => {
2515
+ const support = (await getRuntimeToolMetadata(toolId)).reusableResult;
2516
+ if (!support) {
2517
+ throw new Error(
2518
+ `Tool ${toolId} has no reusable-result capability metadata. Deploy the runtime metadata reader before admitting explicit-freshness artifacts.`,
2519
+ );
2520
+ }
2521
+ return support;
2522
+ },
2513
2523
  getToolRetryPolicy: async (toolId) => {
2514
2524
  const metadata = await getRuntimeToolMetadata(toolId);
2515
2525
  return {
@@ -7037,10 +7037,13 @@ export async function startRuntimeSheetDataset(
7037
7037
  {
7038
7038
  row: Record<string, unknown>;
7039
7039
  inputIndex: number | null;
7040
+ sourceRowIndex: number | null;
7040
7041
  source: Record<string, unknown>;
7042
+ rowOffset: number;
7041
7043
  }
7042
7044
  >();
7043
- for (const row of input.rows) {
7045
+ for (let rowOffset = 0; rowOffset < input.rows.length; rowOffset += 1) {
7046
+ const row = input.rows[rowOffset]!;
7044
7047
  // Materializes projected CSV aliases as visible cells and drops internal
7045
7048
  // __deepline* keys — a plain spread would silently lose the
7046
7049
  // non-enumerable alias fields on the JSON payload boundary.
@@ -7048,31 +7051,37 @@ export async function startRuntimeSheetDataset(
7048
7051
  const key =
7049
7052
  resolveMapRowOutcomeKey(row) ??
7050
7053
  derivePlayRowIdentity(cleanedRow, input.tableNamespace);
7054
+ const source =
7055
+ row.__deeplineSourceRow && typeof row.__deeplineSourceRow === 'object'
7056
+ ? (sanitizePostgresJsonValue(row.__deeplineSourceRow) as Record<
7057
+ string,
7058
+ unknown
7059
+ >)
7060
+ : {};
7061
+ const sourceRowIndex = normalizeRuntimeMapInputIndex(source.rowIndex);
7051
7062
  if (key && !uniqueRows.has(key)) {
7052
7063
  uniqueRows.set(key, {
7053
7064
  row: sanitizePostgresJsonValue(cleanedRow),
7054
- source:
7055
- row.__deeplineSourceRow && typeof row.__deeplineSourceRow === 'object'
7056
- ? (sanitizePostgresJsonValue(row.__deeplineSourceRow) as Record<
7057
- string,
7058
- unknown
7059
- >)
7060
- : {},
7065
+ source,
7066
+ sourceRowIndex,
7061
7067
  inputIndex: normalizeRuntimeMapInputIndex(
7062
7068
  row[MAP_ROW_OUTCOME_RUNTIME_FIELDS.inputIndex],
7063
7069
  ),
7070
+ rowOffset,
7064
7071
  });
7065
7072
  }
7066
7073
  }
7067
7074
  const inputOffset = Math.max(0, Math.floor(input.inputOffset ?? 0));
7068
- const rowEntries = [...uniqueRows.entries()].map(
7069
- ([key, entry], inputIndex) => ({
7070
- key,
7071
- row: entry.row,
7072
- source: entry.source,
7073
- inputIndex: entry.inputIndex ?? inputOffset + inputIndex,
7074
- }),
7075
- );
7075
+ const rowEntries = [...uniqueRows.entries()].map(([key, entry]) => ({
7076
+ key,
7077
+ row: entry.row,
7078
+ source: entry.source,
7079
+ // A CSV transformation may filter or reorder the physical source rows.
7080
+ // Its source ordinal must win over the compacted input index stamped by
7081
+ // ctx.dataset while preparing this page.
7082
+ inputIndex:
7083
+ entry.sourceRowIndex ?? entry.inputIndex ?? inputOffset + entry.rowOffset,
7084
+ }));
7076
7085
  timings.push({
7077
7086
  phase: 'normalize_rows',
7078
7087
  ms: Date.now() - normalizeStartedAt,
@@ -3,6 +3,7 @@ import {
3
3
  RUNTIME_RECEIPT_OUTPUT_MAX_BYTES,
4
4
  } from './output-size-limits';
5
5
  export type {
6
+ CallFreshnessMetadata,
6
7
  SerializedToolExecuteResult,
7
8
  ToolExecuteResult,
8
9
  ToolResultBilling,