deepline 0.3.289 → 0.3.291
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/sdk/src/client.ts +8 -6
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/sdk/src/runs/overview.ts +16 -6
- package/dist/bundling-sources/sdk/src/types.ts +21 -2
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +41 -11
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +317 -63
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -2
- package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/launch-identity.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-context.ts +36 -0
- package/dist/bundling-sources/shared_libs/play-runtime/log-provenance.ts +19 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +6 -5
- package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +12 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-event-catalog.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +74 -33
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +73 -3
- package/dist/bundling-sources/shared_libs/play-runtime/run-log-buffer.ts +39 -2
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +72 -9
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +77 -10
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool-options.ts +145 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +17 -9
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +8 -139
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +19 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +176 -18
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +7 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +1404 -478
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +55 -25
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-dispatch-authority.ts +97 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +34 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-event-search.ts +558 -57
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-value-storage.ts +10 -1
- package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/test-admission-batching.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +22 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +35 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +68 -8
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +75 -28
- package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +2 -0
- package/dist/cli/index.js +195 -582
- package/dist/cli/index.mjs +195 -582
- package/dist/{client-FR_u0dxT.d.ts → client-Ct4jmvAg.d.ts} +29 -8
- package/dist/{client-phK1Ixcl.d.mts → client-Ct9GKrwu.d.mts} +29 -8
- package/dist/{compiler-manifest-9SQtUuKb.d.mts → compiler-manifest-qtTNYvBl.d.mts} +2 -0
- package/dist/{compiler-manifest-9SQtUuKb.d.ts → compiler-manifest-qtTNYvBl.d.ts} +2 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +14 -5
- package/dist/index.mjs +14 -5
- package/dist/install-integrity.json +7 -4
- package/dist/plays/bundle-play-file.d.mts +2 -2
- package/dist/plays/bundle-play-file.d.ts +2 -2
- package/dist/plays/run-request.d.mts +2 -2
- package/dist/plays/run-request.d.ts +2 -2
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -85,6 +85,8 @@ type RuntimeReceiptValueStorageInput = {
|
|
|
85
85
|
inlineFields?: RuntimeReceiptValueInlineFields;
|
|
86
86
|
inlineMaxBytes?: number;
|
|
87
87
|
store?: RuntimeReceiptValueBlobStore;
|
|
88
|
+
/** Consult the durable writer fence only when this value needs a reference. */
|
|
89
|
+
referencesWritable?: () => Promise<boolean>;
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
export type PersistedRuntimeReceiptValue =
|
|
@@ -206,7 +208,10 @@ export async function persistRuntimeReceiptValue(
|
|
|
206
208
|
input.inlineFields === undefined
|
|
207
209
|
? undefined
|
|
208
210
|
: normalizeRuntimeReceiptValueInlineFields(input.inlineFields);
|
|
209
|
-
if (
|
|
211
|
+
if (
|
|
212
|
+
body.byteLength <= inlineMaxBytes ||
|
|
213
|
+
(input.referencesWritable && !(await input.referencesWritable()))
|
|
214
|
+
) {
|
|
210
215
|
return { kind: 'inline', outputJson: input.serialized };
|
|
211
216
|
}
|
|
212
217
|
|
|
@@ -242,6 +247,7 @@ export function createRuntimeReceiptValuePersister(
|
|
|
242
247
|
input: {
|
|
243
248
|
store?: RuntimeReceiptValueBlobStore;
|
|
244
249
|
inlineMaxBytes?: number;
|
|
250
|
+
referencesWritable?: () => Promise<boolean>;
|
|
245
251
|
} = {},
|
|
246
252
|
): RuntimeReceiptValuePersister {
|
|
247
253
|
const inlineMaxBytes =
|
|
@@ -278,6 +284,9 @@ export function createRuntimeReceiptValuePersister(
|
|
|
278
284
|
...value,
|
|
279
285
|
inlineMaxBytes,
|
|
280
286
|
...(input.store ? { store: input.store } : {}),
|
|
287
|
+
...(input.referencesWritable
|
|
288
|
+
? { referencesWritable: input.referencesWritable }
|
|
289
|
+
: {}),
|
|
281
290
|
}),
|
|
282
291
|
};
|
|
283
292
|
} catch (error) {
|
|
@@ -45,6 +45,8 @@ export type PlayVisualNodeProgress = {
|
|
|
45
45
|
completedAt?: number | null;
|
|
46
46
|
artifactTableNamespace?: string | null;
|
|
47
47
|
nodeIo?: PlayDocflowNodeIoState;
|
|
48
|
+
/** Error reported by this step, distinct from a run stopping other work. */
|
|
49
|
+
error?: string;
|
|
48
50
|
conditionCounts?: PlayConditionCounts;
|
|
49
51
|
};
|
|
50
52
|
|
|
@@ -193,6 +195,9 @@ export function normalizePlayVisualNodeProgressMap(
|
|
|
193
195
|
...(startedAt !== undefined ? { startedAt } : {}),
|
|
194
196
|
...(completedAt !== undefined ? { completedAt } : {}),
|
|
195
197
|
...(message !== undefined ? { message } : {}),
|
|
198
|
+
...(typeof rawProgress.error === 'string' && rawProgress.error.trim()
|
|
199
|
+
? { error: rawProgress.error.trim() }
|
|
200
|
+
: {}),
|
|
196
201
|
...(artifactTableNamespace !== undefined
|
|
197
202
|
? { artifactTableNamespace }
|
|
198
203
|
: {}),
|
|
@@ -305,7 +310,7 @@ export function applyVisualNodeProgress(input: {
|
|
|
305
310
|
const completed = Number(progress.completed ?? 0);
|
|
306
311
|
const total = Number(progress.total ?? 0);
|
|
307
312
|
const progressStatus =
|
|
308
|
-
nodeState.status === 'failed'
|
|
313
|
+
nodeState.status === 'failed' || progress.error || progress.nodeIo?.error
|
|
309
314
|
? 'failed'
|
|
310
315
|
: Number.isFinite(total) && total > 0
|
|
311
316
|
? Number.isFinite(completed) && completed >= total
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineBatchStrategyMap,
|
|
3
|
+
type BatchOperationStrategy,
|
|
4
|
+
} from './batching-types';
|
|
5
|
+
|
|
6
|
+
type Single = {
|
|
7
|
+
key: string;
|
|
8
|
+
simulated_delay_ms?: number;
|
|
9
|
+
overlap_group?: string;
|
|
10
|
+
};
|
|
11
|
+
type Result = {
|
|
12
|
+
key: string;
|
|
13
|
+
physical_id: string;
|
|
14
|
+
batch_size: number;
|
|
15
|
+
started_at_ms: number;
|
|
16
|
+
finished_at_ms: number;
|
|
17
|
+
};
|
|
18
|
+
type Bulk = {
|
|
19
|
+
items: Array<{ key: string }>;
|
|
20
|
+
simulated_delay_ms?: number;
|
|
21
|
+
overlap_group?: string;
|
|
22
|
+
};
|
|
23
|
+
const bucket = (input: Single) =>
|
|
24
|
+
JSON.stringify([input.simulated_delay_ms ?? 0, input.overlap_group ?? null]);
|
|
25
|
+
|
|
26
|
+
const strategy: BatchOperationStrategy<
|
|
27
|
+
Single,
|
|
28
|
+
Single | Bulk,
|
|
29
|
+
Result | { items: Result[] },
|
|
30
|
+
Result
|
|
31
|
+
> = {
|
|
32
|
+
sourceOperation: 'test_batching_single',
|
|
33
|
+
batchOperation: 'test_batching_bulk',
|
|
34
|
+
kind: 'identifier_batch',
|
|
35
|
+
maxBatchSize: 100,
|
|
36
|
+
bucketKeyPayloadFields: ['simulated_delay_ms', 'overlap_group'],
|
|
37
|
+
toBucketKey: bucket,
|
|
38
|
+
toItemKey: (input) => input.key,
|
|
39
|
+
canBatchWith: (left, right) => bucket(left) === bucket(right),
|
|
40
|
+
compile(inputs) {
|
|
41
|
+
const first = inputs[0];
|
|
42
|
+
if (
|
|
43
|
+
!first ||
|
|
44
|
+
inputs.length > 100 ||
|
|
45
|
+
inputs.some((input) => !input.key || bucket(input) !== bucket(first))
|
|
46
|
+
) {
|
|
47
|
+
throw new Error('Synthetic batch needs 1–100 compatible keyed members.');
|
|
48
|
+
}
|
|
49
|
+
const keys = [...new Set(inputs.map((input) => input.key))];
|
|
50
|
+
return {
|
|
51
|
+
batchOperation:
|
|
52
|
+
keys.length === 1 ? 'test_batching_single' : 'test_batching_bulk',
|
|
53
|
+
batchPayload:
|
|
54
|
+
keys.length === 1
|
|
55
|
+
? first
|
|
56
|
+
: {
|
|
57
|
+
items: keys.map((key) => ({ key })),
|
|
58
|
+
simulated_delay_ms: first.simulated_delay_ms,
|
|
59
|
+
...(first.overlap_group
|
|
60
|
+
? { overlap_group: first.overlap_group }
|
|
61
|
+
: {}),
|
|
62
|
+
},
|
|
63
|
+
items: inputs.map((payload) => ({ itemKey: payload.key, payload })),
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
splitResult(result, compiled) {
|
|
67
|
+
const rows =
|
|
68
|
+
compiled.batchOperation === 'test_batching_single'
|
|
69
|
+
? [result as Result]
|
|
70
|
+
: (result as { items: Result[] }).items;
|
|
71
|
+
if (!Array.isArray(rows))
|
|
72
|
+
throw new Error('Synthetic batch omitted its result list.');
|
|
73
|
+
const byKey = new Map<string, Result>();
|
|
74
|
+
const expected = new Set(compiled.items.map((item) => item.itemKey));
|
|
75
|
+
for (const row of rows) {
|
|
76
|
+
if (
|
|
77
|
+
!row ||
|
|
78
|
+
!expected.has(row.key) ||
|
|
79
|
+
byKey.has(row.key) ||
|
|
80
|
+
typeof row.physical_id !== 'string' ||
|
|
81
|
+
row.physical_id.length === 0 ||
|
|
82
|
+
row.batch_size !== expected.size ||
|
|
83
|
+
!Number.isInteger(row.started_at_ms) ||
|
|
84
|
+
row.started_at_ms < 0 ||
|
|
85
|
+
!Number.isInteger(row.finished_at_ms) ||
|
|
86
|
+
row.finished_at_ms < row.started_at_ms
|
|
87
|
+
) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
'Synthetic batch returned a malformed or unexpected member.',
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
byKey.set(row.key, row);
|
|
93
|
+
}
|
|
94
|
+
return compiled.items.map((item) => {
|
|
95
|
+
const row = byKey.get(item.itemKey);
|
|
96
|
+
if (!row) throw new Error('Synthetic batch omitted a member.');
|
|
97
|
+
return { itemKey: item.itemKey, result: row, rawResult: row };
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
export const testAdmissionBatchStrategies = defineBatchStrategyMap({
|
|
102
|
+
test_batching_single: strategy,
|
|
103
|
+
});
|
package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts
CHANGED
|
@@ -349,10 +349,14 @@ export function createRuntimeStepToolResultReceipts(
|
|
|
349
349
|
// exists to cap how many stored payloads expand at once; holding one
|
|
350
350
|
// of its slots across this gateway round trip only queued unrelated
|
|
351
351
|
// rows behind this row's write, so the mutation runs outside it.
|
|
352
|
+
const storedResult = stampResultReceipt(
|
|
353
|
+
candidate.result,
|
|
354
|
+
record.resultReceiptKey,
|
|
355
|
+
);
|
|
352
356
|
const persisted = await input.store.complete(
|
|
353
357
|
record.resultReceiptKey,
|
|
354
358
|
ownerRunId,
|
|
355
|
-
serializeToolExecuteResult(
|
|
359
|
+
serializeToolExecuteResult(storedResult),
|
|
356
360
|
COMPLETED_RECEIPT_CACHE_INSERT_LEASE_ID,
|
|
357
361
|
);
|
|
358
362
|
// Publication is immutable, but an independent miss keeps the
|
|
@@ -369,10 +373,10 @@ export function createRuntimeStepToolResultReceipts(
|
|
|
369
373
|
}
|
|
370
374
|
}
|
|
371
375
|
await input.onStored?.({
|
|
372
|
-
result:
|
|
376
|
+
result: storedResult,
|
|
373
377
|
resultReceiptKey: record.resultReceiptKey,
|
|
374
378
|
});
|
|
375
|
-
return { kind: 'stored', result:
|
|
379
|
+
return { kind: 'stored', result: storedResult };
|
|
376
380
|
} finally {
|
|
377
381
|
await stopHeartbeat(record);
|
|
378
382
|
await releaseExecutionLock(record);
|
|
@@ -394,6 +398,16 @@ export function createRuntimeStepToolResultReceipts(
|
|
|
394
398
|
};
|
|
395
399
|
}
|
|
396
400
|
|
|
401
|
+
function stampResultReceipt(
|
|
402
|
+
result: ToolExecuteResult,
|
|
403
|
+
resultReceiptKey: string,
|
|
404
|
+
): ToolExecuteResult {
|
|
405
|
+
return deserializeToolExecuteResult({
|
|
406
|
+
...serializeToolExecuteResult(result),
|
|
407
|
+
execution: { ...result._metadata.execution, resultReceiptKey },
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
397
411
|
function completedResult(
|
|
398
412
|
receipt: RuntimeStepReceipt | null,
|
|
399
413
|
resultReceiptKey: string,
|
|
@@ -402,10 +416,13 @@ function completedResult(
|
|
|
402
416
|
return null;
|
|
403
417
|
}
|
|
404
418
|
if (isSerializedToolExecuteResult(receipt.output)) {
|
|
405
|
-
return
|
|
419
|
+
return stampResultReceipt(
|
|
420
|
+
deserializeToolExecuteResult(receipt.output),
|
|
421
|
+
resultReceiptKey,
|
|
422
|
+
);
|
|
406
423
|
}
|
|
407
424
|
if (isToolExecuteResult(receipt.output)) {
|
|
408
|
-
return receipt.output;
|
|
425
|
+
return stampResultReceipt(receipt.output, resultReceiptKey);
|
|
409
426
|
}
|
|
410
427
|
throw new Error(
|
|
411
428
|
`Completed Tool Result Receipt ${resultReceiptKey} has no valid serialized ToolExecuteResult.`,
|
|
@@ -12,6 +12,41 @@ import {
|
|
|
12
12
|
} from '../plays/tool-execution-error';
|
|
13
13
|
|
|
14
14
|
const TOOL_HTTP_ERROR_BRAND = Symbol.for('deepline.tool-http-error.v1');
|
|
15
|
+
const callEvidenceByError = new WeakMap<object, unknown>();
|
|
16
|
+
|
|
17
|
+
/** Payload evidence stays out of the scalar portable error/status contract. */
|
|
18
|
+
export function attachToolCallFailureEvidence(
|
|
19
|
+
error: object,
|
|
20
|
+
evidence: unknown,
|
|
21
|
+
): void {
|
|
22
|
+
callEvidenceByError.set(error, evidence);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Read the already consumed error body without changing failure classification. */
|
|
26
|
+
export function attachToolCallFailureEvidenceFromResponse(
|
|
27
|
+
error: object,
|
|
28
|
+
body: string,
|
|
29
|
+
): void {
|
|
30
|
+
let response: unknown;
|
|
31
|
+
try {
|
|
32
|
+
response = JSON.parse(body);
|
|
33
|
+
} catch {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!response || typeof response !== 'object') return;
|
|
37
|
+
const meta = (response as Record<string, unknown>).meta;
|
|
38
|
+
if (!meta || typeof meta !== 'object') return;
|
|
39
|
+
attachToolCallFailureEvidence(
|
|
40
|
+
error,
|
|
41
|
+
(meta as Record<string, unknown>).callEvidence,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function readToolCallFailureEvidence(error: unknown): unknown {
|
|
46
|
+
return error && typeof error === 'object'
|
|
47
|
+
? callEvidenceByError.get(error)
|
|
48
|
+
: undefined;
|
|
49
|
+
}
|
|
15
50
|
|
|
16
51
|
type ToolHttpErrorConstructorInput = {
|
|
17
52
|
message: string;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
jsonByteLengthUpTo,
|
|
3
|
+
RUNTIME_RECEIPT_OUTPUT_MAX_BYTES,
|
|
4
|
+
} from './output-size-limits';
|
|
1
5
|
export type {
|
|
2
6
|
SerializedToolExecuteResult,
|
|
3
7
|
ToolExecuteResult,
|
|
@@ -83,6 +87,11 @@ const relayPublishedResultReferences = new WeakMap<
|
|
|
83
87
|
RuntimeReceiptValueReference
|
|
84
88
|
>();
|
|
85
89
|
|
|
90
|
+
// Call evidence belongs to the saved per-call payload. Keep it out of the
|
|
91
|
+
// enumerable Play result so returning a tool result or storing it in a row
|
|
92
|
+
// does not enlarge customer output with diagnostic request bodies.
|
|
93
|
+
const savedToolCallEvidence = new WeakMap<object, unknown>();
|
|
94
|
+
|
|
86
95
|
export function attachRelayPublishedToolResultReference(
|
|
87
96
|
result: ToolExecuteResult,
|
|
88
97
|
reference: RuntimeReceiptValueReference,
|
|
@@ -1271,6 +1280,7 @@ export function createToolExecuteResult<TResult = unknown>(input: {
|
|
|
1271
1280
|
meta?: Record<string, unknown>;
|
|
1272
1281
|
};
|
|
1273
1282
|
}): ToolExecuteResult<TResult> {
|
|
1283
|
+
const { callEvidence, ...publicMeta } = input.meta ?? {};
|
|
1274
1284
|
const result = toResultEnvelope(input.result);
|
|
1275
1285
|
const resultRoot = {
|
|
1276
1286
|
toolResponse: {
|
|
@@ -1320,7 +1330,7 @@ export function createToolExecuteResult<TResult = unknown>(input: {
|
|
|
1320
1330
|
const wrapper = {
|
|
1321
1331
|
status: input.status,
|
|
1322
1332
|
...(input.jobId ? { job_id: input.jobId } : {}),
|
|
1323
|
-
...(input.meta ? { meta:
|
|
1333
|
+
...(input.meta ? { meta: publicMeta } : {}),
|
|
1324
1334
|
...(input.billing ? { billing: input.billing } : {}),
|
|
1325
1335
|
toolResponse,
|
|
1326
1336
|
extractedValues,
|
|
@@ -1336,6 +1346,9 @@ export function createToolExecuteResult<TResult = unknown>(input: {
|
|
|
1336
1346
|
value: metadata,
|
|
1337
1347
|
enumerable: false,
|
|
1338
1348
|
});
|
|
1349
|
+
if (callEvidence !== undefined) {
|
|
1350
|
+
savedToolCallEvidence.set(wrapper, callEvidence);
|
|
1351
|
+
}
|
|
1339
1352
|
return wrapper;
|
|
1340
1353
|
}
|
|
1341
1354
|
|
|
@@ -1976,9 +1989,50 @@ function rawV2WithSerializedListRows(input: {
|
|
|
1976
1989
|
});
|
|
1977
1990
|
}
|
|
1978
1991
|
|
|
1992
|
+
/** Optional call evidence cannot turn a valid provider result into a retry. */
|
|
1993
|
+
function fitSavedCallEvidence(
|
|
1994
|
+
value: SerializedToolExecuteResult,
|
|
1995
|
+
): SerializedToolExecuteResult {
|
|
1996
|
+
if (
|
|
1997
|
+
!value.meta?.callEvidence ||
|
|
1998
|
+
!jsonByteLengthUpTo(value, RUNTIME_RECEIPT_OUTPUT_MAX_BYTES).exceeded
|
|
1999
|
+
)
|
|
2000
|
+
return value;
|
|
2001
|
+
const { callEvidence: _evidence, ...meta } = value.meta;
|
|
2002
|
+
const withoutEvidence = { ...value, meta };
|
|
2003
|
+
// Preserve the original oversized-output failure if the result itself is
|
|
2004
|
+
// invalid. Only newly added diagnostic evidence may be omitted.
|
|
2005
|
+
if (
|
|
2006
|
+
jsonByteLengthUpTo(withoutEvidence, RUNTIME_RECEIPT_OUTPUT_MAX_BYTES)
|
|
2007
|
+
.exceeded
|
|
2008
|
+
)
|
|
2009
|
+
return value;
|
|
2010
|
+
const reason =
|
|
2011
|
+
'Call evidence exceeds the saved-result size limit; the complete tool output is retained.';
|
|
2012
|
+
const unavailable = {
|
|
2013
|
+
...value,
|
|
2014
|
+
meta: { ...meta, callEvidence: { unavailable: reason } },
|
|
2015
|
+
};
|
|
2016
|
+
if (
|
|
2017
|
+
!jsonByteLengthUpTo(unavailable, RUNTIME_RECEIPT_OUTPUT_MAX_BYTES).exceeded
|
|
2018
|
+
)
|
|
2019
|
+
return unavailable;
|
|
2020
|
+
// An output exactly at the historical limit has no room for the marker.
|
|
2021
|
+
// Its original bytes remain legal; the runner retains the warning instead.
|
|
2022
|
+
console.warn(reason);
|
|
2023
|
+
return withoutEvidence;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
1979
2026
|
export function serializeToolExecuteResult(
|
|
1980
2027
|
value: ToolExecuteResult,
|
|
2028
|
+
options?: { includeCallEvidence?: boolean },
|
|
1981
2029
|
): SerializedToolExecuteResult {
|
|
2030
|
+
const callEvidence =
|
|
2031
|
+
options?.includeCallEvidence === false
|
|
2032
|
+
? undefined
|
|
2033
|
+
: savedToolCallEvidence.get(value);
|
|
2034
|
+
const meta =
|
|
2035
|
+
callEvidence !== undefined ? { ...value.meta, callEvidence } : value.meta;
|
|
1982
2036
|
const metadata = metadataInputFromToolExecuteResult(value);
|
|
1983
2037
|
const listDatasets = serializedListDatasetsFromResult(value);
|
|
1984
2038
|
const listRows = serializedListRowsFromResult({
|
|
@@ -2007,11 +2061,11 @@ export function serializeToolExecuteResult(
|
|
|
2007
2061
|
view,
|
|
2008
2062
|
meta: value.toolResponse.meta,
|
|
2009
2063
|
});
|
|
2010
|
-
return {
|
|
2064
|
+
return fitSavedCallEvidence({
|
|
2011
2065
|
__kind: SERIALIZED_TOOL_EXECUTE_RESULT_V2_KIND,
|
|
2012
2066
|
status: value.status,
|
|
2013
2067
|
...(typeof value.job_id === 'string' ? { job_id: value.job_id } : {}),
|
|
2014
|
-
...(isRecord(
|
|
2068
|
+
...(isRecord(meta) ? { meta } : {}),
|
|
2015
2069
|
...(isRecord(value.billing) ? { billing: value.billing } : {}),
|
|
2016
2070
|
toolResponse: {
|
|
2017
2071
|
// rawV2 is the canonical complete provider response. Only the legacy
|
|
@@ -2035,13 +2089,13 @@ export function serializeToolExecuteResult(
|
|
|
2035
2089
|
...(targetValues ? { targetValues } : {}),
|
|
2036
2090
|
metadata,
|
|
2037
2091
|
execution: value._metadata.execution,
|
|
2038
|
-
};
|
|
2092
|
+
});
|
|
2039
2093
|
}
|
|
2040
|
-
return {
|
|
2094
|
+
return fitSavedCallEvidence({
|
|
2041
2095
|
__kind: SERIALIZED_TOOL_EXECUTE_RESULT_V1_KIND,
|
|
2042
2096
|
status: value.status,
|
|
2043
2097
|
...(typeof value.job_id === 'string' ? { job_id: value.job_id } : {}),
|
|
2044
|
-
...(isRecord(
|
|
2098
|
+
...(isRecord(meta) ? { meta } : {}),
|
|
2045
2099
|
...(isRecord(value.billing) ? { billing: value.billing } : {}),
|
|
2046
2100
|
toolResponse: {
|
|
2047
2101
|
raw: serializedRaw,
|
|
@@ -2052,7 +2106,7 @@ export function serializeToolExecuteResult(
|
|
|
2052
2106
|
...(targetValues ? { targetValues } : {}),
|
|
2053
2107
|
metadata,
|
|
2054
2108
|
execution: value._metadata.execution,
|
|
2055
|
-
};
|
|
2109
|
+
});
|
|
2056
2110
|
}
|
|
2057
2111
|
|
|
2058
2112
|
export function isSerializedToolExecuteResult(
|
|
@@ -2156,7 +2210,13 @@ export function cloneToolExecuteResultWithExecution<TResult>(
|
|
|
2156
2210
|
): ToolExecuteResult<TResult> {
|
|
2157
2211
|
return deserializeToolExecuteResult({
|
|
2158
2212
|
...serializeToolExecuteResult(value),
|
|
2159
|
-
execution
|
|
2213
|
+
execution: {
|
|
2214
|
+
...execution,
|
|
2215
|
+
...(!execution.resultReceiptKey &&
|
|
2216
|
+
value._metadata.execution.resultReceiptKey
|
|
2217
|
+
? { resultReceiptKey: value._metadata.execution.resultReceiptKey }
|
|
2218
|
+
: {}),
|
|
2219
|
+
},
|
|
2160
2220
|
}) as ToolExecuteResult<TResult>;
|
|
2161
2221
|
}
|
|
2162
2222
|
|
|
@@ -34,6 +34,8 @@ export type PlayVisualNodeProgress = {
|
|
|
34
34
|
completedAt?: number;
|
|
35
35
|
artifactTableNamespace?: string | null;
|
|
36
36
|
nodeIo?: PlayDocflowNodeIoState;
|
|
37
|
+
/** Error reported by this step, distinct from a run stopping other work. */
|
|
38
|
+
error?: string;
|
|
37
39
|
conditionCounts?: PlayConditionCounts;
|
|
38
40
|
};
|
|
39
41
|
|
|
@@ -8,11 +8,38 @@ type Bulk = {
|
|
|
8
8
|
email_batch: Array<{ email_address: string; ip_address?: string }>;
|
|
9
9
|
};
|
|
10
10
|
type Verdict = Record<string, unknown> & { address: string; status: string };
|
|
11
|
-
type Results = {
|
|
11
|
+
type Results = {
|
|
12
|
+
email_batch?: unknown[];
|
|
13
|
+
errors?: unknown[];
|
|
14
|
+
data?: Results;
|
|
15
|
+
// The scalar validate endpoint returns one verdict at the top level.
|
|
16
|
+
address?: unknown;
|
|
17
|
+
status?: unknown;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const VERDICT_STATUSES = [
|
|
21
|
+
'valid',
|
|
22
|
+
'invalid',
|
|
23
|
+
'catch-all',
|
|
24
|
+
'unknown',
|
|
25
|
+
'spamtrap',
|
|
26
|
+
'abuse',
|
|
27
|
+
'do_not_mail',
|
|
28
|
+
];
|
|
12
29
|
|
|
13
30
|
function emailKey(email: string) {
|
|
14
31
|
return email.trim().toLowerCase();
|
|
15
32
|
}
|
|
33
|
+
|
|
34
|
+
function isVerdict(value: unknown): value is Verdict {
|
|
35
|
+
const row = value as Verdict | null;
|
|
36
|
+
return Boolean(
|
|
37
|
+
row &&
|
|
38
|
+
typeof row.address === 'string' &&
|
|
39
|
+
typeof row.status === 'string' &&
|
|
40
|
+
VERDICT_STATUSES.includes(row.status),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
16
43
|
function bucket(payload: Single) {
|
|
17
44
|
// The response identifies emails, but does not echo the supplied IP.
|
|
18
45
|
return JSON.stringify([
|
|
@@ -23,7 +50,7 @@ function bucket(payload: Single) {
|
|
|
23
50
|
|
|
24
51
|
export const zerobounceValidateBatchStrategy: BatchOperationStrategy<
|
|
25
52
|
Single,
|
|
26
|
-
Bulk,
|
|
53
|
+
Bulk | Single,
|
|
27
54
|
Results,
|
|
28
55
|
{ data: Verdict }
|
|
29
56
|
> = {
|
|
@@ -47,25 +74,58 @@ export const zerobounceValidateBatchStrategy: BatchOperationStrategy<
|
|
|
47
74
|
'ZeroBounce batch requires 1–100 emails with matching IP options.',
|
|
48
75
|
);
|
|
49
76
|
}
|
|
77
|
+
const items = payloads.map((payload) => ({
|
|
78
|
+
itemKey: zerobounceValidateBatchStrategy.toItemKey(payload),
|
|
79
|
+
payload,
|
|
80
|
+
}));
|
|
81
|
+
const emails = [...new Set(payloads.map((p) => emailKey(p.email!)))];
|
|
82
|
+
if (emails.length === 1) {
|
|
83
|
+
// One distinct email (a per-row waterfall, duplicate rows, or receipt
|
|
84
|
+
// dedup leaving one owned member) must not ride the bulk endpoint:
|
|
85
|
+
// zerobounce_batch_validate is gated at 30/min provider_shared, while
|
|
86
|
+
// the scalar validate endpoint allows 80,000 validations per 10 s.
|
|
87
|
+
// Compiling here covers both the receipt-owned cohort
|
|
88
|
+
// (compileOwnedPhysicalBatch) and the legacy path.
|
|
89
|
+
return {
|
|
90
|
+
batchOperation: 'zerobounce_validate',
|
|
91
|
+
batchPayload: first,
|
|
92
|
+
items,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
50
95
|
return {
|
|
51
|
-
// Receipt deduplication may leave one owned email. Keep the bulk operation.
|
|
52
96
|
batchOperation: 'zerobounce_batch_validate',
|
|
53
97
|
batchPayload: {
|
|
54
|
-
email_batch:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}),
|
|
59
|
-
),
|
|
98
|
+
email_batch: emails.map((email_address) => ({
|
|
99
|
+
email_address,
|
|
100
|
+
...(first.ip_address ? { ip_address: first.ip_address } : {}),
|
|
101
|
+
})),
|
|
60
102
|
},
|
|
61
|
-
items
|
|
62
|
-
itemKey: zerobounceValidateBatchStrategy.toItemKey(payload),
|
|
63
|
-
payload,
|
|
64
|
-
})),
|
|
103
|
+
items,
|
|
65
104
|
};
|
|
66
105
|
},
|
|
67
106
|
splitResult(fullResult, compiled) {
|
|
68
107
|
const result = fullResult.data ?? fullResult;
|
|
108
|
+
if (compiled.batchOperation === 'zerobounce_validate') {
|
|
109
|
+
if (
|
|
110
|
+
!isVerdict(result) ||
|
|
111
|
+
compiled.items.some(
|
|
112
|
+
(item) => emailKey(result.address) !== emailKey(item.payload.email!),
|
|
113
|
+
)
|
|
114
|
+
) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
'ZeroBounce single verification returned a malformed or mismatched verdict.',
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// Every item shares the one distinct email this call verified.
|
|
120
|
+
return compiled.items.map((item) => ({
|
|
121
|
+
itemKey: item.itemKey,
|
|
122
|
+
result: { data: result },
|
|
123
|
+
rawResult: result,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
if (!('email_batch' in compiled.batchPayload)) {
|
|
127
|
+
throw new Error('ZeroBounce bulk split received a scalar payload.');
|
|
128
|
+
}
|
|
69
129
|
if (
|
|
70
130
|
!Array.isArray(result.email_batch) ||
|
|
71
131
|
(result.errors !== undefined &&
|
|
@@ -79,21 +139,8 @@ export const zerobounceValidateBatchStrategy: BatchOperationStrategy<
|
|
|
79
139
|
compiled.batchPayload.email_batch.map((p) => emailKey(p.email_address)),
|
|
80
140
|
);
|
|
81
141
|
const byEmail = new Map<string, Verdict>();
|
|
82
|
-
for (const
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
!row ||
|
|
86
|
-
typeof row.address !== 'string' ||
|
|
87
|
-
![
|
|
88
|
-
'valid',
|
|
89
|
-
'invalid',
|
|
90
|
-
'catch-all',
|
|
91
|
-
'unknown',
|
|
92
|
-
'spamtrap',
|
|
93
|
-
'abuse',
|
|
94
|
-
'do_not_mail',
|
|
95
|
-
].includes(row.status)
|
|
96
|
-
) {
|
|
142
|
+
for (const row of result.email_batch) {
|
|
143
|
+
if (!isVerdict(row)) {
|
|
97
144
|
throw new Error(
|
|
98
145
|
'ZeroBounce bulk verification returned a malformed verdict.',
|
|
99
146
|
);
|
|
@@ -37,6 +37,8 @@ export type ToolResultExecutionMetadata = {
|
|
|
37
37
|
cacheKey?: string;
|
|
38
38
|
receiptRole?: 'owner' | 'follower';
|
|
39
39
|
receiptKey?: string;
|
|
40
|
+
/** Canonical saved result identity; receiptKey may name a transport alias. */
|
|
41
|
+
resultReceiptKey?: string;
|
|
40
42
|
attachedToReceiptKey?: string;
|
|
41
43
|
};
|
|
42
44
|
|