deepline 0.1.302 → 0.1.304

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.
@@ -5,38 +5,28 @@ import {
5
5
  brandAsProviderTransientError,
6
6
  brandAsToolExecutionError,
7
7
  isProviderTransientFailure,
8
+ type ProviderTransientErrorCategory,
9
+ type ToolExecutionErrorCategory,
10
+ type ToolExecutionFailureV1,
11
+ type ToolExecutionErrorOrigin,
8
12
  type ToolExecutionErrorOptions,
13
+ type ToolExecutionNetworkKind,
14
+ type ToolExecutionNetworkScope,
9
15
  } from '../../shared_libs/tool-execution-error.js';
10
16
 
11
17
  export {
12
18
  DeeplineError,
13
19
  ProviderTransientError,
14
20
  ToolExecutionError,
21
+ type ProviderTransientErrorCategory,
22
+ type ToolExecutionErrorCategory,
23
+ type ToolExecutionFailureV1,
24
+ type ToolExecutionErrorOrigin,
15
25
  type ToolExecutionErrorOptions,
26
+ type ToolExecutionNetworkKind,
27
+ type ToolExecutionNetworkScope,
16
28
  } from '../../shared_libs/tool-execution-error.js';
17
29
 
18
- /**
19
- * Base error class for all Deepline SDK errors.
20
- *
21
- * Every error thrown by the SDK extends this class, so you can catch all
22
- * Deepline-specific errors with a single `catch (e) { if (e instanceof DeeplineError) }`.
23
- *
24
- * @example
25
- * ```typescript
26
- * import { DeeplineClient, DeeplineError, AuthError } from 'deepline';
27
- *
28
- * const client = new DeeplineClient();
29
- * try {
30
- * await client.executeTool('dropleads_search_people', { query: 'cto' });
31
- * } catch (err) {
32
- * if (err instanceof AuthError) {
33
- * console.error('Bad API key — run: deepline auth register');
34
- * } else if (err instanceof DeeplineError) {
35
- * console.error(`API error ${err.statusCode}: ${err.message}`);
36
- * }
37
- * }
38
- * ```
39
- */
40
30
  /**
41
31
  * Thrown when the API rejects the request due to an invalid or missing API key.
42
32
  *
@@ -58,8 +48,11 @@ export {
58
48
  * }
59
49
  * }
60
50
  * ```
51
+ *
52
+ * @sdkReference errors 090
61
53
  */
