deepline 0.1.301 → 0.1.303
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 -0
- package/dist/bundling-sources/sdk/src/errors.ts +96 -36
- package/dist/bundling-sources/sdk/src/http.ts +82 -5
- package/dist/bundling-sources/sdk/src/index.ts +12 -0
- package/dist/bundling-sources/sdk/src/play.ts +29 -1
- package/dist/bundling-sources/sdk/src/release.ts +5 -3
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +120 -14
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durable-receipt-execution.ts +59 -11
- package/dist/bundling-sources/shared_libs/play-runtime/execution-ledger-store.ts +3 -3
- package/dist/bundling-sources/shared_libs/play-runtime/execution-plan.ts +0 -15
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +136 -36
- package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +17 -12
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +210 -5
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result-paths.ts +15 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +4 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +33 -24
- package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +4 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-contract-version.ts +6 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -0
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +159 -12
- package/dist/bundling-sources/shared_libs/plays/contracts.ts +69 -1
- package/dist/bundling-sources/shared_libs/tool-execution-error.ts +557 -0
- package/dist/cli/index.js +556 -183
- package/dist/cli/index.mjs +556 -183
- package/dist/index.d.mts +69 -40
- package/dist/index.d.ts +69 -40
- package/dist/index.js +480 -80
- package/dist/index.mjs +477 -80
- package/dist/plays/bundle-play-file.d.mts +4 -2
- package/dist/plays/bundle-play-file.d.ts +4 -2
- package/dist/plays/bundle-play-file.mjs +116 -19
- package/dist/tool-execution-error-YDz7UMl-.d.mts +440 -0
- package/dist/tool-execution-error-YDz7UMl-.d.ts +440 -0
- package/package.json +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/completed-receipt-cache.ts +0 -166
- package/dist/compiler-manifest-BjopNIqx.d.mts +0 -240
- package/dist/compiler-manifest-BjopNIqx.d.ts +0 -240
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
normalizeToolHttpErrorMessage,
|
|
4
4
|
type ToolHttpError,
|
|
5
5
|
} from './tool-http-errors';
|
|
6
|
+
import type { ToolExecutionErrorSchemaVersion } from '../tool-execution-error';
|
|
6
7
|
|
|
7
8
|
export const TOOL_EXECUTE_TRANSIENT_HTTP_MAX_ATTEMPTS = 2;
|
|
8
9
|
// A provider may need to finish the original request before it can replay the
|
|
@@ -296,6 +297,7 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
296
297
|
status: number;
|
|
297
298
|
attempt: number;
|
|
298
299
|
bodyText: string;
|
|
300
|
+
schemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
299
301
|
retryAfterHeader?: string | null;
|
|
300
302
|
transientHttpRetrySafe?: boolean;
|
|
301
303
|
providerLatencyMs?: number;
|
|
@@ -318,12 +320,20 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
318
320
|
hasRetryAfterHeader,
|
|
319
321
|
transientHttpRetrySafe,
|
|
320
322
|
});
|
|
323
|
+
const retryAfterMs = parseToolExecuteRetryAfterMs(
|
|
324
|
+
input.retryAfterHeader,
|
|
325
|
+
input.nowMs,
|
|
326
|
+
);
|
|
321
327
|
const error = normalizeToolHttpErrorMessage({
|
|
322
328
|
toolId: input.toolId,
|
|
323
329
|
status: input.status,
|
|
324
330
|
attempt: input.attempt,
|
|
325
331
|
maxAttempts: initialRetryDecision.attemptCap,
|
|
326
332
|
bodyText: input.bodyText,
|
|
333
|
+
schemaVersion: input.schemaVersion,
|
|
334
|
+
retryable: initialRetryDecision.retryable,
|
|
335
|
+
retryAfterMs:
|
|
336
|
+
input.status === 429 && hasRetryAfterHeader ? retryAfterMs : null,
|
|
327
337
|
});
|
|
328
338
|
if (
|
|
329
339
|
typeof input.providerLatencyMs === 'number' &&
|
|
@@ -355,10 +365,6 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
355
365
|
) {
|
|
356
366
|
error.receiptFailureKind = 'repairable';
|
|
357
367
|
}
|
|
358
|
-
const retryAfterMs = parseToolExecuteRetryAfterMs(
|
|
359
|
-
input.retryAfterHeader,
|
|
360
|
-
input.nowMs,
|
|
361
|
-
);
|
|
362
368
|
const retryDelayMs =
|
|
363
369
|
input.status === 429
|
|
364
370
|
? Math.min(
|
|
@@ -369,14 +375,14 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
369
375
|
),
|
|
370
376
|
)
|
|
371
377
|
: retryDecision.reason === 'idempotency_in_progress' &&
|
|
372
|
-
!hasRetryAfterHeader
|
|
373
|
-
? idempotencyInProgressRetryDelayMs(input.attempt)
|
|
374
|
-
: retryDecision.reason === 'gateway_invocation_in_progress' &&
|
|
375
378
|
!hasRetryAfterHeader
|
|
376
379
|
? idempotencyInProgressRetryDelayMs(input.attempt)
|
|
377
|
-
:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
: retryDecision.reason === 'gateway_invocation_in_progress' &&
|
|
381
|
+
!hasRetryAfterHeader
|
|
382
|
+
? idempotencyInProgressRetryDelayMs(input.attempt)
|
|
383
|
+
: retryAfterMs > 0
|
|
384
|
+
? Math.min(TOOL_EXECUTE_RETRY_DELAY_MAX_MS, retryAfterMs)
|
|
385
|
+
: TOOL_EXECUTE_RETRY_DELAY_FALLBACK_MS;
|
|
380
386
|
return {
|
|
381
387
|
...retryDecision,
|
|
382
388
|
error,
|
|
@@ -395,7 +401,6 @@ export function classifyToolExecuteHttpFailure(input: {
|
|
|
395
401
|
: TOOL_EXECUTE_BARE_RATE_LIMIT_BACKPRESSURE_MS
|
|
396
402
|
: null,
|
|
397
403
|
chargeRetryBudget:
|
|
398
|
-
shouldRetry &&
|
|
399
|
-
retryDecision.reason !== 'gateway_invocation_in_progress',
|
|
404
|
+
shouldRetry && retryDecision.reason !== 'gateway_invocation_in_progress',
|
|
400
405
|
};
|
|
401
406
|
}
|
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
import type { WorkReceiptFailureKind } from './work-receipts';
|
|
2
|
+
import {
|
|
3
|
+
LEGACY_TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
|
|
4
|
+
ToolExecutionError,
|
|
5
|
+
deserializeToolExecutionFailure,
|
|
6
|
+
isProviderTransientFailure,
|
|
7
|
+
normalizeToolExecutionCategory,
|
|
8
|
+
normalizeToolExecutionOrigin,
|
|
9
|
+
type ToolExecutionErrorSchemaVersion,
|
|
10
|
+
type ToolExecutionErrorOptions,
|
|
11
|
+
} from '../tool-execution-error';
|
|
2
12
|
|
|
13
|
+
const TOOL_HTTP_ERROR_BRAND = Symbol.for('deepline.tool-http-error.v1');
|
|
14
|
+
|
|
15
|
+
type ToolHttpErrorConstructorInput = {
|
|
16
|
+
message: string;
|
|
17
|
+
billing: Record<string, unknown> | null;
|
|
18
|
+
status: number;
|
|
19
|
+
receiptFailureKind: WorkReceiptFailureKind;
|
|
20
|
+
options: Partial<ToolExecutionErrorOptions>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function applyToolHttpErrorFields(
|
|
24
|
+
error: {
|
|
25
|
+
name: string;
|
|
26
|
+
billing: Record<string, unknown> | null;
|
|
27
|
+
status: number;
|
|
28
|
+
receiptFailureKind: WorkReceiptFailureKind;
|
|
29
|
+
},
|
|
30
|
+
input: ToolHttpErrorConstructorInput,
|
|
31
|
+
): void {
|
|
32
|
+
error.name = 'ToolHttpError';
|
|
33
|
+
error.billing = input.billing;
|
|
34
|
+
error.status = input.status;
|
|
35
|
+
error.receiptFailureKind = input.receiptFailureKind;
|
|
36
|
+
Object.defineProperty(error, TOOL_HTTP_ERROR_BRAND, {
|
|
37
|
+
configurable: false,
|
|
38
|
+
enumerable: false,
|
|
39
|
+
writable: false,
|
|
40
|
+
value: true,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Historical play-runtime HTTP error. Existing artifacts must keep this exact
|
|
46
|
+
* Error prototype; typed errors are an explicit artifact-contract opt-in.
|
|
47
|
+
*/
|
|
3
48
|
export class ToolHttpError extends Error {
|
|
4
49
|
readonly billing: Record<string, unknown> | null;
|
|
5
50
|
/** HTTP status of the failed tool-execute response (e.g. 429, 502). */
|
|
@@ -11,15 +56,89 @@ export class ToolHttpError extends Error {
|
|
|
11
56
|
billing: Record<string, unknown> | null,
|
|
12
57
|
status: number,
|
|
13
58
|
receiptFailureKind: WorkReceiptFailureKind = 'terminal',
|
|
59
|
+
_options: Partial<ToolExecutionErrorOptions> = {},
|
|
14
60
|
) {
|
|
15
61
|
super(message);
|
|
16
|
-
this.name = 'ToolHttpError';
|
|
17
62
|
this.billing = billing;
|
|
18
63
|
this.status = status;
|
|
19
64
|
this.receiptFailureKind = receiptFailureKind;
|
|
65
|
+
applyToolHttpErrorFields(this, {
|
|
66
|
+
message,
|
|
67
|
+
billing,
|
|
68
|
+
status,
|
|
69
|
+
receiptFailureKind,
|
|
70
|
+
options: _options,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static [Symbol.hasInstance](value: unknown): boolean {
|
|
75
|
+
return Boolean(
|
|
76
|
+
value &&
|
|
77
|
+
typeof value === 'object' &&
|
|
78
|
+
(value as Record<symbol, unknown>)[TOOL_HTTP_ERROR_BRAND] === true,
|
|
79
|
+
);
|
|
20
80
|
}
|
|
21
81
|
}
|
|
22
82
|
|
|
83
|
+
class StructuredToolHttpError extends ToolExecutionError {
|
|
84
|
+
readonly billing: Record<string, unknown> | null;
|
|
85
|
+
readonly status: number;
|
|
86
|
+
receiptFailureKind: WorkReceiptFailureKind;
|
|
87
|
+
|
|
88
|
+
constructor(input: ToolHttpErrorConstructorInput) {
|
|
89
|
+
super(input.message, {
|
|
90
|
+
toolId: input.options.toolId ?? 'unknown_tool',
|
|
91
|
+
provider: input.options.provider ?? null,
|
|
92
|
+
operation: input.options.operation ?? null,
|
|
93
|
+
code: input.options.code ?? null,
|
|
94
|
+
origin: input.options.origin ?? 'unknown',
|
|
95
|
+
category: input.options.category ?? 'unknown',
|
|
96
|
+
retryable: input.options.retryable === true,
|
|
97
|
+
statusCode:
|
|
98
|
+
input.options.statusCode === undefined
|
|
99
|
+
? input.status
|
|
100
|
+
: input.options.statusCode,
|
|
101
|
+
requestId: input.options.requestId ?? null,
|
|
102
|
+
retryAfterMs: input.options.retryAfterMs ?? null,
|
|
103
|
+
networkKind: input.options.networkKind ?? null,
|
|
104
|
+
networkScope: input.options.networkScope ?? null,
|
|
105
|
+
});
|
|
106
|
+
this.billing = input.billing;
|
|
107
|
+
this.status = input.status;
|
|
108
|
+
this.receiptFailureKind = input.receiptFailureKind;
|
|
109
|
+
applyToolHttpErrorFields(this, input);
|
|
110
|
+
this.name = isProviderTransientFailure(this)
|
|
111
|
+
? 'ProviderTransientError'
|
|
112
|
+
: 'ToolExecutionError';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function createToolHttpError(
|
|
117
|
+
schemaVersion: ToolExecutionErrorSchemaVersion,
|
|
118
|
+
message: string,
|
|
119
|
+
billing: Record<string, unknown> | null,
|
|
120
|
+
status: number,
|
|
121
|
+
receiptFailureKind: WorkReceiptFailureKind = 'terminal',
|
|
122
|
+
options: Partial<ToolExecutionErrorOptions> = {},
|
|
123
|
+
): ToolHttpError {
|
|
124
|
+
if (schemaVersion === LEGACY_TOOL_EXECUTION_ERROR_SCHEMA_VERSION) {
|
|
125
|
+
return new ToolHttpError(
|
|
126
|
+
message,
|
|
127
|
+
billing,
|
|
128
|
+
status,
|
|
129
|
+
receiptFailureKind,
|
|
130
|
+
options,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return new StructuredToolHttpError({
|
|
134
|
+
message,
|
|
135
|
+
billing,
|
|
136
|
+
status,
|
|
137
|
+
receiptFailureKind,
|
|
138
|
+
options,
|
|
139
|
+
}) as ToolHttpError;
|
|
140
|
+
}
|
|
141
|
+
|
|
23
142
|
function formatCreditAmount(value: unknown): string {
|
|
24
143
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
25
144
|
return String(value ?? '-');
|
|
@@ -222,7 +341,12 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
222
341
|
attempt: number;
|
|
223
342
|
maxAttempts: number;
|
|
224
343
|
bodyText: string;
|
|
344
|
+
retryable?: boolean;
|
|
345
|
+
retryAfterMs?: number | null;
|
|
346
|
+
schemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
225
347
|
}): ToolHttpError {
|
|
348
|
+
const schemaVersion =
|
|
349
|
+
input.schemaVersion ?? LEGACY_TOOL_EXECUTION_ERROR_SCHEMA_VERSION;
|
|
226
350
|
let parsed: Record<string, unknown> | null = null;
|
|
227
351
|
try {
|
|
228
352
|
const candidate = JSON.parse(input.bodyText);
|
|
@@ -230,9 +354,64 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
230
354
|
} catch {
|
|
231
355
|
parsed = null;
|
|
232
356
|
}
|
|
357
|
+
const serializedFailure = parsed?.tool_error;
|
|
358
|
+
const hydratedFailure = deserializeToolExecutionFailure(
|
|
359
|
+
'',
|
|
360
|
+
serializedFailure,
|
|
361
|
+
schemaVersion,
|
|
362
|
+
);
|
|
363
|
+
const provider =
|
|
364
|
+
getStringField(parsed, 'provider') ??
|
|
365
|
+
hydratedFailure?.provider ??
|
|
366
|
+
input.toolId.split(/[._:./-]+/)[0]?.trim() ??
|
|
367
|
+
null;
|
|
368
|
+
const operation =
|
|
369
|
+
getStringField(parsed, 'operation') ??
|
|
370
|
+
hydratedFailure?.operation ??
|
|
371
|
+
input.toolId;
|
|
372
|
+
const code = getStringField(parsed, 'code') ?? hydratedFailure?.code ?? null;
|
|
373
|
+
const rawOrigin =
|
|
374
|
+
getStringField(parsed, 'failure_origin') ?? hydratedFailure?.origin;
|
|
375
|
+
const rawCategory =
|
|
376
|
+
getStringField(parsed, 'error_category') ?? hydratedFailure?.category;
|
|
377
|
+
const origin = normalizeToolExecutionOrigin(rawOrigin, {
|
|
378
|
+
provider,
|
|
379
|
+
operation,
|
|
380
|
+
});
|
|
381
|
+
const category = normalizeToolExecutionCategory(rawCategory);
|
|
382
|
+
const trustworthy = origin !== 'unknown' && category !== 'unknown';
|
|
383
|
+
const publicOptions: Partial<ToolExecutionErrorOptions> = {
|
|
384
|
+
toolId: input.toolId,
|
|
385
|
+
provider,
|
|
386
|
+
operation,
|
|
387
|
+
code,
|
|
388
|
+
origin,
|
|
389
|
+
category,
|
|
390
|
+
retryable: trustworthy
|
|
391
|
+
? (hydratedFailure?.retryable ?? input.retryable === true)
|
|
392
|
+
: false,
|
|
393
|
+
statusCode: input.status,
|
|
394
|
+
requestId:
|
|
395
|
+
getStringField(parsed, 'request_id') ??
|
|
396
|
+
getStringField(parsed, 'requestId') ??
|
|
397
|
+
hydratedFailure?.requestId ??
|
|
398
|
+
null,
|
|
399
|
+
retryAfterMs: hydratedFailure?.retryAfterMs ?? input.retryAfterMs ?? null,
|
|
400
|
+
networkKind:
|
|
401
|
+
hydratedFailure?.networkKind ??
|
|
402
|
+
(category === 'network' && code?.toUpperCase().includes('TIMEOUT')
|
|
403
|
+
? 'timeout'
|
|
404
|
+
: null),
|
|
405
|
+
networkScope:
|
|
406
|
+
hydratedFailure?.networkScope ??
|
|
407
|
+
(origin === 'provider' && category === 'network'
|
|
408
|
+
? 'deepline_to_provider'
|
|
409
|
+
: null),
|
|
410
|
+
};
|
|
233
411
|
const billing = getObjectField(parsed, 'billing');
|
|
234
412
|
if (isInsufficientCreditsBilling(billing)) {
|
|
235
|
-
return
|
|
413
|
+
return createToolHttpError(
|
|
414
|
+
schemaVersion,
|
|
236
415
|
`tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${formatInsufficientCreditsMessage(
|
|
237
416
|
{
|
|
238
417
|
billing,
|
|
@@ -241,6 +420,14 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
241
420
|
)}`,
|
|
242
421
|
billing,
|
|
243
422
|
input.status,
|
|
423
|
+
'terminal',
|
|
424
|
+
{
|
|
425
|
+
...publicOptions,
|
|
426
|
+
origin: 'deepline',
|
|
427
|
+
category: 'billing',
|
|
428
|
+
code: publicOptions.code ?? 'INSUFFICIENT_CREDITS',
|
|
429
|
+
retryable: false,
|
|
430
|
+
},
|
|
244
431
|
);
|
|
245
432
|
}
|
|
246
433
|
const hardBillingPayload = isHardBillingFailurePayload(billing)
|
|
@@ -249,7 +436,8 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
249
436
|
? normalizeHardBillingPayload(parsed)
|
|
250
437
|
: null;
|
|
251
438
|
if (hardBillingPayload) {
|
|
252
|
-
return
|
|
439
|
+
return createToolHttpError(
|
|
440
|
+
schemaVersion,
|
|
253
441
|
formatHardBillingFailureMessage({
|
|
254
442
|
billing: hardBillingPayload,
|
|
255
443
|
toolId: input.toolId,
|
|
@@ -259,9 +447,17 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
259
447
|
}),
|
|
260
448
|
hardBillingPayload,
|
|
261
449
|
input.status,
|
|
450
|
+
'terminal',
|
|
451
|
+
{
|
|
452
|
+
...publicOptions,
|
|
453
|
+
origin: 'deepline',
|
|
454
|
+
category: 'billing',
|
|
455
|
+
retryable: false,
|
|
456
|
+
},
|
|
262
457
|
);
|
|
263
458
|
}
|
|
264
|
-
return
|
|
459
|
+
return createToolHttpError(
|
|
460
|
+
schemaVersion,
|
|
265
461
|
`tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${formatPublicToolErrorPayload(
|
|
266
462
|
{
|
|
267
463
|
parsed,
|
|
@@ -271,6 +467,7 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
271
467
|
billing,
|
|
272
468
|
input.status,
|
|
273
469
|
receiptFailureKindForToolErrorPayload(parsed),
|
|
470
|
+
publicOptions,
|
|
274
471
|
);
|
|
275
472
|
}
|
|
276
473
|
|
|
@@ -281,10 +478,18 @@ export function extractErrorBilling(
|
|
|
281
478
|
}
|
|
282
479
|
|
|
283
480
|
export function isHardBillingToolHttpError(error: unknown): boolean {
|
|
284
|
-
|
|
481
|
+
if (
|
|
285
482
|
error instanceof ToolHttpError &&
|
|
286
483
|
(isInsufficientCreditsBilling(error.billing) ||
|
|
287
484
|
isHardBillingFailurePayload(error.billing))
|
|
485
|
+
) {
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
return (
|
|
489
|
+
error instanceof ToolExecutionError &&
|
|
490
|
+
error.origin === 'deepline' &&
|
|
491
|
+
error.category === 'billing' &&
|
|
492
|
+
error.code !== 'BILLING_UNAVAILABLE'
|
|
288
493
|
);
|
|
289
494
|
}
|
|
290
495
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive the public semantic accessor name from a declared list path.
|
|
3
|
+
*
|
|
4
|
+
* Keep this independent from path normalization: `result.data` may resolve to
|
|
5
|
+
* `toolResponse.raw`, but its public getter remains `extractedLists.data`.
|
|
6
|
+
*/
|
|
7
|
+
export function listNameFromDeclaredPath(path: string): string | null {
|
|
8
|
+
return (
|
|
9
|
+
String(path || '')
|
|
10
|
+
.split('.')
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.at(-1)
|
|
13
|
+
?.replace(/[^A-Za-z0-9_$]/g, '') || null
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -42,6 +42,8 @@ export type ToolResultMetadata = {
|
|
|
42
42
|
execution: ToolResultExecutionMetadata;
|
|
43
43
|
targets: Record<string, ToolResultTargetMetadata>;
|
|
44
44
|
lists: Record<string, ToolResultListMetadata>;
|
|
45
|
+
/** Original declarations preserve semantic accessor names across replay. */
|
|
46
|
+
listExtractorPaths?: readonly string[];
|
|
45
47
|
extractors?: Record<string, ToolResultExtractorDescriptor>;
|
|
46
48
|
};
|
|
47
49
|
|
|
@@ -130,6 +132,8 @@ export type ToolExecuteResultBase<
|
|
|
130
132
|
keys: Record<string, string>;
|
|
131
133
|
}
|
|
132
134
|
>;
|
|
135
|
+
/** Original declarations preserve semantic accessor names across replay. */
|
|
136
|
+
listExtractorPaths?: readonly string[];
|
|
133
137
|
};
|
|
134
138
|
};
|
|
135
139
|
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
type SerializedPlayDataset,
|
|
48
48
|
} from '../plays/dataset';
|
|
49
49
|
import { normalizeTableNamespace, sha256Hex } from '../plays/row-identity';
|
|
50
|
+
import { listNameFromDeclaredPath } from './tool-result-paths';
|
|
50
51
|
|
|
51
52
|
type PathSegment = string | number | '*';
|
|
52
53
|
|
|
@@ -744,43 +745,35 @@ function resolveListRows(
|
|
|
744
745
|
{ path: string; rows: Record<string, unknown>[] }
|
|
745
746
|
> = {};
|
|
746
747
|
for (const rawPath of listExtractorPaths ?? []) {
|
|
748
|
+
const listName = listNameFromDeclaredPath(rawPath);
|
|
749
|
+
if (!listName) continue;
|
|
747
750
|
const path = normalizeResultPath(rawPath);
|
|
748
751
|
if (!path) continue;
|
|
749
752
|
const candidates = [...candidateResultPaths(rawPath)].filter(
|
|
750
753
|
(candidate, index, all) => candidate && all.indexOf(candidate) === index,
|
|
751
754
|
);
|
|
752
|
-
let
|
|
753
|
-
|
|
755
|
+
let resolved: { path: string; rows: Record<string, unknown>[] } | null =
|
|
756
|
+
null;
|
|
754
757
|
let emptyMatch: { path: string; rows: Record<string, unknown>[] } | null =
|
|
755
758
|
null;
|
|
756
759
|
for (const candidate of candidates) {
|
|
757
|
-
|
|
758
|
-
if (!
|
|
760
|
+
const candidateRows = normalizeRows(getAtPath(result, candidate));
|
|
761
|
+
if (!candidateRows) {
|
|
759
762
|
continue;
|
|
760
763
|
}
|
|
761
|
-
if (
|
|
762
|
-
|
|
764
|
+
if (candidateRows.length > 0) {
|
|
765
|
+
resolved = { path: candidate, rows: candidateRows };
|
|
763
766
|
break;
|
|
764
767
|
}
|
|
765
|
-
emptyMatch ??= { path: candidate, rows };
|
|
766
|
-
}
|
|
767
|
-
if (!rows && emptyMatch) {
|
|
768
|
-
resolvedPath = emptyMatch.path;
|
|
769
|
-
rows = emptyMatch.rows;
|
|
768
|
+
emptyMatch ??= { path: candidate, rows: candidateRows };
|
|
770
769
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const name = storedPath
|
|
774
|
-
.split('.')
|
|
775
|
-
.filter(Boolean)
|
|
776
|
-
.at(-1)
|
|
777
|
-
?.replace(/\[\d+\]$/, '');
|
|
778
|
-
const listName = name || storedPath;
|
|
770
|
+
resolved ??= emptyMatch;
|
|
771
|
+
if (!resolved) continue;
|
|
779
772
|
const existing = lists[listName];
|
|
780
|
-
if (existing?.rows.length && rows.length === 0) {
|
|
773
|
+
if (existing?.rows.length && resolved.rows.length === 0) {
|
|
781
774
|
continue;
|
|
782
775
|
}
|
|
783
|
-
lists[listName] =
|
|
776
|
+
lists[listName] = resolved;
|
|
784
777
|
}
|
|
785
778
|
return lists;
|
|
786
779
|
}
|
|
@@ -1084,6 +1077,7 @@ export function createToolExecuteResult<TResult = unknown>(input: {
|
|
|
1084
1077
|
toolId: input.metadata.toolId,
|
|
1085
1078
|
execution: input.execution,
|
|
1086
1079
|
targets,
|
|
1080
|
+
listExtractorPaths: [...(input.metadata.listExtractorPaths ?? [])],
|
|
1087
1081
|
...(input.metadata.extractors
|
|
1088
1082
|
? { extractors: input.metadata.extractors }
|
|
1089
1083
|
: {}),
|
|
@@ -1208,6 +1202,21 @@ export function attachToolResultListDataset<T extends Record<string, unknown>>(
|
|
|
1208
1202
|
count: input.count,
|
|
1209
1203
|
keys: input.keys ?? existing?.keys ?? {},
|
|
1210
1204
|
};
|
|
1205
|
+
const declaredPaths = result._metadata.listExtractorPaths ?? [];
|
|
1206
|
+
const inputCandidates = new Set(candidateResultPaths(input.path));
|
|
1207
|
+
const equivalentDeclaration = declaredPaths.find((path) =>
|
|
1208
|
+
candidateResultPaths(path).some((candidate) =>
|
|
1209
|
+
inputCandidates.has(candidate),
|
|
1210
|
+
),
|
|
1211
|
+
);
|
|
1212
|
+
result._metadata.listExtractorPaths = equivalentDeclaration
|
|
1213
|
+
? [...declaredPaths]
|
|
1214
|
+
: [
|
|
1215
|
+
...declaredPaths.filter(
|
|
1216
|
+
(path) => listNameFromDeclaredPath(path) !== input.name,
|
|
1217
|
+
),
|
|
1218
|
+
input.path,
|
|
1219
|
+
];
|
|
1211
1220
|
const accessor = {
|
|
1212
1221
|
path: input.path,
|
|
1213
1222
|
count: input.count,
|
|
@@ -1585,9 +1594,9 @@ function metadataInputFromToolExecuteResult(
|
|
|
1585
1594
|
[info.path],
|
|
1586
1595
|
]),
|
|
1587
1596
|
),
|
|
1588
|
-
listExtractorPaths:
|
|
1589
|
-
|
|
1590
|
-
|
|
1597
|
+
listExtractorPaths:
|
|
1598
|
+
value._metadata.listExtractorPaths ??
|
|
1599
|
+
Object.values(value._metadata.lists).map((list) => list.path),
|
|
1591
1600
|
listIdentityGetters: Object.fromEntries(
|
|
1592
1601
|
Object.values(value._metadata.lists)
|
|
1593
1602
|
.flatMap((list) => Object.entries(list.keys))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isReusableWorkReceiptState } from './work-receipt-state-machine';
|
|
2
|
+
import type { ToolExecutionFailureV1 } from '../tool-execution-error';
|
|
2
3
|
|
|
3
4
|
export type WorkReceiptFailureKind = 'terminal' | 'repairable';
|
|
4
5
|
|
|
@@ -37,6 +38,7 @@ export type WorkReceipt = {
|
|
|
37
38
|
status: WorkReceiptStatus;
|
|
38
39
|
output?: unknown;
|
|
39
40
|
error?: string | null;
|
|
41
|
+
errorPayload?: ToolExecutionFailureV1 | null;
|
|
40
42
|
failureKind?: WorkReceiptFailureKind | null;
|
|
41
43
|
runId?: string | null;
|
|
42
44
|
leaseId?: string | null;
|
|
@@ -89,6 +91,7 @@ export type WorkReceiptBatchFailCommand = {
|
|
|
89
91
|
runId: string;
|
|
90
92
|
key: string;
|
|
91
93
|
error: string;
|
|
94
|
+
errorPayload?: ToolExecutionFailureV1 | null;
|
|
92
95
|
failureKind?: WorkReceiptFailureKind | null;
|
|
93
96
|
leaseId?: string | null;
|
|
94
97
|
runAttempt?: number | null;
|
|
@@ -161,6 +164,7 @@ export type WorkReceiptStore = {
|
|
|
161
164
|
failReceipt(
|
|
162
165
|
input: WorkReceiptCommand & {
|
|
163
166
|
error: string;
|
|
167
|
+
errorPayload?: ToolExecutionFailureV1 | null;
|
|
164
168
|
failureKind?: WorkReceiptFailureKind | null;
|
|
165
169
|
},
|
|
166
170
|
): Promise<WorkReceipt | null>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Immutable Play artifact ABI version shared by bundling and SDK release
|
|
3
|
+
* policy. Keep this module dependency-free: shared Play code must not import
|
|
4
|
+
* the SDK release manifest or create a package cycle.
|
|
5
|
+
*/
|
|
6
|
+
export const CURRENT_PLAY_ARTIFACT_CONTRACT_VERSION = 2 as const;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ToolExecutionErrorSchemaVersion } from '../tool-execution-error';
|
|
2
|
+
|
|
1
3
|
export type PlayPackageImport = {
|
|
2
4
|
name: string;
|
|
3
5
|
version: string | null;
|
|
@@ -21,6 +23,8 @@ export type PlayArtifactCompatibility = {
|
|
|
21
23
|
minRunnerVersion: number;
|
|
22
24
|
runtimeFeatures: PlayRuntimeFeature[];
|
|
23
25
|
runtimeBackend?: string | null;
|
|
26
|
+
/** Missing preserves the legacy schema used before this field existed. */
|
|
27
|
+
toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion;
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
/** The only executable Play artifact contract. */
|