deepline 0.1.153 → 0.1.155
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.
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +15 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +1180 -825
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +34 -18
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +41 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +143 -8
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +104 -0
- package/dist/bundling-sources/sdk/src/index.ts +0 -1
- package/dist/bundling-sources/sdk/src/play.ts +3 -48
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +27 -2
- package/dist/bundling-sources/sdk/src/release.ts +6 -4
- package/dist/bundling-sources/sdk/src/worker-play-entry.ts +0 -10
- package/dist/bundling-sources/shared_libs/play-data-plane/index.ts +0 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +87 -0
- package/dist/bundling-sources/shared_libs/play-runtime/batch-runtime.ts +0 -59
- package/dist/bundling-sources/shared_libs/play-runtime/cell-staleness.ts +0 -253
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +805 -1570
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +47 -74
- package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +36 -14
- package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +284 -0
- package/dist/bundling-sources/shared_libs/play-runtime/postgres-json.ts +12 -5
- package/dist/bundling-sources/shared_libs/play-runtime/run-lifecycle-policy.ts +78 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +10 -45
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +923 -535
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +45 -76
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/step-program-dataset-builder.ts +1 -14
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +4 -1
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +32 -0
- package/dist/bundling-sources/shared_libs/plays/definition.ts +4 -2
- package/dist/bundling-sources/shared_libs/plays/runtime-validation.ts +3 -14
- package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +1 -43
- package/dist/cli/index.js +1305 -401
- package/dist/cli/index.mjs +1273 -363
- package/dist/{compiler-manifest-BjoRENv9.d.ts → compiler-manifest-DW1flrHk.d.mts} +0 -9
- package/dist/{compiler-manifest-BjoRENv9.d.mts → compiler-manifest-DW1flrHk.d.ts} +0 -9
- package/dist/index.d.mts +9 -38
- package/dist/index.d.ts +9 -38
- package/dist/index.js +26 -13
- package/dist/index.mjs +26 -13
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-data-plane/cell-policy.ts +0 -76
- package/dist/bundling-sources/shared_libs/play-runtime/progress-emitter.ts +0 -197
- package/dist/bundling-sources/shared_libs/play-runtime/waterfall-replay.ts +0 -79
|
@@ -169,6 +169,7 @@ type RuntimeApiRequest =
|
|
|
169
169
|
runId: string;
|
|
170
170
|
key: string;
|
|
171
171
|
reclaimRunning?: boolean;
|
|
172
|
+
forceRefresh?: boolean;
|
|
172
173
|
}
|
|
173
174
|
| {
|
|
174
175
|
action: 'complete_runtime_step_receipt';
|
|
@@ -190,6 +191,40 @@ type RuntimeApiRequest =
|
|
|
190
191
|
runId: string;
|
|
191
192
|
key: string;
|
|
192
193
|
output?: unknown;
|
|
194
|
+
}
|
|
195
|
+
| {
|
|
196
|
+
action: 'get_runtime_step_receipts';
|
|
197
|
+
playName: string;
|
|
198
|
+
runId: string;
|
|
199
|
+
keys: string[];
|
|
200
|
+
}
|
|
201
|
+
| {
|
|
202
|
+
action: 'claim_runtime_step_receipts';
|
|
203
|
+
playName: string;
|
|
204
|
+
runId: string;
|
|
205
|
+
keys: string[];
|
|
206
|
+
reclaimRunning?: boolean;
|
|
207
|
+
forceRefresh?: boolean;
|
|
208
|
+
}
|
|
209
|
+
| {
|
|
210
|
+
action: 'complete_runtime_step_receipts';
|
|
211
|
+
playName: string;
|
|
212
|
+
runId: string;
|
|
213
|
+
receipts: Array<{
|
|
214
|
+
key: string;
|
|
215
|
+
runId: string;
|
|
216
|
+
output: unknown;
|
|
217
|
+
}>;
|
|
218
|
+
}
|
|
219
|
+
| {
|
|
220
|
+
action: 'fail_runtime_step_receipts';
|
|
221
|
+
playName: string;
|
|
222
|
+
runId: string;
|
|
223
|
+
receipts: Array<{
|
|
224
|
+
key: string;
|
|
225
|
+
runId: string;
|
|
226
|
+
error: string;
|
|
227
|
+
}>;
|
|
193
228
|
};
|
|
194
229
|
|
|
195
230
|
export type WorkerRuntimeApiContext = {
|
|
@@ -789,6 +824,19 @@ export async function getRuntimeStepReceiptViaAppRuntime(
|
|
|
789
824
|
});
|
|
790
825
|
}
|
|
791
826
|
|
|
827
|
+
export async function getRuntimeStepReceiptsViaAppRuntime(
|
|
828
|
+
context: WorkerRuntimeApiContext,
|
|
829
|
+
input: Omit<
|
|
830
|
+
Extract<RuntimeApiRequest, { action: 'get_runtime_step_receipts' }>,
|
|
831
|
+
'action'
|
|
832
|
+
>,
|
|
833
|
+
): Promise<RuntimeStepReceipt[]> {
|
|
834
|
+
return await postAppRuntimeApi<RuntimeStepReceipt[]>(context, {
|
|
835
|
+
action: 'get_runtime_step_receipts',
|
|
836
|
+
...input,
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
|
|
792
840
|
export async function claimRuntimeStepReceiptViaAppRuntime(
|
|
793
841
|
context: WorkerRuntimeApiContext,
|
|
794
842
|
input: Omit<
|
|
@@ -802,6 +850,19 @@ export async function claimRuntimeStepReceiptViaAppRuntime(
|
|
|
802
850
|
});
|
|
803
851
|
}
|
|
804
852
|
|
|
853
|
+
export async function claimRuntimeStepReceiptsViaAppRuntime(
|
|
854
|
+
context: WorkerRuntimeApiContext,
|
|
855
|
+
input: Omit<
|
|
856
|
+
Extract<RuntimeApiRequest, { action: 'claim_runtime_step_receipts' }>,
|
|
857
|
+
'action'
|
|
858
|
+
>,
|
|
859
|
+
): Promise<RuntimeStepReceipt[]> {
|
|
860
|
+
return await postAppRuntimeApi<RuntimeStepReceipt[]>(context, {
|
|
861
|
+
action: 'claim_runtime_step_receipts',
|
|
862
|
+
...input,
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
805
866
|
export async function completeRuntimeStepReceiptViaAppRuntime(
|
|
806
867
|
context: WorkerRuntimeApiContext,
|
|
807
868
|
input: Omit<
|
|
@@ -815,6 +876,19 @@ export async function completeRuntimeStepReceiptViaAppRuntime(
|
|
|
815
876
|
});
|
|
816
877
|
}
|
|
817
878
|
|
|
879
|
+
export async function completeRuntimeStepReceiptsViaAppRuntime(
|
|
880
|
+
context: WorkerRuntimeApiContext,
|
|
881
|
+
input: Omit<
|
|
882
|
+
Extract<RuntimeApiRequest, { action: 'complete_runtime_step_receipts' }>,
|
|
883
|
+
'action'
|
|
884
|
+
>,
|
|
885
|
+
): Promise<RuntimeStepReceipt[]> {
|
|
886
|
+
return await postAppRuntimeApi<RuntimeStepReceipt[]>(context, {
|
|
887
|
+
action: 'complete_runtime_step_receipts',
|
|
888
|
+
...input,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
|
|
818
892
|
export async function failRuntimeStepReceiptViaAppRuntime(
|
|
819
893
|
context: WorkerRuntimeApiContext,
|
|
820
894
|
input: Omit<
|
|
@@ -828,6 +902,19 @@ export async function failRuntimeStepReceiptViaAppRuntime(
|
|
|
828
902
|
});
|
|
829
903
|
}
|
|
830
904
|
|
|
905
|
+
export async function failRuntimeStepReceiptsViaAppRuntime(
|
|
906
|
+
context: WorkerRuntimeApiContext,
|
|
907
|
+
input: Omit<
|
|
908
|
+
Extract<RuntimeApiRequest, { action: 'fail_runtime_step_receipts' }>,
|
|
909
|
+
'action'
|
|
910
|
+
>,
|
|
911
|
+
): Promise<RuntimeStepReceipt[]> {
|
|
912
|
+
return await postAppRuntimeApi<RuntimeStepReceipt[]>(context, {
|
|
913
|
+
action: 'fail_runtime_step_receipts',
|
|
914
|
+
...input,
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
831
918
|
export async function skipRuntimeStepReceiptViaAppRuntime(
|
|
832
919
|
context: WorkerRuntimeApiContext,
|
|
833
920
|
input: Omit<
|
|
@@ -148,62 +148,3 @@ export function compileRequestsWithStrategy<TRequest>(input: {
|
|
|
148
148
|
|
|
149
149
|
return compiledBatches;
|
|
150
150
|
}
|
|
151
|
-
|
|
152
|
-
export async function executeWaterfallProviders<TRequest, TResult>(input: {
|
|
153
|
-
providers: string[];
|
|
154
|
-
getPendingRequests: () => TRequest[];
|
|
155
|
-
getCachedResults: (
|
|
156
|
-
provider: string,
|
|
157
|
-
) => Array<ChunkExecutionResult<TRequest, TResult>> | null;
|
|
158
|
-
storeCachedResults: (
|
|
159
|
-
provider: string,
|
|
160
|
-
results: Array<ChunkExecutionResult<TRequest, TResult>>,
|
|
161
|
-
) => void;
|
|
162
|
-
executeProviderRequests: (
|
|
163
|
-
provider: string,
|
|
164
|
-
requests: TRequest[],
|
|
165
|
-
) => Promise<Array<ChunkExecutionResult<TRequest, TResult>>>;
|
|
166
|
-
onHit: (provider: string, request: TRequest, result: TResult) => void;
|
|
167
|
-
onMiss: (provider: string, request: TRequest) => void;
|
|
168
|
-
onProviderComplete?: (provider: string) => void;
|
|
169
|
-
}): Promise<void> {
|
|
170
|
-
for (const provider of input.providers) {
|
|
171
|
-
const cached = input.getCachedResults(provider);
|
|
172
|
-
if (cached) {
|
|
173
|
-
for (const entry of cached) {
|
|
174
|
-
if (entry.result != null) {
|
|
175
|
-
input.onHit(provider, entry.request, entry.result);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
input.onProviderComplete?.(provider);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const pending = input.getPendingRequests();
|
|
183
|
-
if (pending.length === 0) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const results = await input.executeProviderRequests(provider, pending);
|
|
188
|
-
input.storeCachedResults(provider, results);
|
|
189
|
-
|
|
190
|
-
for (const entry of results) {
|
|
191
|
-
if (entry.result != null) {
|
|
192
|
-
input.onHit(provider, entry.request, entry.result);
|
|
193
|
-
} else {
|
|
194
|
-
input.onMiss(provider, entry.request);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
input.onProviderComplete?.(provider);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function resolveWaterfallToolId(
|
|
203
|
-
provider: string,
|
|
204
|
-
toolName: string,
|
|
205
|
-
): string {
|
|
206
|
-
return toolName.startsWith(`${provider}_`)
|
|
207
|
-
? toolName
|
|
208
|
-
: `${provider}_${toolName}`;
|
|
209
|
-
}
|
|
@@ -1,33 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve the next expiry window from a completed cell value.
|
|
3
|
-
*
|
|
4
|
-
* Return a positive whole number of seconds to set the next `staleAt`, or
|
|
5
|
-
* `null` when this particular value should not expire.
|
|
6
|
-
*/
|
|
7
|
-
export type StaleAfterSecondsResolver<Value = unknown> = (
|
|
8
|
-
value: Value,
|
|
9
|
-
) => number | null;
|
|
10
|
-
|
|
11
|
-
/** Authored freshness policy: fixed TTL seconds or a value-based resolver. */
|
|
12
|
-
export type AuthoredStaleAfterSeconds<Value = unknown> =
|
|
13
|
-
| number
|
|
14
|
-
| StaleAfterSecondsResolver<Value>;
|
|
15
|
-
|
|
16
|
-
/** Freshness policy as written by play authors before runtime normalization. */
|
|
17
|
-
export type AuthoredCellStalenessPolicy<Value = unknown> = {
|
|
18
|
-
recompute?: boolean;
|
|
19
|
-
recomputeOnError?: boolean;
|
|
20
|
-
staleAfterSeconds?: AuthoredStaleAfterSeconds<Value>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/** Runtime-normalized freshness policy stored in execution plans. */
|
|
24
|
-
export type CellStalenessPolicy = {
|
|
25
|
-
recompute?: boolean;
|
|
26
|
-
recomputeOnError?: boolean;
|
|
27
|
-
staleAfterSeconds?: number;
|
|
28
|
-
dynamicStaleAfterSeconds?: boolean;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
1
|
/** Stored per-cell freshness and completion metadata. */
|
|
32
2
|
export type CellStalenessMeta = {
|
|
33
3
|
status?: string | null;
|
|
@@ -56,101 +26,8 @@ export type PreviousCell<Value = unknown> = {
|
|
|
56
26
|
staleAfterSeconds?: number;
|
|
57
27
|
};
|
|
58
28
|
|
|
59
|
-
export type CellStalenessDecision =
|
|
60
|
-
| {
|
|
61
|
-
action: 'recompute';
|
|
62
|
-
reason: 'missing' | 'failed' | 'forced' | 'stale';
|
|
63
|
-
}
|
|
64
|
-
| { action: 'reuse'; reason: 'fresh' | 'no_policy' | 'no_expiry' };
|
|
65
|
-
|
|
66
|
-
export type CellStalenessPolicyByField = Record<string, CellStalenessPolicy>;
|
|
67
|
-
export type AuthoredCellStalenessPolicyByField = Record<
|
|
68
|
-
string,
|
|
69
|
-
AuthoredCellStalenessPolicy
|
|
70
|
-
>;
|
|
71
|
-
|
|
72
29
|
export const DEEPLINE_CELL_META_FIELD = '__deeplineCellMeta';
|
|
73
30
|
|
|
74
|
-
export function validateStaleAfterSeconds(
|
|
75
|
-
staleAfterSeconds: number | undefined,
|
|
76
|
-
label = 'staleAfterSeconds',
|
|
77
|
-
): void {
|
|
78
|
-
if (staleAfterSeconds === undefined) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (
|
|
82
|
-
!Number.isFinite(staleAfterSeconds) ||
|
|
83
|
-
!Number.isInteger(staleAfterSeconds) ||
|
|
84
|
-
staleAfterSeconds <= 0
|
|
85
|
-
) {
|
|
86
|
-
throw new Error(`${label} must be a positive whole number of seconds.`);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function normalizeCellStalenessPolicy(
|
|
91
|
-
policy: AuthoredCellStalenessPolicy | undefined,
|
|
92
|
-
): CellStalenessPolicy {
|
|
93
|
-
if (
|
|
94
|
-
policy?.recompute !== undefined &&
|
|
95
|
-
typeof policy.recompute !== 'boolean'
|
|
96
|
-
) {
|
|
97
|
-
throw new Error('recompute must be a boolean.');
|
|
98
|
-
}
|
|
99
|
-
if (
|
|
100
|
-
policy?.recomputeOnError !== undefined &&
|
|
101
|
-
typeof policy.recomputeOnError !== 'boolean'
|
|
102
|
-
) {
|
|
103
|
-
throw new Error('recomputeOnError must be a boolean.');
|
|
104
|
-
}
|
|
105
|
-
const recompute = policy?.recompute === true;
|
|
106
|
-
const recomputeOnError = policy?.recomputeOnError === true;
|
|
107
|
-
const flags = {
|
|
108
|
-
...(recompute ? { recompute: true } : {}),
|
|
109
|
-
...(recomputeOnError ? { recomputeOnError: true } : {}),
|
|
110
|
-
};
|
|
111
|
-
const staleAfterSeconds = policy?.staleAfterSeconds;
|
|
112
|
-
if (staleAfterSeconds === undefined) {
|
|
113
|
-
return flags;
|
|
114
|
-
}
|
|
115
|
-
if (typeof staleAfterSeconds === 'function') {
|
|
116
|
-
return {
|
|
117
|
-
...flags,
|
|
118
|
-
dynamicStaleAfterSeconds: true,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
validateStaleAfterSeconds(staleAfterSeconds);
|
|
122
|
-
return { ...flags, staleAfterSeconds };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function resolveCompletedCellStalenessMeta<Value>(input: {
|
|
126
|
-
policy?: AuthoredCellStalenessPolicy<Value>;
|
|
127
|
-
value: Value;
|
|
128
|
-
completedAt: number;
|
|
129
|
-
}): Pick<CellStalenessMeta, 'staleAfterSeconds' | 'staleAt'> {
|
|
130
|
-
const staleAfterSeconds = input.policy?.staleAfterSeconds;
|
|
131
|
-
if (staleAfterSeconds === undefined) {
|
|
132
|
-
return {};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const resolved =
|
|
136
|
-
typeof staleAfterSeconds === 'function'
|
|
137
|
-
? staleAfterSeconds(input.value)
|
|
138
|
-
: staleAfterSeconds;
|
|
139
|
-
if (resolved === null) {
|
|
140
|
-
return { staleAt: null };
|
|
141
|
-
}
|
|
142
|
-
if (typeof resolved !== 'number') {
|
|
143
|
-
throw new Error(
|
|
144
|
-
'staleAfterSeconds(value) must return a positive whole number of seconds or null.',
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
validateStaleAfterSeconds(resolved, 'staleAfterSeconds(value)');
|
|
148
|
-
return {
|
|
149
|
-
staleAfterSeconds: resolved,
|
|
150
|
-
staleAt: input.completedAt + resolved * 1000,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
31
|
export function previousCellFromValue<Value>(input: {
|
|
155
32
|
hasValue: boolean;
|
|
156
33
|
value: Value;
|
|
@@ -187,133 +64,3 @@ export function previousCellFromValue<Value>(input: {
|
|
|
187
64
|
}
|
|
188
65
|
return previous;
|
|
189
66
|
}
|
|
190
|
-
|
|
191
|
-
export function shouldRecomputeCell(input: {
|
|
192
|
-
hasValue: boolean;
|
|
193
|
-
value?: unknown;
|
|
194
|
-
meta?: CellStalenessMeta | null;
|
|
195
|
-
policy?: CellStalenessPolicy;
|
|
196
|
-
nowMs?: number;
|
|
197
|
-
}): CellStalenessDecision {
|
|
198
|
-
if (!input.hasValue) {
|
|
199
|
-
return { action: 'recompute', reason: 'missing' };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const status = String(input.meta?.status ?? '').trim();
|
|
203
|
-
if (status === 'failed') {
|
|
204
|
-
return { action: 'recompute', reason: 'failed' };
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (input.policy?.recompute === true) {
|
|
208
|
-
return { action: 'recompute', reason: 'forced' };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
input.policy?.recomputeOnError === true &&
|
|
213
|
-
isErrorLikeCellValue(input.value)
|
|
214
|
-
) {
|
|
215
|
-
return { action: 'recompute', reason: 'failed' };
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const staleAt =
|
|
219
|
-
input.meta && Object.prototype.hasOwnProperty.call(input.meta, 'staleAt')
|
|
220
|
-
? input.meta.staleAt
|
|
221
|
-
: undefined;
|
|
222
|
-
if (staleAt === null) {
|
|
223
|
-
return { action: 'reuse', reason: 'no_expiry' };
|
|
224
|
-
}
|
|
225
|
-
if (typeof staleAt === 'number' && Number.isFinite(staleAt)) {
|
|
226
|
-
return (input.nowMs ?? Date.now()) > staleAt
|
|
227
|
-
? { action: 'recompute', reason: 'stale' }
|
|
228
|
-
: { action: 'reuse', reason: 'fresh' };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (input.policy?.dynamicStaleAfterSeconds) {
|
|
232
|
-
return { action: 'recompute', reason: 'stale' };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const staleAfterSeconds = input.policy?.staleAfterSeconds;
|
|
236
|
-
validateStaleAfterSeconds(staleAfterSeconds);
|
|
237
|
-
if (staleAfterSeconds === undefined) {
|
|
238
|
-
return { action: 'reuse', reason: 'no_policy' };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const completedAt =
|
|
242
|
-
typeof input.meta?.completedAt === 'number' &&
|
|
243
|
-
Number.isFinite(input.meta.completedAt)
|
|
244
|
-
? input.meta.completedAt
|
|
245
|
-
: null;
|
|
246
|
-
if (completedAt === null) {
|
|
247
|
-
return { action: 'recompute', reason: 'missing' };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const ageMs = Math.max(0, (input.nowMs ?? Date.now()) - completedAt);
|
|
251
|
-
return ageMs > staleAfterSeconds * 1000
|
|
252
|
-
? { action: 'recompute', reason: 'stale' }
|
|
253
|
-
: { action: 'reuse', reason: 'fresh' };
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function resolveReusableCellMetaForCurrentPolicy<Value>(input: {
|
|
257
|
-
hasValue: boolean;
|
|
258
|
-
value: Value;
|
|
259
|
-
meta?: CellStalenessMeta | null;
|
|
260
|
-
policy?: AuthoredCellStalenessPolicy<Value>;
|
|
261
|
-
}): Pick<CellStalenessMeta, 'staleAfterSeconds' | 'staleAt'> {
|
|
262
|
-
if (
|
|
263
|
-
!input.hasValue ||
|
|
264
|
-
typeof input.policy?.staleAfterSeconds !== 'function'
|
|
265
|
-
) {
|
|
266
|
-
return {};
|
|
267
|
-
}
|
|
268
|
-
const status = String(input.meta?.status ?? '').trim();
|
|
269
|
-
if (status === 'failed') {
|
|
270
|
-
return {};
|
|
271
|
-
}
|
|
272
|
-
const completedAt =
|
|
273
|
-
typeof input.meta?.completedAt === 'number' &&
|
|
274
|
-
Number.isFinite(input.meta.completedAt)
|
|
275
|
-
? input.meta.completedAt
|
|
276
|
-
: null;
|
|
277
|
-
if (completedAt === null) {
|
|
278
|
-
return {};
|
|
279
|
-
}
|
|
280
|
-
return resolveCompletedCellStalenessMeta({
|
|
281
|
-
policy: input.policy,
|
|
282
|
-
value: input.value,
|
|
283
|
-
completedAt,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function cellPolicyFields(
|
|
288
|
-
policies: CellStalenessPolicyByField | undefined,
|
|
289
|
-
): string[] {
|
|
290
|
-
if (!policies) {
|
|
291
|
-
return [];
|
|
292
|
-
}
|
|
293
|
-
return Object.entries(policies)
|
|
294
|
-
.filter(
|
|
295
|
-
([, policy]) =>
|
|
296
|
-
policy.staleAfterSeconds !== undefined ||
|
|
297
|
-
policy.dynamicStaleAfterSeconds === true ||
|
|
298
|
-
policy.recompute === true ||
|
|
299
|
-
policy.recomputeOnError === true,
|
|
300
|
-
)
|
|
301
|
-
.map(([field]) => field);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function isErrorLikeCellValue(value: unknown): boolean {
|
|
305
|
-
if (typeof value === 'string') {
|
|
306
|
-
return /(^|: )error(:|$)/i.test(value.trim());
|
|
307
|
-
}
|
|
308
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
const record = value as Record<string, unknown>;
|
|
312
|
-
const raw = (record.toolResponse as { raw?: unknown } | undefined)?.raw;
|
|
313
|
-
return (
|
|
314
|
-
(raw !== undefined && isErrorLikeCellValue(raw)) ||
|
|
315
|
-
(typeof record.error === 'string' && record.error.trim().length > 0) ||
|
|
316
|
-
record.status === 'failed' ||
|
|
317
|
-
record.status === 'error'
|
|
318
|
-
);
|
|
319
|
-
}
|