deepline 0.3.47 → 0.3.48
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 +36 -6
- package/dist/bundling-sources/sdk/src/errors.ts +5 -0
- package/dist/bundling-sources/sdk/src/http.ts +4 -1
- package/dist/bundling-sources/sdk/src/index.ts +1 -0
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +61 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +26 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +2 -0
- package/dist/bundling-sources/shared_libs/plays/tool-execution-error.ts +82 -0
- package/dist/bundling-sources/shared_libs/tool-execution-error.ts +82 -0
- package/dist/cli/index.js +63 -12
- package/dist/cli/index.mjs +63 -12
- package/dist/{compiler-manifest-BuoqasqI.d.mts → compiler-manifest-CbzdZrJj.d.mts} +13 -1
- package/dist/{compiler-manifest-BuoqasqI.d.ts → compiler-manifest-CbzdZrJj.d.ts} +13 -1
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +62 -11
- package/dist/index.mjs +62 -11
- package/dist/install-integrity.json +2 -2
- 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
|
@@ -3106,6 +3106,14 @@ export class DeeplineClient {
|
|
|
3106
3106
|
signal?: AbortSignal;
|
|
3107
3107
|
lastEventId?: string;
|
|
3108
3108
|
mode?: 'cli' | 'ui';
|
|
3109
|
+
/**
|
|
3110
|
+
* A run just accepted by durable scheduler admission can take a short
|
|
3111
|
+
* time to appear in the asynchronous Convex read model. Callers that
|
|
3112
|
+
* received that admission may opt into a bounded retry of the initial
|
|
3113
|
+
* pending response; a normal arbitrary run id still fails loudly by
|
|
3114
|
+
* default.
|
|
3115
|
+
*/
|
|
3116
|
+
waitForProjection?: boolean;
|
|
3109
3117
|
},
|
|
3110
3118
|
): AsyncGenerator<PlayLiveEvent> {
|
|
3111
3119
|
const headers =
|
|
@@ -3114,12 +3122,33 @@ export class DeeplineClient {
|
|
|
3114
3122
|
: undefined;
|
|
3115
3123
|
const params = new URLSearchParams();
|
|
3116
3124
|
params.set('mode', options?.mode ?? 'cli');
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3125
|
+
const projectionDeadline = Date.now() + 30_000;
|
|
3126
|
+
let projectionAttempt = 0;
|
|
3127
|
+
for (;;) {
|
|
3128
|
+
let sawEvent = false;
|
|
3129
|
+
try {
|
|
3130
|
+
for await (const event of this.http.streamSse<PlayLiveEvent>(
|
|
3131
|
+
`/api/v2/runs/${encodeURIComponent(workflowId)}/tail?${params.toString()}`,
|
|
3132
|
+
{ signal: options?.signal, headers },
|
|
3133
|
+
)) {
|
|
3134
|
+
sawEvent = true;
|
|
3135
|
+
if (event.scope === 'play') {
|
|
3136
|
+
yield event;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
return;
|
|
3140
|
+
} catch (error) {
|
|
3141
|
+
const projectionPending =
|
|
3142
|
+
options?.waitForProjection === true &&
|
|
3143
|
+
!sawEvent &&
|
|
3144
|
+
error instanceof DeeplineError &&
|
|
3145
|
+
(error.statusCode === 404 ||
|
|
3146
|
+
(error.statusCode === 202 &&
|
|
3147
|
+
error.code === 'RUN_PROJECTION_PENDING')) &&
|
|
3148
|
+
Date.now() < projectionDeadline;
|
|
3149
|
+
if (!projectionPending) throw error;
|
|
3150
|
+
await sleep(streamReconnectDelayMs(projectionAttempt));
|
|
3151
|
+
projectionAttempt += 1;
|
|
3123
3152
|
}
|
|
3124
3153
|
}
|
|
3125
3154
|
}
|
|
@@ -4439,6 +4468,7 @@ export class DeeplineClient {
|
|
|
4439
4468
|
for await (const event of this.streamPlayRunEvents(workflowId, {
|
|
4440
4469
|
mode: 'cli',
|
|
4441
4470
|
signal: options?.signal,
|
|
4471
|
+
waitForProjection: true,
|
|
4442
4472
|
})) {
|
|
4443
4473
|
if (options?.signal?.aborted) {
|
|
4444
4474
|
await this.cancelPlay(workflowId);
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type ToolExecutionErrorOptions,
|
|
16
16
|
type ToolExecutionNetworkKind,
|
|
17
17
|
type ToolExecutionNetworkScope,
|
|
18
|
+
type ToolExecutionPublicDetails,
|
|
18
19
|
} from '../../shared_libs/plays/tool-execution-error';
|
|
19
20
|
|
|
20
21
|
export {
|
|
@@ -33,6 +34,7 @@ export {
|
|
|
33
34
|
type ToolExecutionErrorOptions,
|
|
34
35
|
type ToolExecutionNetworkKind,
|
|
35
36
|
type ToolExecutionNetworkScope,
|
|
37
|
+
type ToolExecutionPublicDetails,
|
|
36
38
|
} from '../../shared_libs/plays/tool-execution-error';
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -145,6 +147,8 @@ export class ToolRateLimitError extends RateLimitError {
|
|
|
145
147
|
readonly networkKind: ToolExecutionError['networkKind'];
|
|
146
148
|
/** Network boundary that failed, or `null` for non-network failures. */
|
|
147
149
|
readonly networkScope: ToolExecutionError['networkScope'];
|
|
150
|
+
/** Explicitly allowlisted diagnostics safe for SDK callers. */
|
|
151
|
+
readonly publicDetails: ToolExecutionError['publicDetails'];
|
|
148
152
|
|
|
149
153
|
/** Constructed by the SDK after a structured tool HTTP 429. */
|
|
150
154
|
constructor(message: string, options: ToolExecutionErrorOptions) {
|
|
@@ -161,6 +165,7 @@ export class ToolRateLimitError extends RateLimitError {
|
|
|
161
165
|
this.requestId = options.requestId;
|
|
162
166
|
this.networkKind = options.networkKind;
|
|
163
167
|
this.networkScope = options.networkScope;
|
|
168
|
+
this.publicDetails = options.publicDetails ?? null;
|
|
164
169
|
this.details = options.details;
|
|
165
170
|
brandAsToolExecutionError(this);
|
|
166
171
|
if (isProviderTransientFailure(this)) {
|
|
@@ -621,7 +621,10 @@ export class HttpClient {
|
|
|
621
621
|
signal: options?.signal,
|
|
622
622
|
});
|
|
623
623
|
|
|
624
|
-
|
|
624
|
+
// An SSE endpoint must either establish an event stream or reject the
|
|
625
|
+
// request. A 202 JSON response is a deliberately retryable admission
|
|
626
|
+
// state, not an empty, successfully completed stream.
|
|
627
|
+
if (!response.ok || response.status === 202) {
|
|
625
628
|
const body = await response.text();
|
|
626
629
|
const parsed = parseResponseBody(body);
|
|
627
630
|
if (
|
|
@@ -199,7 +199,7 @@ export const SDK_RELEASE = {
|
|
|
199
199
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
|
-
version: '0.3.
|
|
202
|
+
version: '0.3.48',
|
|
203
203
|
updateSummary:
|
|
204
204
|
'Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.',
|
|
205
205
|
packageCapabilities: {
|
|
@@ -156,6 +156,7 @@ type RuntimeApiRequest =
|
|
|
156
156
|
action: 'start_run';
|
|
157
157
|
idempotencyKey?: string;
|
|
158
158
|
playName: string;
|
|
159
|
+
playReference?: string | null;
|
|
159
160
|
runId: string;
|
|
160
161
|
artifactStorageKey?: string | null;
|
|
161
162
|
artifactHash?: string | null;
|
|
@@ -172,6 +173,45 @@ type RuntimeApiRequest =
|
|
|
172
173
|
inputSha256?: string;
|
|
173
174
|
replayedFromRunId?: string | null;
|
|
174
175
|
}
|
|
176
|
+
| {
|
|
177
|
+
/** Ordered projection of a Runtime Postgres/Absurd admission. */
|
|
178
|
+
action: 'project_run_created';
|
|
179
|
+
idempotencyKey?: string;
|
|
180
|
+
playName: string;
|
|
181
|
+
playReference?: string | null;
|
|
182
|
+
workflowId: string;
|
|
183
|
+
runId: string;
|
|
184
|
+
workflowFamilyKey: string;
|
|
185
|
+
definitionScope?: 'org' | 'system';
|
|
186
|
+
revisionId?: string | null;
|
|
187
|
+
artifactStorageKey?: string | null;
|
|
188
|
+
artifactHash?: string | null;
|
|
189
|
+
graphHash?: string | null;
|
|
190
|
+
runtimeBackend?: string | null;
|
|
191
|
+
schedulerBackend?: string | null;
|
|
192
|
+
schedulerSchema?: string | null;
|
|
193
|
+
executionProfile?: string | null;
|
|
194
|
+
runtimeReleaseId?: string | null;
|
|
195
|
+
runtimeDeployVersion?: string | null;
|
|
196
|
+
runtimeCallbackBaseUrl?: string | null;
|
|
197
|
+
coordinatorUrl?: string | null;
|
|
198
|
+
coordinatorWorkerName?: string | null;
|
|
199
|
+
runtimeWorkflowName?: string | null;
|
|
200
|
+
runtimeHarnessWorkerName?: string | null;
|
|
201
|
+
maxCreditsPerRun?: number | null;
|
|
202
|
+
staticPipeline?: unknown;
|
|
203
|
+
source?: 'published' | 'ad_hoc' | 'draft';
|
|
204
|
+
triggerSource?: 'webhook' | 'cron' | 'sql_listener' | 'api';
|
|
205
|
+
inputFileId?: string;
|
|
206
|
+
inputBytes?: number;
|
|
207
|
+
inputSha256?: string;
|
|
208
|
+
replayedFromRunId?: string | null;
|
|
209
|
+
secretRefs?: unknown[];
|
|
210
|
+
inputSummary?: Record<string, unknown>;
|
|
211
|
+
/** Terminal bypass used only when the causal admission projection is
|
|
212
|
+
* permanently blocked and no queued row can be projected. */
|
|
213
|
+
admissionFailure?: string;
|
|
214
|
+
}
|
|
175
215
|
| ({
|
|
176
216
|
action: 'save_results';
|
|
177
217
|
} & RuntimeSaveResults)
|
|
@@ -668,7 +708,8 @@ function isRetryableAppRuntimeAction(
|
|
|
668
708
|
action === 'release_runtime_step_receipt' ||
|
|
669
709
|
action === 'save_results' ||
|
|
670
710
|
action === 'skip_runtime_step_receipt' ||
|
|
671
|
-
action === 'start_run'
|
|
711
|
+
action === 'start_run' ||
|
|
712
|
+
action === 'project_run_created'
|
|
672
713
|
);
|
|
673
714
|
}
|
|
674
715
|
|
|
@@ -1925,6 +1966,7 @@ export async function startRunViaAppRuntime(
|
|
|
1925
1966
|
context: WorkerRuntimeApiContext,
|
|
1926
1967
|
input: {
|
|
1927
1968
|
playName: string;
|
|
1969
|
+
playReference?: string | null;
|
|
1928
1970
|
runId: string;
|
|
1929
1971
|
artifactStorageKey?: string | null;
|
|
1930
1972
|
artifactHash?: string | null;
|
|
@@ -1949,6 +1991,24 @@ export async function startRunViaAppRuntime(
|
|
|
1949
1991
|
});
|
|
1950
1992
|
}
|
|
1951
1993
|
|
|
1994
|
+
/**
|
|
1995
|
+
* Deliver the causally-first `run.created` projection after Runtime Postgres
|
|
1996
|
+
* has atomically admitted the run and its Absurd task. It is safe to retry:
|
|
1997
|
+
* the server preserves any existing queued, running, or terminal run.
|
|
1998
|
+
*/
|
|
1999
|
+
export async function projectRunCreatedViaAppRuntime(
|
|
2000
|
+
context: WorkerRuntimeApiContext,
|
|
2001
|
+
input: Omit<
|
|
2002
|
+
Extract<RuntimeApiRequest, { action: 'project_run_created' }>,
|
|
2003
|
+
'action'
|
|
2004
|
+
>,
|
|
2005
|
+
): Promise<void> {
|
|
2006
|
+
await postAppRuntimeApi<{ ok: true; status: 'queued' }>(context, {
|
|
2007
|
+
action: 'project_run_created',
|
|
2008
|
+
...input,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1952
2012
|
export async function saveResultsViaAppRuntime(
|
|
1953
2013
|
context: WorkerRuntimeApiContext,
|
|
1954
2014
|
input: RuntimeSaveResults,
|
|
@@ -42,7 +42,15 @@ export type { PlayRunInputPayload };
|
|
|
42
42
|
export type PlaySchedulerSubmitInput = {
|
|
43
43
|
runId: string;
|
|
44
44
|
playId: string;
|
|
45
|
+
/** Canonical definition name used by the Convex read-model projector. */
|
|
46
|
+
definitionName?: string | null;
|
|
45
47
|
playName: string;
|
|
48
|
+
/** Immutable display/lookup name selected at admission, if distinct. */
|
|
49
|
+
playReference?: string | null;
|
|
50
|
+
/** Definition ownership selected at admission; never re-resolve on replay. */
|
|
51
|
+
definitionScope?: 'org' | 'system' | null;
|
|
52
|
+
/** Immutable published revision selected at admission. */
|
|
53
|
+
revisionId?: string | null;
|
|
46
54
|
workflowFamilyKey?: string | null;
|
|
47
55
|
artifactStorageKey: string;
|
|
48
56
|
/** Optional inline artifact for the Node scheduler. */
|
|
@@ -52,6 +60,10 @@ export type PlaySchedulerSubmitInput = {
|
|
|
52
60
|
/** Immutable scheduler/worker/runner ABI snapshot (not a Git SHA). */
|
|
53
61
|
runtimeProtocolVersion?: PlayRuntimeProtocolVersion | null;
|
|
54
62
|
input: PlayRunInputPayload;
|
|
63
|
+
/** Immutable secret authority selected at admission; never re-resolve on projection. */
|
|
64
|
+
secretRefs?: unknown[];
|
|
65
|
+
/** Display-only, size-bounded projection of the admitted input. */
|
|
66
|
+
inputSummary?: Record<string, unknown>;
|
|
55
67
|
/** Convex metadata for the exact input saved before scheduler submission. */
|
|
56
68
|
inputFileId?: string;
|
|
57
69
|
inputBytes?: number;
|
|
@@ -71,6 +83,8 @@ export type PlaySchedulerSubmitInput = {
|
|
|
71
83
|
storageKey?: string;
|
|
72
84
|
fileName?: string;
|
|
73
85
|
logicalPath?: string;
|
|
86
|
+
storageKind?: 'r2';
|
|
87
|
+
contentHash?: string;
|
|
74
88
|
contentType?: string;
|
|
75
89
|
bytes?: number;
|
|
76
90
|
} | null;
|
|
@@ -87,6 +101,8 @@ export type PlaySchedulerSubmitInput = {
|
|
|
87
101
|
logicalPath?: string;
|
|
88
102
|
fileName?: string;
|
|
89
103
|
storageKey: string;
|
|
104
|
+
storageKind?: 'r2';
|
|
105
|
+
contentHash?: string;
|
|
90
106
|
contentType?: string;
|
|
91
107
|
bytes?: number;
|
|
92
108
|
inlineText?: string;
|
|
@@ -136,6 +152,16 @@ export type PlaySchedulerSubmitInput = {
|
|
|
136
152
|
*/
|
|
137
153
|
queuePriority?: number | null;
|
|
138
154
|
executionProfile?: string | null;
|
|
155
|
+
/** Concrete Runtime Postgres namespace selected at admission. */
|
|
156
|
+
schedulerSchema?: string | null;
|
|
157
|
+
/** Immutable release tuple for the Convex read-model projection. */
|
|
158
|
+
runtimeReleaseId?: string | null;
|
|
159
|
+
runtimeCallbackBaseUrl?: string | null;
|
|
160
|
+
coordinatorWorkerName?: string | null;
|
|
161
|
+
runtimeWorkflowName?: string | null;
|
|
162
|
+
runtimeHarnessWorkerName?: string | null;
|
|
163
|
+
/** Billing admission cap from the immutable contract snapshot. */
|
|
164
|
+
maxCreditsPerRun?: number | null;
|
|
139
165
|
/** runner backend to use for executing attempts */
|
|
140
166
|
runtimeBackend: string;
|
|
141
167
|
/**
|
|
@@ -103,6 +103,7 @@ class StructuredToolHttpError extends ToolExecutionError {
|
|
|
103
103
|
retryAfterMs: input.options.retryAfterMs ?? null,
|
|
104
104
|
networkKind: input.options.networkKind ?? null,
|
|
105
105
|
networkScope: input.options.networkScope ?? null,
|
|
106
|
+
publicDetails: input.options.publicDetails ?? null,
|
|
106
107
|
});
|
|
107
108
|
this.billing = input.billing;
|
|
108
109
|
this.status = input.status;
|
|
@@ -447,6 +448,7 @@ export function normalizeToolHttpErrorMessage(input: {
|
|
|
447
448
|
(origin === 'provider' && category === 'network'
|
|
448
449
|
? 'deepline_to_provider'
|
|
449
450
|
: null),
|
|
451
|
+
publicDetails: hydratedFailure?.publicDetails ?? null,
|
|
450
452
|
};
|
|
451
453
|
const billing = getObjectField(parsed, 'billing');
|
|
452
454
|
if (isInsufficientCreditsBilling(billing)) {
|
|
@@ -84,6 +84,17 @@ export type ToolExecutionNetworkScope =
|
|
|
84
84
|
| 'runtime_to_deepline'
|
|
85
85
|
| 'deepline_to_provider';
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Bounded, primitive-only diagnostics explicitly approved for customers.
|
|
89
|
+
* Raw provider bodies, credentials, prompts, stacks, and causes never belong
|
|
90
|
+
* in this shared API/SDK/Play contract.
|
|
91
|
+
*
|
|
92
|
+
* @sdkReference errors 063
|
|
93
|
+
*/
|
|
94
|
+
export type ToolExecutionPublicDetails = Readonly<
|
|
95
|
+
Record<string, string | number | boolean | null>
|
|
96
|
+
>;
|
|
97
|
+
|
|
87
98
|
/**
|
|
88
99
|
* Portable version-1 `tool_error` payload.
|
|
89
100
|
*
|
|
@@ -120,6 +131,8 @@ export type ToolExecutionFailureV1 = {
|
|
|
120
131
|
networkKind: ToolExecutionNetworkKind | null;
|
|
121
132
|
/** Network boundary that failed, or `null`. */
|
|
122
133
|
networkScope: ToolExecutionNetworkScope | null;
|
|
134
|
+
/** Explicitly allowlisted customer diagnostics, when present. */
|
|
135
|
+
publicDetails?: ToolExecutionPublicDetails | null;
|
|
123
136
|
};
|
|
124
137
|
|
|
125
138
|
/**
|
|
@@ -265,6 +278,8 @@ export class ToolExecutionError extends DeeplineError {
|
|
|
265
278
|
readonly networkKind: ToolExecutionNetworkKind | null;
|
|
266
279
|
/** Network boundary that failed, or `null` for non-network failures. */
|
|
267
280
|
readonly networkScope: ToolExecutionNetworkScope | null;
|
|
281
|
+
/** Explicitly allowlisted diagnostics safe for SDK and Play callers. */
|
|
282
|
+
readonly publicDetails: ToolExecutionPublicDetails | null;
|
|
268
283
|
|
|
269
284
|
/**
|
|
270
285
|
* Construct a structured tool error.
|
|
@@ -290,6 +305,7 @@ export class ToolExecutionError extends DeeplineError {
|
|
|
290
305
|
this.retryAfterMs = options.retryAfterMs;
|
|
291
306
|
this.networkKind = options.networkKind;
|
|
292
307
|
this.networkScope = options.networkScope;
|
|
308
|
+
this.publicDetails = options.publicDetails ?? null;
|
|
293
309
|
applyBrand(this, TOOL_EXECUTION_ERROR_BRAND);
|
|
294
310
|
if (isProviderTransientFailure(options)) {
|
|
295
311
|
applyBrand(this, PROVIDER_TRANSIENT_ERROR_BRAND);
|
|
@@ -547,6 +563,69 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
547
563
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
548
564
|
}
|
|
549
565
|
|
|
566
|
+
const MAX_PUBLIC_DETAIL_ENTRIES = 20;
|
|
567
|
+
const MAX_PUBLIC_DETAIL_KEY_LENGTH = 80;
|
|
568
|
+
const MAX_PUBLIC_DETAIL_STRING_LENGTH = 512;
|
|
569
|
+
const publicDetailsByError = new WeakMap<Error, ToolExecutionPublicDetails>();
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Normalizes the one public-detail shape at every transport boundary. Nested
|
|
573
|
+
* values are rejected to prevent accidental disclosure of provider responses.
|
|
574
|
+
*/
|
|
575
|
+
export function normalizeToolExecutionPublicDetails(
|
|
576
|
+
value: unknown,
|
|
577
|
+
): ToolExecutionPublicDetails | null {
|
|
578
|
+
if (!isRecord(value)) return null;
|
|
579
|
+
const details: Record<string, string | number | boolean | null> = {};
|
|
580
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
581
|
+
if (Object.keys(details).length >= MAX_PUBLIC_DETAIL_ENTRIES) break;
|
|
582
|
+
if (
|
|
583
|
+
key.length === 0 ||
|
|
584
|
+
key.length > MAX_PUBLIC_DETAIL_KEY_LENGTH ||
|
|
585
|
+
!/^[a-z][a-zA-Z0-9_]*$/.test(key)
|
|
586
|
+
) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
if (typeof entry === 'string') {
|
|
590
|
+
if (entry.length <= MAX_PUBLIC_DETAIL_STRING_LENGTH) details[key] = entry;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (typeof entry === 'number') {
|
|
594
|
+
if (Number.isFinite(entry)) details[key] = entry;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (typeof entry === 'boolean') {
|
|
598
|
+
details[key] = entry;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (entry === null) details[key] = null;
|
|
602
|
+
}
|
|
603
|
+
return Object.keys(details).length > 0 ? details : null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Attaches allowlisted details to an in-process error without making them
|
|
608
|
+
* enumerable. The execute boundary can later serialize them through the
|
|
609
|
+
* portable `tool_error.publicDetails` field.
|
|
610
|
+
*/
|
|
611
|
+
export function withToolExecutionPublicDetails<T extends Error>(
|
|
612
|
+
error: T,
|
|
613
|
+
details: unknown,
|
|
614
|
+
): T {
|
|
615
|
+
const normalized = normalizeToolExecutionPublicDetails(details);
|
|
616
|
+
if (normalized) publicDetailsByError.set(error, normalized);
|
|
617
|
+
return error;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Reads details deliberately attached through `withToolExecutionPublicDetails`. */
|
|
621
|
+
export function getToolExecutionPublicDetails(
|
|
622
|
+
error: unknown,
|
|
623
|
+
): ToolExecutionPublicDetails | null {
|
|
624
|
+
return error instanceof Error
|
|
625
|
+
? (publicDetailsByError.get(error) ?? null)
|
|
626
|
+
: null;
|
|
627
|
+
}
|
|
628
|
+
|
|
550
629
|
export function normalizeToolExecutionFailure(
|
|
551
630
|
value: unknown,
|
|
552
631
|
): ToolExecutionFailureV1 | null {
|
|
@@ -565,6 +644,7 @@ export function normalizeToolExecutionFailure(
|
|
|
565
644
|
operation,
|
|
566
645
|
});
|
|
567
646
|
const category = normalizeToolExecutionCategory(value.category);
|
|
647
|
+
const publicDetails = normalizeToolExecutionPublicDetails(value.publicDetails);
|
|
568
648
|
const trustworthy =
|
|
569
649
|
origin !== 'unknown' &&
|
|
570
650
|
category !== 'unknown' &&
|
|
@@ -583,6 +663,7 @@ export function normalizeToolExecutionFailure(
|
|
|
583
663
|
retryAfterMs: finiteNonNegativeInteger(value.retryAfterMs),
|
|
584
664
|
networkKind: normalizeNetworkKind(value.networkKind),
|
|
585
665
|
networkScope: normalizeNetworkScope(value.networkScope),
|
|
666
|
+
...(publicDetails ? { publicDetails } : {}),
|
|
586
667
|
};
|
|
587
668
|
}
|
|
588
669
|
|
|
@@ -604,6 +685,7 @@ export function serializeToolExecutionFailure(
|
|
|
604
685
|
retryAfterMs: error.retryAfterMs,
|
|
605
686
|
networkKind: error.networkKind,
|
|
606
687
|
networkScope: error.networkScope,
|
|
688
|
+
publicDetails: error.publicDetails,
|
|
607
689
|
});
|
|
608
690
|
}
|
|
609
691
|
|
|
@@ -84,6 +84,17 @@ export type ToolExecutionNetworkScope =
|
|
|
84
84
|
| 'runtime_to_deepline'
|
|
85
85
|
| 'deepline_to_provider';
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Bounded, primitive-only diagnostics explicitly approved for customers.
|
|
89
|
+
* Raw provider bodies, credentials, prompts, stacks, and causes never belong
|
|
90
|
+
* in this shared API/SDK/Play contract.
|
|
91
|
+
*
|
|
92
|
+
* @sdkReference errors 063
|
|
93
|
+
*/
|
|
94
|
+
export type ToolExecutionPublicDetails = Readonly<
|
|
95
|
+
Record<string, string | number | boolean | null>
|
|
96
|
+
>;
|
|
97
|
+
|
|
87
98
|
/**
|
|
88
99
|
* Portable version-1 `tool_error` payload.
|
|
89
100
|
*
|
|
@@ -120,6 +131,8 @@ export type ToolExecutionFailureV1 = {
|
|
|
120
131
|
networkKind: ToolExecutionNetworkKind | null;
|
|
121
132
|
/** Network boundary that failed, or `null`. */
|
|
122
133
|
networkScope: ToolExecutionNetworkScope | null;
|
|
134
|
+
/** Explicitly allowlisted customer diagnostics, when present. */
|
|
135
|
+
publicDetails?: ToolExecutionPublicDetails | null;
|
|
123
136
|
};
|
|
124
137
|
|
|
125
138
|
/**
|
|
@@ -265,6 +278,8 @@ export class ToolExecutionError extends DeeplineError {
|
|
|
265
278
|
readonly networkKind: ToolExecutionNetworkKind | null;
|
|
266
279
|
/** Network boundary that failed, or `null` for non-network failures. */
|
|
267
280
|
readonly networkScope: ToolExecutionNetworkScope | null;
|
|
281
|
+
/** Explicitly allowlisted diagnostics safe for SDK and Play callers. */
|
|
282
|
+
readonly publicDetails: ToolExecutionPublicDetails | null;
|
|
268
283
|
|
|
269
284
|
/**
|
|
270
285
|
* Construct a structured tool error.
|
|
@@ -290,6 +305,7 @@ export class ToolExecutionError extends DeeplineError {
|
|
|
290
305
|
this.retryAfterMs = options.retryAfterMs;
|
|
291
306
|
this.networkKind = options.networkKind;
|
|
292
307
|
this.networkScope = options.networkScope;
|
|
308
|
+
this.publicDetails = options.publicDetails ?? null;
|
|
293
309
|
applyBrand(this, TOOL_EXECUTION_ERROR_BRAND);
|
|
294
310
|
if (isProviderTransientFailure(options)) {
|
|
295
311
|
applyBrand(this, PROVIDER_TRANSIENT_ERROR_BRAND);
|
|
@@ -547,6 +563,69 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
547
563
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
548
564
|
}
|
|
549
565
|
|
|
566
|
+
const MAX_PUBLIC_DETAIL_ENTRIES = 20;
|
|
567
|
+
const MAX_PUBLIC_DETAIL_KEY_LENGTH = 80;
|
|
568
|
+
const MAX_PUBLIC_DETAIL_STRING_LENGTH = 512;
|
|
569
|
+
const publicDetailsByError = new WeakMap<Error, ToolExecutionPublicDetails>();
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Normalizes the one public-detail shape at every transport boundary. Nested
|
|
573
|
+
* values are rejected to prevent accidental disclosure of provider responses.
|
|
574
|
+
*/
|
|
575
|
+
export function normalizeToolExecutionPublicDetails(
|
|
576
|
+
value: unknown,
|
|
577
|
+
): ToolExecutionPublicDetails | null {
|
|
578
|
+
if (!isRecord(value)) return null;
|
|
579
|
+
const details: Record<string, string | number | boolean | null> = {};
|
|
580
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
581
|
+
if (Object.keys(details).length >= MAX_PUBLIC_DETAIL_ENTRIES) break;
|
|
582
|
+
if (
|
|
583
|
+
key.length === 0 ||
|
|
584
|
+
key.length > MAX_PUBLIC_DETAIL_KEY_LENGTH ||
|
|
585
|
+
!/^[a-z][a-zA-Z0-9_]*$/.test(key)
|
|
586
|
+
) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
if (typeof entry === 'string') {
|
|
590
|
+
if (entry.length <= MAX_PUBLIC_DETAIL_STRING_LENGTH) details[key] = entry;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
if (typeof entry === 'number') {
|
|
594
|
+
if (Number.isFinite(entry)) details[key] = entry;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (typeof entry === 'boolean') {
|
|
598
|
+
details[key] = entry;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (entry === null) details[key] = null;
|
|
602
|
+
}
|
|
603
|
+
return Object.keys(details).length > 0 ? details : null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Attaches allowlisted details to an in-process error without making them
|
|
608
|
+
* enumerable. The execute boundary can later serialize them through the
|
|
609
|
+
* portable `tool_error.publicDetails` field.
|
|
610
|
+
*/
|
|
611
|
+
export function withToolExecutionPublicDetails<T extends Error>(
|
|
612
|
+
error: T,
|
|
613
|
+
details: unknown,
|
|
614
|
+
): T {
|
|
615
|
+
const normalized = normalizeToolExecutionPublicDetails(details);
|
|
616
|
+
if (normalized) publicDetailsByError.set(error, normalized);
|
|
617
|
+
return error;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Reads details deliberately attached through `withToolExecutionPublicDetails`. */
|
|
621
|
+
export function getToolExecutionPublicDetails(
|
|
622
|
+
error: unknown,
|
|
623
|
+
): ToolExecutionPublicDetails | null {
|
|
624
|
+
return error instanceof Error
|
|
625
|
+
? (publicDetailsByError.get(error) ?? null)
|
|
626
|
+
: null;
|
|
627
|
+
}
|
|
628
|
+
|
|
550
629
|
export function normalizeToolExecutionFailure(
|
|
551
630
|
value: unknown,
|
|
552
631
|
): ToolExecutionFailureV1 | null {
|
|
@@ -565,6 +644,7 @@ export function normalizeToolExecutionFailure(
|
|
|
565
644
|
operation,
|
|
566
645
|
});
|
|
567
646
|
const category = normalizeToolExecutionCategory(value.category);
|
|
647
|
+
const publicDetails = normalizeToolExecutionPublicDetails(value.publicDetails);
|
|
568
648
|
const trustworthy =
|
|
569
649
|
origin !== 'unknown' &&
|
|
570
650
|
category !== 'unknown' &&
|
|
@@ -583,6 +663,7 @@ export function normalizeToolExecutionFailure(
|
|
|
583
663
|
retryAfterMs: finiteNonNegativeInteger(value.retryAfterMs),
|
|
584
664
|
networkKind: normalizeNetworkKind(value.networkKind),
|
|
585
665
|
networkScope: normalizeNetworkScope(value.networkScope),
|
|
666
|
+
...(publicDetails ? { publicDetails } : {}),
|
|
586
667
|
};
|
|
587
668
|
}
|
|
588
669
|
|
|
@@ -604,6 +685,7 @@ export function serializeToolExecutionFailure(
|
|
|
604
685
|
retryAfterMs: error.retryAfterMs,
|
|
605
686
|
networkKind: error.networkKind,
|
|
606
687
|
networkScope: error.networkScope,
|
|
688
|
+
publicDetails: error.publicDetails,
|
|
607
689
|
});
|
|
608
690
|
}
|
|
609
691
|
|