deepline 0.3.235 → 0.3.237

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.
@@ -2047,6 +2047,8 @@ export type BillingRecentUsageEntry = {
2047
2047
  | 'pending'
2048
2048
  | 'free'
2049
2049
  | 'failed';
2050
+ /** Final billing truth may be unknown even after execution completes. */
2051
+ charge_finality?: 'final' | 'pending' | 'unknown';
2050
2052
  rough_usd_cost: number | null;
2051
2053
  created_at: string;
2052
2054
  };
@@ -2076,6 +2078,10 @@ export type BillingUsageResult = {
2076
2078
  run_id: string | null;
2077
2079
  request_id?: string;
2078
2080
  total: number;
2081
+ /** Only true when total covers the complete matching history. */
2082
+ total_is_exact?: boolean;
2083
+ /** Proven matching entries, without assuming unscanned rows match. */
2084
+ total_lower_bound?: number;
2079
2085
  offset: number;
2080
2086
  limit: number;
2081
2087
  next_offset: number;
@@ -204,6 +204,18 @@ function describeRequestTarget(path: string): string {
204
204
  return 'Deepline';
205
205
  }
206
206
 
207
+ function networkDiagnosticUrl(value: string): string | undefined {
208
+ try {
209
+ const url = new URL(value);
210
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') return undefined;
211
+ // Diagnostics never retain URL credentials, query values, or fragments.
212
+ return `${url.origin}${url.pathname}`;
213
+ } catch {
214
+ // Preserve the original transport failure for malformed request URLs.
215
+ return undefined;
216
+ }
217
+ }
218
+
207
219
  function providerLabelFromToolId(toolId: string): string {
208
220
  const normalized = toolId.trim().toLowerCase();
209
221
  const provider = normalized.split(/[_:./-]+/)[0];
@@ -379,6 +391,10 @@ export class HttpClient {
379
391
  const baseUrl = this.config.baseUrl;
380
392
  const url = `${baseUrl}${path}`;
381
393
  const method = options?.method ?? 'GET';
394
+ const requestStartedAt = Date.now();
395
+ let fetchAttempts = 0;
396
+ let lastRequestUrl = url;
397
+ let networkFailedAt: number | undefined;
382
398
 
383
399
  const headers = this.authHeaders(options?.headers);
384
400
 
@@ -397,6 +413,7 @@ export class HttpClient {
397
413
  requestAttempts: for (let attempt = 0; attempt <= maxRetries; attempt++) {
398
414
  if (options?.signal?.aborted) {
399
415
  lastError = requestCancelledError(options.signal);
416
+ networkFailedAt = Date.now();
400
417
  break;
401
418
  }
402
419
  if (attempt > 0) {
@@ -410,6 +427,7 @@ export class HttpClient {
410
427
  await sleep(delayMs, options?.signal);
411
428
  } catch (error) {
412
429
  lastError = error instanceof Error ? error : new Error(String(error));
430
+ networkFailedAt = Date.now();
413
431
  break;
414
432
  }
415
433
  }
@@ -417,6 +435,7 @@ export class HttpClient {
417
435
  for (const candidateUrl of candidateUrls) {
418
436
  if (options?.signal?.aborted) {
419
437
  lastError = requestCancelledError(options.signal);
438
+ networkFailedAt = Date.now();
420
439
  break requestAttempts;
421
440
  }
422
441
  const controller = new AbortController();
@@ -432,17 +451,20 @@ export class HttpClient {
432
451
  });
433
452
 
434
453
  try {
454
+ const requestBody =
455
+ options?.formData !== undefined
456
+ ? typeof options.formData === 'function'
457
+ ? options.formData()
458
+ : options.formData
459
+ : options?.body !== undefined
460
+ ? JSON.stringify(options.body)
461
+ : undefined;
462
+ fetchAttempts += 1;
463
+ lastRequestUrl = candidateUrl;
435
464
  const response = await fetch(candidateUrl, {
436
465
  method,
437
466
  headers,
438
- body:
439
- options?.formData !== undefined
440
- ? typeof options.formData === 'function'
441
- ? options.formData()
442
- : options.formData
443
- : options?.body !== undefined
444
- ? JSON.stringify(options.body)
445
- : undefined,
467
+ body: requestBody,
446
468
  signal: controller.signal,
447
469
  });
448
470
 
@@ -605,6 +627,7 @@ export class HttpClient {
605
627
  return parsed as T;
606
628
  } catch (error) {
607
629
  clearTimeout(timeoutId);
630
+ networkFailedAt = Date.now();
608
631
  if (options?.signal?.aborted) {
609
632
  lastError = requestCancelledError(options.signal);
610
633
  break requestAttempts;
@@ -637,6 +660,19 @@ export class HttpClient {
637
660
  lastError,
638
661
  describeRequestTarget(path),
639
662
  );
663
+ const failedAt = networkFailedAt ?? Date.now();
664
+ const diagnosticUrl = networkDiagnosticUrl(lastRequestUrl);
665
+ const networkDetails = {
666
+ ...mappedNetworkError.details,
667
+ request: {
668
+ method,
669
+ ...(diagnosticUrl === undefined ? {} : { url: diagnosticUrl }),
670
+ failedAtUtc: new Date(failedAt).toISOString(),
671
+ // Elapsed time covers the entire logical request, including retries.
672
+ elapsedMs: Math.max(0, failedAt - requestStartedAt),
673
+ attempts: fetchAttempts,
674
+ },
675
+ };
640
676
  if (options?.toolId) {
641
677
  const code = mappedNetworkError.code;
642
678
  throw withErrorCause(
@@ -662,7 +698,7 @@ export class HttpClient {
662
698
  ? 'unknown'
663
699
  : 'unavailable',
664
700
  networkScope: 'client_to_deepline',
665
- details: mappedNetworkError.details,
701
+ details: networkDetails,
666
702
  publicDetails: options.idempotencyKey
667
703
  ? { idempotencyKey: options.idempotencyKey }
668
704
  : null,
@@ -676,7 +712,7 @@ export class HttpClient {
676
712
  withCoworkNetworkHint(mappedNetworkError.message),
677
713
  undefined,
678
714
  mappedNetworkError.code,
679
- mappedNetworkError.details,
715
+ networkDetails,
680
716
  ),
681
717
  lastError,
682
718
  );
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
200
200
  // getters keep their established compatibility behavior.
201
201
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
202
202
  // 0.3.90 is the first deliberately versioned SDK release for API v3.
203
- version: '0.3.235',
203
+ version: '0.3.237',
204
204
  updateSummary:
205
205
  'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
206
206
  packageCapabilities: {
@@ -11,6 +11,7 @@
11
11
  * cross-call session state.
12
12
  */
13
13
  import { createRequire } from 'node:module';
14
+ import { AsyncLocalStorage } from 'node:async_hooks';
14
15
  import { readFileSync } from 'node:fs';
15
16
  import path from 'node:path';
16
17
  import {
@@ -30,6 +31,18 @@ import {
30
31
 
31
32
  export { ABSURD_SCHEMA_VERSION };
32
33
 
34
+ const absurdSchedulerQuerySignal = new AsyncLocalStorage<AbortSignal>();
35
+
36
+ /** Applies cancellation to this async operation's Absurd SQL calls only.
37
+ * The SDK has no signal argument; keep cancellation in its Deepline adapter,
38
+ * without affecting other tasks sharing the same queryable or pool. */
39
+ export function runWithAbsurdSchedulerQuerySignal<T>(
40
+ signal: AbortSignal,
41
+ operation: () => Promise<T>,
42
+ ): Promise<T> {
43
+ return absurdSchedulerQuerySignal.run(signal, operation);
44
+ }
45
+
33
46
  /**
34
47
  * Builds an SDK `Queryable` backed by the hardened runtime scheduler pool.
35
48
  * Every query runs on a freshly checked-out pooled client and releases it,
@@ -43,13 +56,19 @@ export function absurdQueryableForRuntimeScheduler(
43
56
  sql: string,
44
57
  params?: unknown[],
45
58
  ): Promise<QueryResult<Row>> => {
46
- return withRuntimeSchedulerClient(poolOptions, async (client) => {
47
- const result = await client.query<Row & Record<string, unknown>>(
48
- sql,
49
- params,
50
- );
51
- return { rows: result.rows as Row[] };
52
- });
59
+ const signal = absurdSchedulerQuerySignal.getStore();
60
+ return withRuntimeSchedulerClient(
61
+ poolOptions,
62
+ async (client) => {
63
+ const result = await client.query<Row & Record<string, unknown>>(
64
+ sql,
65
+ params,
66
+ );
67
+ return { rows: result.rows as Row[] };
68
+ },
69
+ undefined,
70
+ { signal },
71
+ );
53
72
  },
54
73
  };
55
74
  }