62
54
  export class AuthError extends DeeplineError {
55
+ /** Constructed by the SDK when Deepline rejects the caller's credentials. */
63
56
  constructor(message = 'Authentication failed. Check your DEEPLINE_API_KEY.') {
64
57
  super(message, 401, 'AUTH_ERROR');
65
58
  this.name = 'AuthError';
@@ -88,11 +81,14 @@ export class AuthError extends DeeplineError {
88
81
  * }
89
82
  * }
90
83
  * ```
84
+ *
85
+ * @sdkReference errors 100
91
86
  */
92
87
  export class RateLimitError extends DeeplineError {
93
88
  /** Milliseconds to wait before retrying, from the `Retry-After` response header. Defaults to 5000. */
94
89
  public retryAfterMs: number;
95
90
 
91
+ /** Constructed by the SDK after exhausting HTTP-level rate-limit retries. */
96
92
  constructor(retryAfterMs = 5000, message?: string) {
97
93
  super(
98
94
  message ?? `Rate limited. Retry after ${retryAfterMs}ms.`,
@@ -109,19 +105,38 @@ export class RateLimitError extends DeeplineError {
109
105
  * structured ToolExecutionError ontology. JavaScript has one prototype chain,
110
106
  * so this class extends RateLimitError and carries ToolExecutionError's stable
111
107
  * cross-bundle brand.
108
+ *
109
+ * This class appears in external SDK calls after HTTP 429 retries are
110
+ * exhausted. It also satisfies `instanceof ToolExecutionError` and, for a
111
+ * provider-owned rate limit, `instanceof ProviderTransientError`. Authored
112
+ * Plays should use `ProviderTransientError`; they do not need this
113
+ * compatibility class.
114
+ *
115
+ * @sdkReference errors 110
112
116
  */
113
117
  export class ToolRateLimitError extends RateLimitError {
118
+ /** Public tool id passed to `tools.execute`. */
114
119
  readonly toolId: string;
120
+ /** Provider responsible for the operation, or `null`. */
115
121
  readonly provider: string | null;
122
+ /** Provider operation name, or `null`. */
116
123
  readonly operation: string | null;
124
+ /** Stable machine-readable failure code when one exists. */
117
125
  override readonly code: string | undefined;
126
+ /** Boundary responsible for the failure. */
118
127
  readonly origin: ToolExecutionError['origin'];
128
+ /** Stable reason family for policy and diagnostics. */
119
129
  readonly category: ToolExecutionError['category'];
130
+ /** Whether repeating the same semantic call is delivery-safe. */
120
131
  readonly retryable: boolean;
132
+ /** Provider or Deepline request id, or `null`. */
121
133
  readonly requestId: string | null;
134
+ /** Network failure kind, or `null` for non-network failures. */
122
135
  readonly networkKind: ToolExecutionError['networkKind'];
136
+ /** Network boundary that failed, or `null` for non-network failures. */
123
137
  readonly networkScope: ToolExecutionError['networkScope'];
124
138
 
139
+ /** Constructed by the SDK after a structured tool HTTP 429. */
125
140
  constructor(message: string, options: ToolExecutionErrorOptions) {
126
141
  super(options.retryAfterMs ?? 5_000, message);
127
142
  this.name = 'ToolRateLimitError';
@@ -162,8 +177,11 @@ export class ToolRateLimitError extends RateLimitError {
162
177
  * }
163
178
  * }
164
179
  * ```
180
+ *
181
+ * @sdkReference errors 120
165
182
  */
166
183
  export class ConfigError extends DeeplineError {
184
+ /** Construct a local SDK configuration failure. */
167
185
  constructor(message: string) {
168
186
  super(message, undefined, 'CONFIG_ERROR');
169
187
  this.name = 'ConfigError';
@@ -130,7 +130,15 @@ export {
130
130
  ToolRateLimitError,
131
131
  ConfigError,
132
132
  } from './errors.js';
133
- export type { ToolExecutionErrorOptions } from './errors.js';
133
+ export type {
134
+ ProviderTransientErrorCategory,
135
+ ToolExecutionErrorCategory,
136
+ ToolExecutionFailureV1,
137
+ ToolExecutionErrorOrigin,
138
+ ToolExecutionErrorOptions,
139
+ ToolExecutionNetworkKind,
140
+ ToolExecutionNetworkScope,
141
+ } from './errors.js';
134
142
 
135
143
  // ——— Config ———
136
144
  export { resolveConfig, PROD_URL } from './config.js';
@@ -157,7 +157,7 @@ export const SDK_RELEASE = {
157
157
  // 0.1.253 makes play-page browser opening opt-in and retires --no-open.
158
158
  // 0.1.254 removes the internal operations tree from the published SDK CLI.
159
159
  // Operators use the checkout-local deepline-admin binary instead.
160
- version: '0.1.302',
160
+ version: '0.1.304',
161
161
  contracts: {
162
162
  api: {
163
163
  name: 'sdk-http-api',
@@ -1644,7 +1644,7 @@ export class PlayContextImpl {
1644
1644
  tableNamespace: string | null,
1645
1645
  update: Omit<PlayRowUpdate, 'key'>,
1646
1646
  ): void {
1647
- assertNoSecretTaint(update, 'ctx.map row update');
1647
+ assertNoSecretTaint(update, 'ctx.dataset row update');
1648
1648
  const rowScope = rowContext.getStore()?.mapScope;
1649
1649
  if (rowScope && key) {
1650
1650
  this.emitExecutionEvent({
@@ -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 resolvedPath: string | null = null;
753
- let rows: Record<string, unknown>[] | null = null;
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
- rows = normalizeRows(getAtPath(result, candidate));
758
- if (!rows) {
760
+ const candidateRows = normalizeRows(getAtPath(result, candidate));
761
+ if (!candidateRows) {
759
762
  continue;
760
763
  }
761
- if (rows.length > 0) {
762
- resolvedPath = candidate;
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
- if (!rows) continue;
772
- const storedPath = resolvedPath ?? path;
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] = { path: storedPath, rows };
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: Object.values(value._metadata.lists).map(
1589
- (list) => list.path,
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))
@@ -20,12 +20,30 @@ export const TOOL_EXECUTION_ERROR_SCHEMA_HEADER =
20
20
  const MAX_IDENTIFIER_LENGTH = 200;
21
21
  const MAX_CODE_LENGTH = 160;
22
22
 
23
+ /**
24
+ * The boundary responsible for a failed tool call.
25
+ *
26
+ * Use `provider` to distinguish a provider answer from caller input and
27
+ * Deepline infrastructure. `unknown` fails closed and must not trigger a
28
+ * waterfall fallback.
29
+ *
30
+ * @sdkReference errors 020
31
+ */
23
32
  export type ToolExecutionErrorOrigin =
24
33
  | 'caller'
25
34
  | 'provider'
26
35
  | 'deepline'
27
36
  | 'unknown';
28
37
 
38
+ /**
39
+ * The stable reason family for a failed tool call.
40
+ *
41
+ * Branch on this field only after narrowing to `ToolExecutionError`. Catch
42
+ * `ProviderTransientError` when the policy is simply “try the next read
43
+ * provider”; it is the safer and shorter waterfall contract.
44
+ *
45
+ * @sdkReference errors 030
46
+ */
29
47
  export type ToolExecutionErrorCategory =
30
48
  | 'validation'
31
49
  | 'authentication'
@@ -38,6 +56,13 @@ export type ToolExecutionErrorCategory =
38
56
  | 'internal'
39
57
  | 'unknown';
40
58
 
59
+ /**
60
+ * The transport failure observed when `category` is `network`.
61
+ *
62
+ * This is `null` for failures that are not network failures.
63
+ *
64
+ * @sdkReference errors 040
65
+ */
41
66
  export type ToolExecutionNetworkKind =
42
67
  | 'timeout'
43
68
  | 'dns'
@@ -46,27 +71,66 @@ export type ToolExecutionNetworkKind =
46
71
  | 'unavailable'
47
72
  | 'unknown';
48
73
 
74
+ /**
75
+ * The request boundary on which a network failure occurred.
76
+ *
77
+ * `deepline_to_provider` is provider-side. Client and runtime scopes are
78
+ * Deepline transport failures and never qualify as provider fallthrough.
79
+ *
80
+ * @sdkReference errors 050
81
+ */
49
82
  export type ToolExecutionNetworkScope =
50
83
  | 'client_to_deepline'
51
84
  | 'runtime_to_deepline'
52
85
  | 'deepline_to_provider';
53
86
 
87
+ /**
88
+ * Portable version-1 `tool_error` payload.
89
+ *
90
+ * This allowlisted shape crosses the API, runtime, and SDK boundaries.
91
+ * `message` remains on the Error object and is deliberately not a policy
92
+ * field.
93
+ *
94
+ * @sdkReference errors 064
95
+ */
54
96
  export type ToolExecutionFailureV1 = {
97
+ /** Payload version. */
55
98
  schemaVersion: typeof TOOL_EXECUTION_ERROR_SCHEMA_VERSION;
99
+ /** Public tool id passed to `tools.execute`. */
56
100
  toolId: string;
101
+ /** Provider responsible for the operation, or `null`. */
57
102
  provider: string | null;
103
+ /** Provider operation name, or `null`. */
58
104
  operation: string | null;
105
+ /** Stable machine-readable failure code, or `null`. */
59
106
  code: string | null;
107
+ /** Boundary responsible for the failure. */
60
108
  origin: ToolExecutionErrorOrigin;
109
+ /** Stable reason family. */
61
110
  category: ToolExecutionErrorCategory;
111
+ /** Whether repeating the same semantic call is delivery-safe. */
62
112
  retryable: boolean;
113
+ /** HTTP status when one exists, or `null`. */
63
114
  statusCode: number | null;
115
+ /** Provider or Deepline request id, or `null`. */
64
116
  requestId: string | null;
117
+ /** Suggested same-call retry delay in milliseconds, or `null`. */
65
118
  retryAfterMs: number | null;
119
+ /** Network failure kind, or `null`. */
66
120
  networkKind: ToolExecutionNetworkKind | null;
121
+ /** Network boundary that failed, or `null`. */
67
122
  networkScope: ToolExecutionNetworkScope | null;
68
123
  };
69
124
 
125
+ /**
126
+ * Constructor input for a structured tool failure.
127
+ *
128
+ * Deepline creates these values while decoding the versioned wire payload.
129
+ * Customer code normally reads `ToolExecutionError` fields instead of
130
+ * constructing an error.
131
+ *
132
+ * @sdkReference errors 065
133
+ */
70
134
  export type ToolExecutionErrorOptions = Omit<
71
135
  ToolExecutionFailureV1,
72
136
  'schemaVersion'
@@ -78,6 +142,12 @@ export type ToolExecutionErrorOptions = Omit<
78
142
  details?: Record<string, unknown>;
79
143
  };
80
144
 
145
+ /**
146
+ * Provider-owned failure categories that may fall through to another read
147
+ * provider.
148
+ *
149
+ * @sdkReference errors 060
150
+ */
81
151
  export type ProviderTransientErrorCategory =
82
152
  | 'rate_limit'
83
153
  | 'network'
@@ -112,16 +182,39 @@ function applyBrand(value: object, brand: symbol): void {
112
182
  *
113
183
  * The global brand preserves `instanceof DeeplineError` when a bundled play
114
184
  * and the runtime load separate physical copies of this module.
185
+ *
186
+ * @sdkReference errors 010
115
187
  */
116
188
  export class DeeplineError extends Error {
189
+ /** HTTP status when the failure crossed an HTTP boundary. */
190
+ statusCode?: number;
191
+ /** Stable machine-readable error code when one exists. */
192
+ code?: string;
193
+ /** Local diagnostic context; not a portable error contract. */
194
+ details?: Record<string, unknown>;
195
+
196
+ /**
197
+ * Construct a Deepline error.
198
+ *
199
+ * SDK and runtime code construct these errors. Application and Play code
200
+ * normally catches the public subclasses instead.
201
+ *
202
+ * @param message Human-readable failure summary.
203
+ * @param statusCode HTTP status when one exists.
204
+ * @param code Stable machine-readable code when one exists.
205
+ * @param details Local diagnostic context; never a portable error contract.
206
+ */
117
207
  constructor(
118
208
  message: string,
119
- public statusCode?: number,
120
- public code?: string,
121
- public details?: Record<string, unknown>,
209
+ statusCode?: number,
210
+ code?: string,
211
+ details?: Record<string, unknown>,
122
212
  ) {
123
213
  super(message);
124
214
  this.name = 'DeeplineError';
215
+ this.statusCode = statusCode;
216
+ this.code = code;
217
+ this.details = details;
125
218
  applyBrand(this, DEEPLINE_ERROR_BRAND);
126
219
  }
127
220
 
@@ -139,19 +232,46 @@ export class DeeplineError extends Error {
139
232
  * `retryable` means Deepline's delivery/idempotency contract says it is safe
140
233
  * to repeat the same semantic call. It does not describe durable receipt
141
234
  * repairability and does not make arbitrary side-effecting fallbacks safe.
235
+ *
236
+ * In a Play, catch `ProviderTransientError` to continue a read waterfall and
237
+ * let every other `ToolExecutionError` remain loud. In an SDK client, catch
238
+ * this base class when you need structured diagnostics for every tool failure.
239
+ *
240
+ * @sdkReference errors 070
142
241
  */
143
242
  export class ToolExecutionError extends DeeplineError {
243
+ /** Public tool id passed to `tools.execute`. */
144
244
  readonly toolId: string;
245
+ /** Provider responsible for the operation, or `null` when unattributed. */
145
246
  readonly provider: string | null;
247
+ /** Provider operation name, or `null` when unavailable. */
146
248
  readonly operation: string | null;
249
+ /** Boundary responsible for the failure. */
147
250
  readonly origin: ToolExecutionErrorOrigin;
251
+ /** Stable reason family for policy and diagnostics. */
148
252
  readonly category: ToolExecutionErrorCategory;
253
+ /**
254
+ * Whether repeating the same semantic call is delivery-safe.
255
+ *
256
+ * This does not mean the error may be ignored. Waterfall fallthrough is
257
+ * represented by `ProviderTransientError`.
258
+ */
149
259
  readonly retryable: boolean;
260
+ /** Provider or Deepline request id, or `null` when unavailable. */
150
261
  readonly requestId: string | null;
262
+ /** Suggested same-call retry delay in milliseconds, or `null`. */
151
263
  readonly retryAfterMs: number | null;
264
+ /** Network failure kind, or `null` for non-network failures. */
152
265
  readonly networkKind: ToolExecutionNetworkKind | null;
266
+ /** Network boundary that failed, or `null` for non-network failures. */
153
267
  readonly networkScope: ToolExecutionNetworkScope | null;
154
268
 
269
+ /**
270
+ * Construct a structured tool error.
271
+ *
272
+ * Deepline constructs this from the versioned `tool_error` payload.
273
+ * Application and Play code should catch it rather than create it.
274
+ */
155
275
  constructor(message: string, options: ToolExecutionErrorOptions) {
156
276
  super(
157
277
  message,
@@ -211,11 +331,20 @@ export function isProviderTransientFailure(input: {
211
331
  * A provider-owned transient failure that is safe to handle as an empty
212
332
  * waterfall leg. Validation, auth, billing, Deepline, and unknown failures
213
333
  * never satisfy this type.
334
+ *
335
+ * `retryable` remains independent: it says whether the same semantic call may
336
+ * be repeated safely. Falling through to a different read provider depends on
337
+ * this class, not on `retryable`.
338
+ *
339
+ * @sdkReference errors 080
214
340
  */
215
341
  export class ProviderTransientError extends ToolExecutionError {
342
+ /** Provider attribution is guaranteed for this subtype. */
216
343
  override readonly origin = 'provider' as const;
344
+ /** Provider failure category that made this error eligible for fallthrough. */
217
345
  declare readonly category: ProviderTransientErrorCategory;
218
346
 
347
+ /** Constructed by Deepline when a provider-owned transient failure arrives. */
219
348
  constructor(
220
349
  message: string,
221
350
  options: Omit<ToolExecutionErrorOptions, 'origin' | 'category'> & {