firecrawl 4.41.0 → 4.42.1

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.
@@ -152,7 +152,7 @@ export async function getBatchScrapeErrors(
152
152
  try {
153
153
  const res = await http.get<{
154
154
  success?: boolean;
155
- data?: { errors: Array<Record<string, string>>; robotsBlocked: string[] };
155
+ data?: CrawlErrorsResponse;
156
156
  }>(`/v2/batch/scrape/${jobId}/errors`);
157
157
  if (res.status !== 200) throwForBadResponse(res, "get batch scrape errors");
158
158
  const payload = res.data?.data ?? (res.data as any);
@@ -162,7 +162,7 @@ export async function crawl(http: HttpClient, request: CrawlRequest, pollInterva
162
162
 
163
163
  export async function getCrawlErrors(http: HttpClient, crawlId: string): Promise<CrawlErrorsResponse> {
164
164
  try {
165
- const res = await http.get<{ success?: boolean; data?: { errors: Array<Record<string, string>>; robotsBlocked: string[] } }>(`/v2/crawl/${crawlId}/errors`);
165
+ const res = await http.get<{ success?: boolean; data?: CrawlErrorsResponse }>(`/v2/crawl/${crawlId}/errors`);
166
166
  if (res.status !== 200) throwForBadResponse(res, "get crawl errors");
167
167
  const payload = res.data?.data ?? (res.data as any);
168
168
  return { errors: payload.errors || [], robotsBlocked: payload.robotsBlocked || [] };
@@ -1,5 +1,6 @@
1
1
  import { type MapData, type MapOptions, type SearchResultWeb } from "../types";
2
2
  import { HttpClient } from "../utils/httpClient";
3
+ import { agentHintMetadata } from "../utils/agentHints";
3
4
  import {
4
5
  throwForBadResponse,
5
6
  normalizeAxiosError,
@@ -65,7 +66,7 @@ export async function map(
65
66
  description: (item as any).description,
66
67
  });
67
68
  }
68
- return { id: res.data.id, links };
69
+ return { id: res.data.id, links, ...agentHintMetadata(res.data) };
69
70
  } catch (err: any) {
70
71
  if (err?.isAxiosError) return normalizeAxiosError(err, "map");
71
72
  throw err;
@@ -1,5 +1,6 @@
1
1
  import { type Document, type ParseFile, type ParseOptions } from "../types";
2
2
  import { HttpClient } from "../utils/httpClient";
3
+ import { agentHintMetadata } from "../utils/agentHints";
3
4
  import { ensureValidParseOptions } from "../utils/validation";
4
5
  import {
5
6
  throwForBadResponse,
@@ -88,7 +89,7 @@ export async function parse(
88
89
  if (res.status !== 200 || !res.data?.success) {
89
90
  throwForBadResponse(res, "parse");
90
91
  }
91
- return (res.data.data || {}) as Document;
92
+ return { ...res.data.data, ...agentHintMetadata(res.data) };
92
93
  } catch (err: any) {
93
94
  if (err?.isAxiosError) return normalizeAxiosError(err, "parse");
94
95
  throw err;
@@ -6,6 +6,7 @@ import {
6
6
  type ScrapeOptions,
7
7
  } from "../types";
8
8
  import { HttpClient } from "../utils/httpClient";
9
+ import { agentHintMetadata } from "../utils/agentHints";
9
10
  import { ensureValidScrapeOptions } from "../utils/validation";
10
11
  import {
11
12
  throwForBadResponse,
@@ -131,7 +132,7 @@ export async function scrape(
131
132
  if (res.status !== 200 || !res.data?.success) {
132
133
  throwForBadResponse(res, "scrape");
133
134
  }
134
- return (res.data.data || {}) as Document;
135
+ return { ...res.data.data, ...agentHintMetadata(res.data) };
135
136
  } catch (err: any) {
136
137
  const delayMs = autoResume !== false ? processingContinuesDelayMs(err) : undefined;
137
138
  if (
@@ -8,6 +8,7 @@ import {
8
8
  type SearchResultImages,
9
9
  } from "../types";
10
10
  import { HttpClient } from "../utils/httpClient";
11
+ import { agentHintMetadata } from "../utils/agentHints";
11
12
  import { ensureValidScrapeOptions } from "../utils/validation";
12
13
  import {
13
14
  throwForBadResponse,
@@ -101,7 +102,7 @@ export async function search(
101
102
  throwForBadResponse(res, "search");
102
103
  }
103
104
  const data = (res.data.data || {}) as Record<string, any>;
104
- const out: SearchData = {};
105
+ const out: SearchData = { ...agentHintMetadata(res.data) };
105
106
  if (res.data.warning) out.warning = res.data.warning;
106
107
  if (data.web) out.web = transformArray<SearchResultWeb>(data.web);
107
108
  if (data.news) out.news = transformArray<SearchResultNews>(data.news);
package/src/v2/types.ts CHANGED
@@ -678,6 +678,8 @@ export interface DocumentMetadata {
678
678
  }
679
679
 
680
680
  export interface Document {
681
+ /** API guidance, separate from extracted page content. */
682
+ agent_hints?: string[];
681
683
  markdown?: string;
682
684
  html?: string;
683
685
  rawHtml?: string;
@@ -820,6 +822,7 @@ export interface SearchResultImages {
820
822
  }
821
823
 
822
824
  export interface SearchData {
825
+ agent_hints?: string[];
823
826
  warning?: string;
824
827
  web?: Array<SearchResultWeb | Document>;
825
828
  news?: Array<SearchResultNews | Document>;
@@ -1086,6 +1089,7 @@ export interface BatchScrapeJob {
1086
1089
  }
1087
1090
 
1088
1091
  export interface MapData {
1092
+ agent_hints?: string[];
1089
1093
  id?: string;
1090
1094
  links: SearchResultWeb[];
1091
1095
  }
@@ -1449,9 +1453,83 @@ export interface AgentExchangeOptions {
1449
1453
  toolkits?: string[];
1450
1454
  maxCalls?: number;
1451
1455
  requireApproval?: boolean;
1452
- /** Answers a pendingApproval from the previous turn of the thread. */
1456
+ /**
1457
+ * Answers a pendingApproval from the previous turn of the thread. A `terms`
1458
+ * approval is accepted or declined as a whole: `callIds` and `always` are
1459
+ * ignored on it.
1460
+ */
1453
1461
  approve?: { approvalId: string; callIds?: string[]; always?: boolean };
1454
1462
  decline?: { approvalId: string };
1463
+ /**
1464
+ * What to do when a provider the agent would use needs data terms the team
1465
+ * has not accepted. Gated providers are never called in any mode:
1466
+ * - "skip" (server default): answer with accepted providers only and list
1467
+ * the gated ones in `exchange.skippedProviders`.
1468
+ * - "ask": the same, plus `exchange.requiresAction` and a `terms`
1469
+ * pendingApproval. Get your user's explicit consent, call terms/accept,
1470
+ * then continue the thread with `approve: { approvalId }`.
1471
+ * Terms gating is rolling out: until it is on for a thread, none of the
1472
+ * terms fields appear. There is no auto-accept mode. Omitted on a follow-up turn inherits the
1473
+ * previous turn's value.
1474
+ */
1475
+ onTermsRequired?: AgentOnTermsRequired;
1476
+ }
1477
+
1478
+ export type AgentOnTermsRequired = "skip" | "ask";
1479
+
1480
+ /** A gated provider the run would have used but did not. */
1481
+ export interface AgentSkippedProvider {
1482
+ provider: string;
1483
+ name: string;
1484
+ capability?: string;
1485
+ /** What it would have added, in the agent's words. */
1486
+ adds?: string;
1487
+ reason: "terms_required";
1488
+ /** The gating terms version. */
1489
+ version: string;
1490
+ /** Where a person accepts the terms in the dashboard. */
1491
+ termsUrl: string;
1492
+ }
1493
+
1494
+ /**
1495
+ * The exact Exchange calls to view and accept a provider's terms. Nothing here
1496
+ * is executed for you, and terms/accept must only be called after the user
1497
+ * has explicitly agreed to that provider's terms.
1498
+ */
1499
+ export interface AgentTermsRequiredAction {
1500
+ type: "accept_terms";
1501
+ /**
1502
+ * The `terms` pendingApproval that answers this. After the user agrees and
1503
+ * terms/accept succeeds, continue the thread with
1504
+ * `exchange.approve: { approvalId }`, or refuse with `decline`.
1505
+ */
1506
+ approvalId: string;
1507
+ providers: {
1508
+ provider: string;
1509
+ name: string;
1510
+ capability?: string;
1511
+ adds?: string;
1512
+ version: string;
1513
+ /** null when the catalog published no digest; terms/show returns it. */
1514
+ digest: string | null;
1515
+ url: string;
1516
+ show: {
1517
+ provider: "firecrawl";
1518
+ capability: "terms/show";
1519
+ options: { provider: string };
1520
+ };
1521
+ accept: {
1522
+ provider: "firecrawl";
1523
+ capability: "terms/accept";
1524
+ options: {
1525
+ provider: string;
1526
+ version: string;
1527
+ /** null when the catalog published no digest; terms/show returns it. */
1528
+ digest: string | null;
1529
+ confirmed: true;
1530
+ };
1531
+ };
1532
+ }[];
1455
1533
  }
1456
1534
 
1457
1535
  /** Per-run summary reported on a status response. */
@@ -1460,8 +1538,26 @@ export interface AgentExchangeSummary {
1460
1538
  /** What the run resolved to after thread inheritance, not what it requested. */
1461
1539
  toolkits?: string[];
1462
1540
  requireApproval?: boolean;
1541
+ onTermsRequired?: AgentOnTermsRequired;
1463
1542
  paidCalls: number;
1464
1543
  creditsUsed: number | null;
1544
+ /** Gated providers that would have helped and were not used. Any mode. */
1545
+ skippedProviders?: AgentSkippedProvider[];
1546
+ /** "ask" mode, when a terms offer ended the turn. */
1547
+ requiresAction?: AgentTermsRequiredAction;
1548
+ }
1549
+
1550
+ /** A provider in a `terms` pendingApproval. */
1551
+ export interface AgentTermsGate {
1552
+ provider: string;
1553
+ name: string;
1554
+ logo?: string;
1555
+ capability?: string;
1556
+ adds?: string;
1557
+ version: string;
1558
+ /** null when the catalog published no digest; terms/show returns it. */
1559
+ digest: string | null;
1560
+ url: string;
1465
1561
  }
1466
1562
 
1467
1563
  /** A follow-up the agent offers for the next turn of the thread. */
@@ -1470,26 +1566,52 @@ export interface AgentSuggestion {
1470
1566
  prompt: string;
1471
1567
  }
1472
1568
 
1473
- /** A turn that ended waiting for the caller to allow or refuse paid calls. */
1474
- export interface PendingApproval {
1569
+ /** A paid call a `calls` pendingApproval is holding back. */
1570
+ export interface PendingApprovalCall {
1571
+ id: string;
1572
+ provider: string;
1573
+ capability: string;
1574
+ input: Record<string, unknown>;
1575
+ more?: Record<string, unknown>[];
1576
+ creditsEstimate: number | null;
1577
+ }
1578
+
1579
+ interface PendingApprovalBase {
1475
1580
  id: string;
1476
1581
  reason: string;
1477
- calls: {
1478
- id: string;
1479
- provider: string;
1480
- capability: string;
1481
- input: Record<string, unknown>;
1482
- more?: Record<string, unknown>[];
1483
- creditsEstimate: number | null;
1484
- }[];
1485
1582
  resolution: null | {
1486
1583
  approved: boolean;
1584
+ /** Calls approved. Ignored on terms offers. */
1487
1585
  callIds: string[];
1488
1586
  always: boolean;
1489
1587
  byRunId: string;
1490
1588
  };
1491
1589
  }
1492
1590
 
1591
+ /**
1592
+ * A turn that ended waiting for the caller to allow or refuse paid calls.
1593
+ * `kind` is absent on items written before terms offers existed.
1594
+ */
1595
+ export interface PendingCallsApproval extends PendingApprovalBase {
1596
+ kind?: "calls";
1597
+ calls: PendingApprovalCall[];
1598
+ terms?: never;
1599
+ }
1600
+
1601
+ /**
1602
+ * A turn that ended waiting for the caller to accept providers' data terms
1603
+ * ("ask" mode). `calls` is always empty (typed `never[]` rather
1604
+ * than `[]` so existing `pendingApproval.calls[0]` code still compiles).
1605
+ */
1606
+ export interface PendingTermsApproval extends PendingApprovalBase {
1607
+ kind: "terms";
1608
+ calls: never[];
1609
+ terms: AgentTermsGate[];
1610
+ }
1611
+
1612
+ /** Narrow on `kind === "terms"`. */
1613
+ export type PendingApproval = PendingCallsApproval | PendingTermsApproval;
1614
+
1493
1615
  export interface AgentResponse {
1494
1616
  success: boolean;
1495
1617
  id: string;
@@ -1833,6 +1955,8 @@ export interface CrawlErrorsResponse {
1833
1955
  url: string;
1834
1956
  code?: string;
1835
1957
  error: string;
1958
+ /** Present when the page needs provider terms accepted first. */
1959
+ requiresAction?: RequiresAction;
1836
1960
  }[];
1837
1961
  robotsBlocked: string[];
1838
1962
  }
@@ -1876,6 +2000,7 @@ export function parseRequiresAction(value: unknown): RequiresAction | undefined
1876
2000
  }
1877
2001
 
1878
2002
  export class SdkError extends Error {
2003
+ declare agent_hints?: string[];
1879
2004
  requestId?: string;
1880
2005
  status?: number;
1881
2006
  code?: string;
@@ -1956,11 +2081,13 @@ export interface BrowserExecuteResponse {
1956
2081
  stderr?: string;
1957
2082
  exitCode?: number;
1958
2083
  killed?: boolean;
2084
+ truncated?: boolean;
1959
2085
  error?: string;
1960
2086
  }
1961
2087
 
1962
2088
  export interface BrowserDeleteResponse {
1963
2089
  success: boolean;
2090
+ status?: string;
1964
2091
  sessionDurationMs?: number;
1965
2092
  creditsBilled?: number;
1966
2093
  error?: string;
@@ -0,0 +1,7 @@
1
+ /** Keep response guidance as metadata when convenience methods unwrap data. */
2
+ export function agentHintMetadata(body: unknown): { agent_hints?: string[] } {
3
+ const hints = (body as { agent_hints?: unknown } | null)?.agent_hints;
4
+ return Array.isArray(hints) && hints.every((hint) => typeof hint === "string")
5
+ ? { agent_hints: [...hints] }
6
+ : {};
7
+ }
@@ -1,11 +1,12 @@
1
1
  import { type AxiosError, type AxiosResponse } from "axios";
2
2
  import { SdkError, JobTimeoutError, parseRequiresAction } from "../types";
3
+ import { agentHintMetadata } from "./agentHints";
3
4
 
4
5
  export function throwForBadResponse(resp: AxiosResponse, action: string): never {
5
6
  const status = resp.status;
6
7
  const body = resp.data || {};
7
8
  const msg = body?.error || body?.message || `Request failed (${status}) while trying to ${action}`;
8
- throw new SdkError(
9
+ throw Object.assign(new SdkError(
9
10
  msg,
10
11
  status,
11
12
  body?.code,
@@ -13,7 +14,7 @@ export function throwForBadResponse(resp: AxiosResponse, action: string): never
13
14
  undefined,
14
15
  body?.chargeId,
15
16
  parseRequiresAction(body?.requiresAction),
16
- );
17
+ ), agentHintMetadata(body));
17
18
  }
18
19
 
19
20
  export function normalizeAxiosError(err: AxiosError, action: string): never {
@@ -21,7 +22,7 @@ export function normalizeAxiosError(err: AxiosError, action: string): never {
21
22
  const body: any = err.response?.data;
22
23
  const message = body?.error || err.message || `Request failed${status ? ` (${status})` : ""} while trying to ${action}`;
23
24
  const code = (body?.code as string) || err.code;
24
- throw new SdkError(
25
+ throw Object.assign(new SdkError(
25
26
  message,
26
27
  status,
27
28
  code,
@@ -29,7 +30,7 @@ export function normalizeAxiosError(err: AxiosError, action: string): never {
29
30
  undefined,
30
31
  body?.chargeId,
31
32
  parseRequiresAction(body?.requiresAction),
32
- );
33
+ ), agentHintMetadata(body));
33
34
  }
34
35
 
35
36
  export function isRetryableError(err: any): boolean {
@@ -64,4 +65,3 @@ export function isRetryableError(err: any): boolean {
64
65
  // Default: retry on unknown errors (safer than not retrying)
65
66
  return true;
66
67
  }
67
-
@@ -4,6 +4,7 @@ import axios, {
4
4
  type AxiosResponse,
5
5
  } from "axios";
6
6
  import { getVersion } from "./getVersion";
7
+ import { pinToApiOrigin } from "../../utils/apiOrigin";
7
8
 
8
9
  export interface HttpClientOptions {
9
10
  apiKey: string;
@@ -64,6 +65,7 @@ export class HttpClient {
64
65
  config.headers = {
65
66
  ...(config.headers || {}),
66
67
  };
68
+ if (config.url) config.url = pinToApiOrigin(this.apiUrl, config.url);
67
69
 
68
70
  let lastError: any;
69
71
  for (let attempt = 0; attempt < this.maxRetries; attempt++) {