deepline 0.1.56 → 0.1.57

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/cli/index.js CHANGED
@@ -220,10 +220,10 @@ function resolveConfig(options) {
220
220
 
221
221
  // src/release.ts
222
222
  var SDK_RELEASE = {
223
- version: "0.1.56",
223
+ version: "0.1.57",
224
224
  apiContract: "2026-05-play-tool-describe-starters",
225
225
  supportPolicy: {
226
- latest: "0.1.56",
226
+ latest: "0.1.57",
227
227
  minimumSupported: "0.1.53",
228
228
  deprecatedBelow: "0.1.53"
229
229
  }
@@ -197,10 +197,10 @@ function resolveConfig(options) {
197
197
 
198
198
  // src/release.ts
199
199
  var SDK_RELEASE = {
200
- version: "0.1.56",
200
+ version: "0.1.57",
201
201
  apiContract: "2026-05-play-tool-describe-starters",
202
202
  supportPolicy: {
203
- latest: "0.1.56",
203
+ latest: "0.1.57",
204
204
  minimumSupported: "0.1.53",
205
205
  deprecatedBelow: "0.1.53"
206
206
  }
package/dist/index.js CHANGED
@@ -216,10 +216,10 @@ function resolveConfig(options) {
216
216
 
217
217
  // src/release.ts
218
218
  var SDK_RELEASE = {
219
- version: "0.1.56",
219
+ version: "0.1.57",
220
220
  apiContract: "2026-05-play-tool-describe-starters",
221
221
  supportPolicy: {
222
- latest: "0.1.56",
222
+ latest: "0.1.57",
223
223
  minimumSupported: "0.1.53",
224
224
  deprecatedBelow: "0.1.53"
225
225
  }
package/dist/index.mjs CHANGED
@@ -170,10 +170,10 @@ function resolveConfig(options) {
170
170
 
171
171
  // src/release.ts
172
172
  var SDK_RELEASE = {
173
- version: "0.1.56",
173
+ version: "0.1.57",
174
174
  apiContract: "2026-05-play-tool-describe-starters",
175
175
  supportPolicy: {
176
- latest: "0.1.56",
176
+ latest: "0.1.57",
177
177
  minimumSupported: "0.1.53",
178
178
  deprecatedBelow: "0.1.53"
179
179
  }
@@ -130,6 +130,12 @@ import type {
130
130
  LiveNodeProgressMap,
131
131
  LiveNodeProgressSnapshot,
132
132
  } from './runtime/live-progress';
133
+ import {
134
+ ToolHttpError,
135
+ extractErrorBilling,
136
+ isHardBillingToolHttpError,
137
+ normalizeToolHttpErrorMessage,
138
+ } from './runtime/tool-http-errors';
133
139
 
134
140
  // The play's default export. The bundler injects this — see bundle-play-file.ts.
135
141
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -195,23 +201,6 @@ const EXECUTE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
195
201
  const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
196
202
  const V2_EXECUTE_RESPONSE_CONTRACT = 'v2-tool-execution-result';
197
203
 
198
- class ToolHttpError extends Error {
199
- readonly billing: Record<string, unknown> | null;
200
-
201
- constructor(message: string, billing: Record<string, unknown> | null) {
202
- super(message);
203
- this.name = 'ToolHttpError';
204
- this.billing = billing;
205
- }
206
- }
207
-
208
- function formatCreditAmount(value: unknown): string {
209
- if (typeof value !== 'number' || !Number.isFinite(value)) {
210
- return String(value ?? '-');
211
- }
212
- return Number(value.toFixed(8)).toString();
213
- }
214
-
215
204
  function getStringField(value: unknown, key: string): string | null {
216
205
  if (!isRecord(value)) return null;
217
206
  const field = value[key];
@@ -227,71 +216,6 @@ function getObjectField(
227
216
  return isRecord(field) ? field : null;
228
217
  }
229
218
 
230
- function isInsufficientCreditsBilling(
231
- billing: Record<string, unknown> | null,
232
- ): billing is Record<string, unknown> {
233
- return billing?.kind === 'insufficient_credits';
234
- }
235
-
236
- function formatInsufficientCreditsMessage(input: {
237
- billing: Record<string, unknown>;
238
- toolId: string;
239
- }): string {
240
- const operation =
241
- getStringField(input.billing, 'operation_id') ??
242
- getStringField(input.billing, 'operation') ??
243
- input.toolId;
244
- const balance = formatCreditAmount(input.billing.balance_credits);
245
- const required = formatCreditAmount(input.billing.required_credits);
246
- const recommended = formatCreditAmount(
247
- input.billing.recommended_add_credits ?? input.billing.needed_credits,
248
- );
249
- const billingUrl = getStringField(input.billing, 'billing_url');
250
- const addSuffix =
251
- billingUrl && recommended !== '-'
252
- ? ` Add >=${recommended} at ${billingUrl}.`
253
- : billingUrl
254
- ? ` Add credits at ${billingUrl}.`
255
- : '';
256
- return `Workspace balance ${balance} < required ${required} for ${operation}.${addSuffix}`;
257
- }
258
-
259
- function normalizeToolHttpErrorMessage(input: {
260
- toolId: string;
261
- status: number;
262
- attempt: number;
263
- maxAttempts: number;
264
- bodyText: string;
265
- }): ToolHttpError {
266
- let parsed: Record<string, unknown> | null = null;
267
- try {
268
- const candidate = JSON.parse(input.bodyText);
269
- parsed = isRecord(candidate) ? candidate : null;
270
- } catch {
271
- parsed = null;
272
- }
273
- const billing = getObjectField(parsed, 'billing');
274
- if (isInsufficientCreditsBilling(billing)) {
275
- return new ToolHttpError(
276
- `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${formatInsufficientCreditsMessage(
277
- {
278
- billing,
279
- toolId: input.toolId,
280
- },
281
- )}`,
282
- billing,
283
- );
284
- }
285
- return new ToolHttpError(
286
- `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${input.bodyText.slice(0, 500)}`,
287
- billing,
288
- );
289
- }
290
-
291
- function extractErrorBilling(error: unknown): Record<string, unknown> | null {
292
- return error instanceof ToolHttpError ? error.billing : null;
293
- }
294
-
295
219
  /** R2 binding injected by the Worker runtime (when present in deploy metadata). */
296
220
  type WorkerEnv = {
297
221
  PLAYS_BUCKET?: R2Bucket;
@@ -1348,7 +1272,7 @@ async function callToolDirect(
1348
1272
  bodyText: text,
1349
1273
  });
1350
1274
  const retryable =
1351
- res.status === 429 ||
1275
+ (res.status === 429 && !isHardBillingToolHttpError(lastError)) ||
1352
1276
  (res.status >= 500 && WORKER_RETRY_SAFE_5XX_TOOLS.has(toolId));
1353
1277
  if (!retryable || attempt >= maxAttempts) {
1354
1278
  throw lastError;
@@ -0,0 +1,198 @@
1
+ export class ToolHttpError extends Error {
2
+ readonly billing: Record<string, unknown> | null;
3
+
4
+ constructor(message: string, billing: Record<string, unknown> | null) {
5
+ super(message);
6
+ this.name = 'ToolHttpError';
7
+ this.billing = billing;
8
+ }
9
+ }
10
+
11
+ function formatCreditAmount(value: unknown): string {
12
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
13
+ return String(value ?? '-');
14
+ }
15
+ return Number(value.toFixed(8)).toString();
16
+ }
17
+
18
+ function isRecord(value: unknown): value is Record<string, unknown> {
19
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
20
+ }
21
+
22
+ function getStringField(value: unknown, key: string): string | null {
23
+ if (!isRecord(value)) return null;
24
+ const field = value[key];
25
+ return typeof field === 'string' && field.trim() ? field : null;
26
+ }
27
+
28
+ function getObjectField(
29
+ value: unknown,
30
+ key: string,
31
+ ): Record<string, unknown> | null {
32
+ if (!isRecord(value)) return null;
33
+ const field = value[key];
34
+ return isRecord(field) ? field : null;
35
+ }
36
+
37
+ function isInsufficientCreditsBilling(
38
+ billing: Record<string, unknown> | null,
39
+ ): billing is Record<string, unknown> {
40
+ return billing?.kind === 'insufficient_credits';
41
+ }
42
+
43
+ function isHardBillingFailurePayload(
44
+ payload: Record<string, unknown> | null,
45
+ ): payload is Record<string, unknown> {
46
+ if (!payload) return false;
47
+ const category = String(
48
+ payload.error_category ?? payload.errorCategory ?? '',
49
+ ).toLowerCase();
50
+ const code = String(payload.code ?? payload.error_code ?? '').toUpperCase();
51
+ const message = String(
52
+ payload.error ?? payload.message ?? payload.failure_description ?? '',
53
+ ).toLowerCase();
54
+ if (category === 'billing') return true;
55
+ if (
56
+ code === 'INSUFFICIENT_CREDITS' ||
57
+ code === 'BILLING_CAP_EXCEEDED' ||
58
+ code === 'MONTHLY_BILLING_LIMIT_EXCEEDED'
59
+ ) {
60
+ return true;
61
+ }
62
+ return (
63
+ (message.includes('billing cap') ||
64
+ message.includes('monthly billing limit') ||
65
+ message.includes('rolling 30-day organization billing cap') ||
66
+ message.includes('insufficient credits')) &&
67
+ !message.includes('rate limit')
68
+ );
69
+ }
70
+
71
+ function normalizeHardBillingPayload(
72
+ payload: Record<string, unknown>,
73
+ ): Record<string, unknown> {
74
+ return {
75
+ kind: 'billing_cap_exceeded',
76
+ code:
77
+ typeof payload.code === 'string' && payload.code.trim()
78
+ ? payload.code
79
+ : 'MONTHLY_BILLING_LIMIT_EXCEEDED',
80
+ error_category: 'billing',
81
+ failure_origin:
82
+ typeof payload.failure_origin === 'string' && payload.failure_origin.trim()
83
+ ? payload.failure_origin
84
+ : 'deepline_billing',
85
+ message:
86
+ typeof payload.error === 'string' && payload.error.trim()
87
+ ? payload.error
88
+ : typeof payload.message === 'string' && payload.message.trim()
89
+ ? payload.message
90
+ : 'Deepline billing cap exceeded.',
91
+ ...payload,
92
+ };
93
+ }
94
+
95
+ function formatHardBillingFailureMessage(input: {
96
+ billing: Record<string, unknown>;
97
+ toolId: string;
98
+ status: number;
99
+ attempt: number;
100
+ maxAttempts: number;
101
+ }): string {
102
+ const code = getStringField(input.billing, 'code');
103
+ const message =
104
+ getStringField(input.billing, 'message') ??
105
+ getStringField(input.billing, 'error') ??
106
+ 'Deepline billing cap exceeded.';
107
+ return [
108
+ `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}:`,
109
+ 'Deepline billing cap exceeded.',
110
+ 'Run halted before marking remaining rows processed.',
111
+ code ? `code=${code}.` : '',
112
+ message,
113
+ ]
114
+ .filter(Boolean)
115
+ .join(' ');
116
+ }
117
+
118
+ function formatInsufficientCreditsMessage(input: {
119
+ billing: Record<string, unknown>;
120
+ toolId: string;
121
+ }): string {
122
+ const operation =
123
+ getStringField(input.billing, 'operation_id') ??
124
+ getStringField(input.billing, 'operation') ??
125
+ input.toolId;
126
+ const balance = formatCreditAmount(input.billing.balance_credits);
127
+ const required = formatCreditAmount(input.billing.required_credits);
128
+ const recommended = formatCreditAmount(
129
+ input.billing.recommended_add_credits ?? input.billing.needed_credits,
130
+ );
131
+ const billingUrl = getStringField(input.billing, 'billing_url');
132
+ const addSuffix =
133
+ billingUrl && recommended !== '-'
134
+ ? ` Add >=${recommended} at ${billingUrl}.`
135
+ : billingUrl
136
+ ? ` Add credits at ${billingUrl}.`
137
+ : '';
138
+ return `Workspace balance ${balance} < required ${required} for ${operation}.${addSuffix}`;
139
+ }
140
+
141
+ export function normalizeToolHttpErrorMessage(input: {
142
+ toolId: string;
143
+ status: number;
144
+ attempt: number;
145
+ maxAttempts: number;
146
+ bodyText: string;
147
+ }): ToolHttpError {
148
+ let parsed: Record<string, unknown> | null = null;
149
+ try {
150
+ const candidate = JSON.parse(input.bodyText);
151
+ parsed = isRecord(candidate) ? candidate : null;
152
+ } catch {
153
+ parsed = null;
154
+ }
155
+ const billing = getObjectField(parsed, 'billing');
156
+ if (isInsufficientCreditsBilling(billing)) {
157
+ return new ToolHttpError(
158
+ `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${formatInsufficientCreditsMessage(
159
+ {
160
+ billing,
161
+ toolId: input.toolId,
162
+ },
163
+ )}`,
164
+ billing,
165
+ );
166
+ }
167
+ const hardBillingPayload = isHardBillingFailurePayload(billing)
168
+ ? normalizeHardBillingPayload(billing)
169
+ : isHardBillingFailurePayload(parsed)
170
+ ? normalizeHardBillingPayload(parsed)
171
+ : null;
172
+ if (hardBillingPayload) {
173
+ return new ToolHttpError(
174
+ formatHardBillingFailureMessage({
175
+ billing: hardBillingPayload,
176
+ toolId: input.toolId,
177
+ status: input.status,
178
+ attempt: input.attempt,
179
+ maxAttempts: input.maxAttempts,
180
+ }),
181
+ hardBillingPayload,
182
+ );
183
+ }
184
+ return new ToolHttpError(
185
+ `tool ${input.toolId} ${input.status} attempt ${input.attempt}/${input.maxAttempts}: ${input.bodyText.slice(0, 500)}`,
186
+ billing,
187
+ );
188
+ }
189
+
190
+ export function extractErrorBilling(
191
+ error: unknown,
192
+ ): Record<string, unknown> | null {
193
+ return error instanceof ToolHttpError ? error.billing : null;
194
+ }
195
+
196
+ export function isHardBillingToolHttpError(error: unknown): boolean {
197
+ return error instanceof ToolHttpError && isHardBillingFailurePayload(error.billing);
198
+ }
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.56',
53
+ version: '0.1.57',
54
54
  apiContract: '2026-05-play-tool-describe-starters',
55
55
  supportPolicy: {
56
- latest: '0.1.56',
56
+ latest: '0.1.57',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {