fix(integrations): validation pass over Crunchbase, PitchBook, and CB Insights (#6925)

* fix(crunchbase): widen tier-gated collection allowlists and cap the deleted feed

The deleted-entity, autocomplete, and fields-metadata allowlists each held only
the collections the narrowest package tier publishes, so requests valid on a
richer package were rejected locally before any request went out. An Advanced
Financials key could not read the funding-round deletion feed at all.

- deleted-entity collections: 9 -> the 14-collection union across all tiers
- autocomplete and fields-metadata: 14 -> all 43 collections
- clamp the deleted feed to its documented max of 25, not Search's 1000
- offer "All collections" so the cross-collection feed stays reachable
- name the richer-tier card additions instead of presenting the base set as exhaustive

Also rewrites a test that asserted the broken behavior and tightens a
substring URL assertion that passed on the value it was meant to reject.

* fix(pitchbook): stop a rejected API key reaching block output and logs

PitchBook's 401 body echoes the submitted key back inside `message`. No
PitchBook tool declared an `errorExtractor`, so the failure fell through to the
generic chain, whose first entry returns `data.message` verbatim — putting the
credential in the block error, the run log, and any agent context reading the
failure. The existing scrubber sat in `transformResponse`, which never runs on a
non-ok response.

- add a `pitchbook-errors` extractor that replaces the unauthorized message with
  a fixed string, and wire it through all 91 tools
- the extractor returns undefined unless the body carries a `message`, so a
  foreign 401 on the shared fallback chain is never labelled a PitchBook failure
- correct `investor_preferences.preferredIndustry` to the shape the API returns
- make `company_industries.emergingSpaces` opaque; its item shape is undocumented
- reject a non-list of article ids instead of throwing a bare TypeError

* fix(cbinsights): reject malformed input instead of silently rescoping a billed query

CB Insights is metered, so a filter that fails to parse must fail the request —
dropping it does not narrow the result, it charges for a query the caller never
asked for.

- reject an unrecognized boolean rather than dropping it, which had been
  widening a VC-backed firmographics search
- reject a non-numeric limit instead of falling back to the endpoint default
- reject non-text filter entries instead of stringifying them to "[object Object]"
- accept only asc/desc for sort direction; a typo had returned the bottom of a
  metered result set as though it were the top
- treat a whitespace-only numeric bound as unset, not as zero
- drop `totalHits`/`totalHitsRelation` from list business relationships; that
  endpoint reports no total, so both were permanently null
- trim `nextPageToken`, matching the id fields

Also moves the token cache onto `lru-cache` per the in-process caching rule,
replacing hand-rolled TTL arithmetic and a manual prune.

* chore(harmonic): drop the team-key help text from the credential descriptor

* chore(tools): regenerate tool metadata for the validation fixes

* fix(tools): redact the retained error body, not just the message

Scrubbing the extracted message left the raw provider body reachable:
`createTransformedErrorFromErrorInfo` attaches `errorInfo.data` to the thrown
error and the executor surfaces it on the failed tool's `output.data`, so a
PitchBook key rejected with an echoing 401 still reached block output and agent
tool results via `output.data.message`.

- add an optional `redactData` to the error-extractor contract, so an extractor
  that exists because a provider echoes a credential can replace the body too
- retain `redactErrorData(errorInfo, extractorId)` in place of the raw body
- PitchBook replaces only the unauthorized body; every other failure is untouched
- cover the executor path itself, since asserting on the redactor directly still
  passes when nothing is wired to it
This commit is contained in:
Waleed
2026-08-20 22:22:28 -07:00
committed by GitHub
parent aca152c7fb
commit dbbe99e473
120 changed files with 807 additions and 153 deletions
@@ -489,8 +489,6 @@ Retrieve partnerships, client/vendor relationships, and licensing activity for u
| --------- | ---- | ----------- |
| `orgs` | json | Organizations as \[\{orgId, businessRelationships\}\] |
| `nextPageToken` | string | Token for the next page, or null when there are no more results |
| `totalHits` | number | Total number of matching records |
| `totalHitsRelation` | string | Whether totalHits is exact \('eq'\) or a floor \('gte', used above 10,000\) |
### CB Insights List Management and Board
@@ -79,7 +79,7 @@ Look up a single Crunchbase organization by permalink or UUID, returning the req
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
| `entityId` | string | Yes | Organization permalink \(e.g. "tesla-motors"\) or UUID |
| `fieldIds` | json | No | Organization fields to return, e.g. \["identifier","name","founded_on","categories"\]. Defaults to identifier, name, short_description, website_url, linkedin, location_identifiers, categories, founded_on, num_employees_enum, operating_status, rank_org, permalink. |
| `cardIds` | json | No | Related-entity cards to include, e.g. \["founders","headquarters_address"\]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. A card returns at most 100 items. |
| `cardIds` | json | No | Related-entity cards to include, e.g. \["founders","headquarters_address"\]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. Richer packages add acquiree_acquisitions, acquirer_acquisitions, investors, ipos, jobs, participated_funding_rounds, participated_funds, participated_investments, press_references, raised_funding_rounds, raised_funds, raised_investments. A card returns at most 100 items. |
#### Output
@@ -126,7 +126,7 @@ Look up a single Crunchbase person by permalink or UUID, returning the requested
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
| `entityId` | string | Yes | Person permalink \(e.g. "elon-musk"\) or UUID |
| `fieldIds` | json | No | Person fields to return, e.g. \["identifier","name","primary_job_title","primary_organization"\]. Defaults to identifier, name, first_name, last_name, primary_job_title, primary_organization, short_description, location_identifiers, linkedin, rank_person, permalink. |
| `cardIds` | json | No | Related-entity cards to include, e.g. \["jobs","primary_organization"\]. Available: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. A card returns at most 100 items. |
| `cardIds` | json | No | Related-entity cards to include, e.g. \["jobs","primary_organization"\]. Available on every license tier: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. Advanced Financials adds participated_funding_rounds, participated_funds, participated_investments, partner_funding_rounds, partner_investments, press_references. A card returns at most 100 items. |
#### Output
@@ -317,7 +317,7 @@ Suggest Crunchbase entities matching a typed query, returning the permalinks and
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
| `query` | string | Yes | Text to autocomplete against, e.g. "airbnb" |
| `collectionIds` | json | No | Collections to search, e.g. \["organizations","people"\]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection. |
| `collectionIds` | json | No | Collections to search, e.g. \["organizations","people"\]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection. |
| `limit` | number | No | Suggestions to return, max 25 \(default 10\) |
#### Output
@@ -335,10 +335,10 @@ List entities Crunchbase has deleted, so a mirrored copy can be pruned in step w
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
| `collection` | string | No | Restrict the feed to a single collection: categories, event_appearances, events, ipos, jobs, locations, organizations, ownerships, or people. Leave empty to read the feed across collections. |
| `collectionIds` | json | No | Collections to include when reading the cross-collection feed, e.g. \["organizations","people"\]. Ignored when a single collection is set. |
| `collection` | string | No | Restrict the feed to a single collection: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, or press_references. Which of them your key can read depends on your Crunchbase package. Leave empty to read the feed across collections. |
| `collectionIds` | json | No | Collections to include when reading the cross-collection feed, e.g. \["organizations","people"\]. One or more of: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, press_references. Ignored when a single collection is set. |
| `deletedAtOrder` | string | No | Order by deletion time: "asc" \(default\) or "desc" |
| `limit` | number | No | Rows to return per page |
| `limit` | number | No | Rows to return per page, 1-25 \(default 10\) |
| `afterId` | string | No | UUID of the last row on the current page, to fetch the next page |
| `beforeId` | string | No | UUID of the first row on the current page, to fetch the previous page |
@@ -358,7 +358,7 @@ List the field ids, types, and descriptions each Crunchbase collection publishes
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
| `collectionIds` | json | No | Collections to describe, e.g. \["organizations","people"\]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection. |
| `collectionIds` | json | No | Collections to describe, e.g. \["organizations","people"\]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection. |
#### Output
@@ -427,8 +427,6 @@ Retrieve the industry classification, verticals, keywords, and emerging spaces a
| ↳ `description` | string | Vertical label |
| `keywords` | array | Keywords associated with the company |
| `emergingSpaces` | array | Analyst-defined emerging spaces the company is placed in |
| ↳ `code` | string | Emerging space code |
| ↳ `description` | string | Emerging space label |
### PitchBook Company Investors
@@ -1840,8 +1838,14 @@ Retrieve what an investor targets: check size, deal size, valuation, revenue, ge
| ↳ `code` | string | Preference code |
| ↳ `description` | string | Preference label |
| `preferredIndustry` | array | Industries the investor targets |
| ↳ `code` | string | Industry code |
| ↳ `description` | string | Industry label |
| ↳ `industryCode` | json | Most specific industry classification |
| ↳ `industrySector` | object | Top-level sector |
| ↳ `code` | string | Sector code |
| ↳ `description` | string | Sector label |
| ↳ `industryGroup` | object | Industry group within the sector |
| ↳ `code` | string | Group code |
| ↳ `description` | string | Group label |
| ↳ `primary` | boolean | Whether this is the primary industry |
| `preferredDealTypes` | array | Deal types the investor targets |
| ↳ `code` | string | Deal type code |
| ↳ `description` | string | Deal type label |