Conversation
Flow-authoring agents were hardcoding provider='crustdata' because the longDescription opened with "Uses the Crustdata PersonDB" and the filter list didn't tag crustdata-only features. Flow 11091 (LinkedIn Lead Gen) is the concrete case that ate six crustdata batches instead of one fullenrich search. - longDescription now opens with a PROVIDERS block that states fullenrich is default and preferred, and only lists crustdata as a fallback. - Each filter in the SEARCH CRITERIA list is tagged *(crustdata only)* where appropriate so an agent reading the doc knows which filters force a provider switch. - Class JSDoc updated to match.
…nt warnings Based on subagent testing (bubblelab-test-bubble against people-search-tool) and FullEnrich's published accepted-values reference (https://docs.fullenrich.com/ → Accepted Filter Values), fix four silent-failure modes on the FullEnrich branch: - mapSeniorityToFullEnrich: drop Entry Level / Associate / In Training → Associate. FullEnrich's seniority enum is {Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior} — no Associate. Unknown values are now pushed into the result's warnings array instead of silently forwarding a no-op filter value. - mapFunctionCategoryToFullEnrich (new): translate the Crustdata-flavored functionCategories enum to FullEnrich's top-level function enum (Administrative, Consulting & Advisory, Customer Service, Design, Education, Executive & Leadership, Finance, Human Resources, Legal, Marketing, Media & Communications, Medical & Health, Operations, Product, Project & Program Management, Public Safety & Security, Research & Science, Retail & Consumer, Sales, Software, Traditional Engineering, Transportation & Logistics, …). Values that don't map are passed through (they might be valid FE subfunctions like 'Recruiting/Talent Acquisition') and recorded in warnings. - Email enrichment warnings: enrichPeopleEmails now returns { people, warnings } and callers merge them into the tool result. Timeouts, insufficient-credits stops, start-failures, and thrown errors are each surfaced rather than silently returning un-enriched people. Bumped the poll interval from 3s → 5s (no UX cost; enrichment is 30–120s typically). - Docs: enrichEmails describe string now calls out the 30–120s latency and points at the warnings array so flow-authoring agents surface progress / timeout UI instead of wiring it into user-facing flows blind.
… guidance Subagent testing showed three remaining failure modes after the warnings-only version (see test iterations v1→v3): - FullEnrich silently soft-matches company names, so `companyName: 'Stripe'` returned 'Stripes' + ex-Stripe folks. - FullEnrich's person-level seniority/function tags are ML-derived and imperfect; `functionCategories: ['Software'] + seniorityLevels: ['VP']` surfaced a 'VP of Strategic Finance'. - Warnings alone get ignored — agents wire them into flows and ship the wrong data. Fixes: 1. Set `exact_match: true` on every string filter in the FE request. FE's docs (verified via https://docs.fullenrich.com/api/v2/people/search/post) confirm this toggle disables fuzzy matching at the provider level. Result: Stripe vs Stripes is strictly resolved. 2. Replace soft-warn with HARD ERROR for: - seniorityLevels with values outside FE's enum (Owner/Founder/C-level/ Partner/VP/Head/Director/Manager/Senior) — error lists the accepted values AND points to provider="crustdata" for Crustdata-only values. - functionCategories with values outside FE's top-level functions OR known subfunctions — error enumerates acceptable values. - companyIndustries with values outside FE's taxonomy (uses alias map first — 'SaaS' → 'Software Development', 'Fintech' → 'Financial Services', etc. — so common shorthand works). - Crustdata-only filters (locationRadius, excludeCompanies, minYearsExperience, etc.) when provider routes to FE — error tells the caller to switch providers or drop the filter. 3. Add 'HOW TO GET HIGH-QUALITY LEADS' block to the tool's longDescription. Most impactful guidance: prefer an explicit `jobTitles` list over relying on ML-derived seniority/function tags alone. Subagent retests went 0/10 → 10/10 on first attempt across Stripe-engineering and Customer-Success scenarios after this nudge. The canonical high-precision recipe (`jobTitles` + `country` + `minCompanyHeadcount` + `companyIndustries`) is called out explicitly. Test evidence: https://docs.fullenrich.com/ accepted filter values list + retest flows 468 (3/3 Stripe eng VPs) and 470 (5/5 German CSMs) succeeded on first iteration with no instruction beyond the tool description.
📝 WalkthroughWalkthroughThis PR bumps package versions from 0.1.300 to 0.1.301 across the monorepo and significantly enhances the people-search-tool with FullEnrich API support, including mapping validators for seniority/function/industry filters, warning aggregation, conditional hard-fail routing for unsupported filters, and improved email enrichment handling. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying bubblelab-documentation with
|
| Latest commit: |
c67607f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://67cb217c.bubblelab-documentation.pages.dev |
| Branch Preview URL: | https://fix-people-search-default-fu.bubblelab-documentation.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts (2)
1342-1356:⚠️ Potential issue | 🟡 MinorWarn when Crustdata email enrichment is requested but cannot run.
With the new
warningsresult field, this branch should also surface the missingFULLENRICH_API_KEYcase. TodayenrichEmails=truesilently returns un-enriched people when only the Crustdata credential is configured.Proposed warning path
- if ( - enrichEmails && - credentials[CredentialType.FULLENRICH_API_KEY] && - people.length > 0 - ) { + if (enrichEmails && people.length > 0) { + if (!credentials[CredentialType.FULLENRICH_API_KEY]) { + enrichmentWarnings.push( + 'Email enrichment was requested but skipped because FULLENRICH_API_KEY credential is not configured.' + ); + } else { const enrichResult = await this.enrichPeopleEmails( people, credentials, includePersonalEmails ?? false ); people = enrichResult.people; enrichmentWarnings.push(...enrichResult.warnings); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts` around lines 1342 - 1356, The branch that triggers email enrichment currently only runs when credentials[CredentialType.FULLENRICH_API_KEY] is present, which silently skips enrichment if the caller requested enrichEmails=true but only Crustdata is configured; update the logic around enrichEmails/credentials to detect the case where this.params.enrichEmails is true and credentials lacks CredentialType.FULLENRICH_API_KEY and push a user-facing warning into enrichmentWarnings (same array used after calling this.enrichPeopleEmails) explaining that FULLENRICH_API_KEY is missing and enrichment could not run; keep the existing call to this.enrichPeopleEmails unchanged when the key is present and still append any warnings returned from that call.
735-740:⚠️ Potential issue | 🟠 MajorApply boolean filter logic for
recentlyChangedJobs: falseinstead of silently dropping it.The schema accepts a boolean filter, but the implementation only checks for
trueand ignoresfalse. When a caller passesrecentlyChangedJobs: falsealongside another criterion, the false filter is silently dropped, returning recently-changed people unintentionally.Since FullEnrich's
current_company_days_since_last_job_changefield supports bothminandmax, update:
- Line 1470: Change
recentlyChangedJobs === truetorecentlyChangedJobs !== undefined- Lines 1573–1576: Handle both cases:
- ...(recentlyChangedJobs === true && { + ...(recentlyChangedJobs !== undefined && { current_company_days_since_last_job_change: [ - { max: 90 }, + recentlyChangedJobs ? { max: 90 } : { min: 91 }, ], }),This applies to lines 1470 and 1573–1576.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts` around lines 735 - 740, The recentlyChangedJobs boolean is being ignored when false; update the filter construction for recentlyChangedJobs so you check for recentlyChangedJobs !== undefined (not === true) and then map both values into FullEnrich's current_company_days_since_last_job_change range: when recentlyChangedJobs === true set an upper bound (max) to the "recent" threshold constant used in this module, and when recentlyChangedJobs === false set a lower bound (min) to one day beyond that threshold (or appropriate >recent threshold) so the false case excludes recently changed people; apply this change where recentlyChangedJobs is referenced and where current_company_days_since_last_job_change range filters are assembled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts`:
- Around line 44-53: Update the stale documentation comments in
people-search-tool (the block describing translation of Crustdata seniority and
the other doc blocks that repeat this behavior) to reflect current behavior:
unsupported filters now cause a hard error (do not say they're silently dropped
or only warn), and Crustdata is only used when provider="crustdata" (do not
claim the tool "falls back" to Crustdata otherwise); edit the comment that
begins "Translate the tool's Crustdata-flavored seniority vocabulary..." and the
other similar doc blocks to state these two facts clearly and mention that
FullEnrich mapping/validation happens prior to sending the request and will
raise on unsupported filter values.
- Around line 360-435: The current mapIndustryToFullEnrich function rejects any
industry not in FE_INDUSTRY_CANONICAL or FE_INDUSTRY_ALIASES; instead stop
pre-rejecting unknown valid FullEnrich values by deferring validation to the
FullEnrich API: in mapIndustryToFullEnrich (and related callers) remove the
hard-fail path that pushes values into unknown for lookup misses and either (a)
include the unknown raw/trimmed value in the returned mapped set to be sent to
FullEnrich, or (b) rename/repurpose the unknown array to "deferred" and ensure
those values are forwarded to the API for authoritative validation; keep
FE_INDUSTRY_CANONICAL and FE_INDUSTRY_ALIASES only for helpful alias expansion,
not for rejecting inputs.
---
Outside diff comments:
In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts`:
- Around line 1342-1356: The branch that triggers email enrichment currently
only runs when credentials[CredentialType.FULLENRICH_API_KEY] is present, which
silently skips enrichment if the caller requested enrichEmails=true but only
Crustdata is configured; update the logic around enrichEmails/credentials to
detect the case where this.params.enrichEmails is true and credentials lacks
CredentialType.FULLENRICH_API_KEY and push a user-facing warning into
enrichmentWarnings (same array used after calling this.enrichPeopleEmails)
explaining that FULLENRICH_API_KEY is missing and enrichment could not run; keep
the existing call to this.enrichPeopleEmails unchanged when the key is present
and still append any warnings returned from that call.
- Around line 735-740: The recentlyChangedJobs boolean is being ignored when
false; update the filter construction for recentlyChangedJobs so you check for
recentlyChangedJobs !== undefined (not === true) and then map both values into
FullEnrich's current_company_days_since_last_job_change range: when
recentlyChangedJobs === true set an upper bound (max) to the "recent" threshold
constant used in this module, and when recentlyChangedJobs === false set a lower
bound (min) to one day beyond that threshold (or appropriate >recent threshold)
so the false case excludes recently changed people; apply this change where
recentlyChangedJobs is referenced and where
current_company_days_since_last_job_change range filters are assembled.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 09269f25-fe08-4633-a4e4-243668a968fe
📒 Files selected for processing (8)
packages/bubble-core/package.jsonpackages/bubble-core/src/bubbles/tool-bubble/people-search-tool.tspackages/bubble-runtime/package.jsonpackages/bubble-scope-manager/package.jsonpackages/bubble-shared-schemas/package.jsonpackages/create-bubblelab-app/package.jsonpackages/create-bubblelab-app/templates/basic/package.jsonpackages/create-bubblelab-app/templates/reddit-scraper/package.json
| /** | ||
| * Translate the tool's Crustdata-flavored seniority vocabulary to the values | ||
| * FullEnrich's /people/search actually accepts. FullEnrich's seniority enum is: | ||
| * Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior. | ||
| * | ||
| * Unknown inputs are passed through unchanged — FullEnrich accepts the request | ||
| * but silently ignores unrecognized seniority values, so there is nothing to | ||
| * gain from dropping them client-side. Callers targeting seniority levels FE | ||
| * doesn't index (e.g. "Entry Level", "In Training") will get `warnings` | ||
| * pointing that out. |
There was a problem hiding this comment.
Update stale FullEnrich behavior docs.
These docs still say unknown values are passed through with warnings, unsupported filters are silently dropped, or that the tool “falls back” to Crustdata. The implementation now hard-errors on unsupported filters and uses Crustdata only when provider="crustdata".
Suggested direction
- * Unknown inputs are passed through unchanged — FullEnrich accepts the request
- * but silently ignores unrecognized seniority values, so there is nothing to
- * gain from dropping them client-side.
+ * Unknown inputs are returned in `unknown` so the FullEnrich path can fail
+ * fast with accepted-value guidance instead of silently broadening/narrowing
+ * the search.- * doesn't support ... languages).
+ * doesn't support. Unsupported filters on the FullEnrich path return a hard
+ * error; set provider='crustdata' when those filters are required.Also applies to: 105-110, 823-829, 861-868
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts` around
lines 44 - 53, Update the stale documentation comments in people-search-tool
(the block describing translation of Crustdata seniority and the other doc
blocks that repeat this behavior) to reflect current behavior: unsupported
filters now cause a hard error (do not say they're silently dropped or only
warn), and Crustdata is only used when provider="crustdata" (do not claim the
tool "falls back" to Crustdata otherwise); edit the comment that begins
"Translate the tool's Crustdata-flavored seniority vocabulary..." and the other
similar doc blocks to state these two facts clearly and mention that FullEnrich
mapping/validation happens prior to sending the request and will raise on
unsupported filter values.
| /** | ||
| * Canonical FE industry values (subset). Used for exact-match passthrough — | ||
| * we don't enumerate all ~400; a value not in aliases and not in this list is | ||
| * rejected with a pointer at the FE docs. This covers the canonical values | ||
| * most common for tech/sales prospecting. | ||
| */ | ||
| const FE_INDUSTRY_CANONICAL = new Set([ | ||
| 'Software Development', | ||
| 'Technology, Information and Internet', | ||
| 'Technology, Information and Media', | ||
| 'Financial Services', | ||
| 'Banking', | ||
| 'Insurance', | ||
| 'Hospitals and Health Care', | ||
| 'Health, Wellness & Fitness', | ||
| 'Biotechnology Research', | ||
| 'Pharmaceutical Manufacturing', | ||
| 'Retail', | ||
| 'Business Consulting and Services', | ||
| 'IT Services and IT Consulting', | ||
| 'Manufacturing', | ||
| 'Motor Vehicle Manufacturing', | ||
| 'Automotive', | ||
| 'Online Audio and Video Media', | ||
| 'Broadcast Media Production and Distribution', | ||
| 'Entertainment Providers', | ||
| 'Education', | ||
| 'Higher Education', | ||
| 'Government Administration', | ||
| 'Non-profit Organizations', | ||
| 'Marketing Services', | ||
| 'Advertising Services', | ||
| 'Law Practice', | ||
| 'Legal Services', | ||
| 'Real Estate', | ||
| 'Construction', | ||
| 'Oil and Gas', | ||
| 'Renewable Energy Power Generation', | ||
| 'Utilities', | ||
| 'Telecommunications', | ||
| 'Transportation, Logistics, Supply Chain and Storage', | ||
| 'Airlines and Aviation', | ||
| 'Hospitality', | ||
| 'Staffing and Recruiting', | ||
| 'Venture Capital and Private Equity Principals', | ||
| 'Computer Games', | ||
| 'Defense and Space Manufacturing', | ||
| 'E-Learning Providers', | ||
| 'Food and Beverage Services', | ||
| 'Research Services', | ||
| 'Investment Management', | ||
| 'Investment Banking', | ||
| 'Capital Markets', | ||
| ]); | ||
|
|
||
| function mapIndustryToFullEnrich(values: string[]): { | ||
| mapped: string[]; | ||
| unknown: string[]; | ||
| } { | ||
| const mapped = new Set<string>(); | ||
| const unknown: string[] = []; | ||
| for (const raw of values) { | ||
| const trimmed = raw.trim(); | ||
| if (FE_INDUSTRY_CANONICAL.has(trimmed)) { | ||
| mapped.add(trimmed); | ||
| continue; | ||
| } | ||
| const aliased = FE_INDUSTRY_ALIASES[trimmed.toLowerCase()]; | ||
| if (aliased) { | ||
| for (const v of aliased) mapped.add(v); | ||
| continue; | ||
| } | ||
| unknown.push(trimmed); | ||
| } | ||
| return { mapped: Array.from(mapped), unknown }; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
FullEnrich Accepted Filter Values Company Industry people search full list
💡 Result:
The FullEnrich API documentation provides a list of accepted filter values for company industries, company types, and other filters used in people and company searches. These are predefined enums that can be used as filters in the Search API endpoints. Company Industries (partial list from docs; full list is extensive): Import & Export, Think Tanks, Environmental Services, Architecture and Planning, Staffing and Recruiting, Legal Services, IT Services and IT Consulting, Insurance, Non-profit Organizations, Venture Capital and Private Equity, Principals, Home Health Care Services, Primary and Secondary Education, Software Development, Research Services, Law Practice, Real Estate, Individual and Family Services, Mental Health Care, Real Estate Agents and Brokers, Health and Human Services, Hospitality, Farming, Fundraising, Accounting, Medical Device, Beverage Manufacturing, Wellness and Fitness Services, Hospitals and Health Care, Marketing Services, Financial Services, Outsourcing and Offshoring Consulting, Chemical Manufacturing, Executive Offices, Advertising Services, Transportation Logistics Supply Chain and Storage, E-Learning Providers, Travel Arrangements, Education Management, Industry Associations, Information Technology & Services, Writing and Editing, Public Safety, Media Production, Business Consulting and Services, Oil and Gas, Railroad Equipment Manufacturing, Oil Gas and Mining, Strategic Management Services, Technology Information and Internet, Landscaping Services, Wholesale Building Materials, Sporting Goods Manufacturing, Motor Vehicle Manufacturing, Security and Investigations, Civic and Social Organizations, Apparel Manufacturing, Construction, Biotechnology Research, Spectator Sports, Farming Ranching Forestry, Housing and Community Development, Veterinary Services, Consumer Services, Utilities, Engineering Services, Lime and Gypsum Products Manufacturing, Circuses and Magic Shows, Fuel Cell Manufacturing, Savings Institutions, Death Care Services, and more (see official docs for complete exhaustive list). Company Types: Partnership, Nonprofit, Educational, Privately Held, Public Company, Self-Owned, Self-Employed, Government Agency. For people search, additional filters include current_company_industries (using the same industry list), current_company_types (same as above), current_position_seniority_level (e.g., Owner, Founder, C-level, Partner, VP, Head, Director, Senior, Manager; full list in docs), job functions, locations (continent, country, state/region, city), etc. Location filters support: Continent (e.g., North America), Country (English), State/Region (local language), City (local language). Recommend testing in UI for exact values. Seniority data is noted as "soon" in enums page, but people search supports current_position_seniority_level. These values are used in both People Search and Company Search APIs with OR logic within categories and AND across categories. For full up-to-date list and usage, refer to https://docs.fullenrich.com/api/v2/general/enums.
Citations:
- 1: https://docs.fullenrich.com/api/v2/general/enums
- 2: https://fullenrich.mintlify.app/api/v2/general/enums
- 3: https://docs.fullenrich.com/api/v2/company/search/post
- 4: https://docs.fullenrich.com/api/v2/people/search/post
🏁 Script executed:
# Search for calls to mapIndustryToFullEnrich
rg "mapIndustryToFullEnrich" packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts -A 5 -B 2Repository: bubblelabai/BubbleLab
Length of output: 936
🏁 Script executed:
# Check lines 1500-1506 mentioned in the review
sed -n '1495,1510p' packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.tsRepository: bubblelabai/BubbleLab
Length of output: 1628
🏁 Script executed:
# Search for how unknown industries are handled after mapping
rg "unknown" packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts -B 3 -A 3Repository: bubblelabai/BubbleLab
Length of output: 11367
Reject legitimate FullEnrich industries rejected outside the local subset.
FE_INDUSTRY_CANONICAL is a documented subset covering "most common for tech/sales prospecting," yet mapIndustryToFullEnrich hard-fails the request for any industry not in this local list or aliases. This means a legitimate FullEnrich canonical industry outside this subset is rejected before reaching the API. The error message itself points to the FullEnrich docs for the full list, implying the value might be valid there—but the code has already rejected it.
Include the complete FullEnrich industry taxonomy or defer industry validation to the FullEnrich API instead of pre-rejecting at the client level.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts` around
lines 360 - 435, The current mapIndustryToFullEnrich function rejects any
industry not in FE_INDUSTRY_CANONICAL or FE_INDUSTRY_ALIASES; instead stop
pre-rejecting unknown valid FullEnrich values by deferring validation to the
FullEnrich API: in mapIndustryToFullEnrich (and related callers) remove the
hard-fail path that pushes values into unknown for lookup misses and either (a)
include the unknown raw/trimmed value in the returned mapped set to be sent to
FullEnrich, or (b) rename/repurpose the unknown array to "deferred" and ensure
those values are forwarded to the API for authoritative validation; keep
FE_INDUSTRY_CANONICAL and FE_INDUSTRY_ALIASES only for helpful alias expansion,
not for rejecting inputs.
There was a problem hiding this comment.
Pull request overview
Updates the PeopleSearchTool to default to FullEnrich, adds stricter FullEnrich filter handling (mapping + validation + exact matching), and bumps package/template versions to 0.1.301 so new apps pull in the updated tool behavior.
Changes:
- Default
PeopleSearchTool.providertofullenrichand expand the FullEnrich filter mapping/coverage (languages, function categories, education/past roles, tenure, recent job change). - Add strict taxonomy validation +
exact_match: truefor FullEnrich string filters, plus improved email-enrichment warning surfacing. - Version bumps across packages and create-app templates to
0.1.301.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/bubble-core/src/bubbles/tool-bubble/people-search-tool.ts | FullEnrich-first behavior, strict validation/mapping, exact-match filters, enrichment warnings, doc/schema updates (with some inconsistencies noted). |
| packages/bubble-core/package.json | Bump @bubblelab/bubble-core version to 0.1.301. |
| packages/bubble-runtime/package.json | Bump runtime package version to 0.1.301. |
| packages/bubble-shared-schemas/package.json | Bump shared schemas version to 0.1.301. |
| packages/bubble-scope-manager/package.json | Bump scope manager version to 0.1.301. |
| packages/create-bubblelab-app/package.json | Bump create-app CLI version to 0.1.301. |
| packages/create-bubblelab-app/templates/basic/package.json | Bump template deps to ^0.1.301. |
| packages/create-bubblelab-app/templates/reddit-scraper/package.json | Bump template deps to ^0.1.301. |
| /** | ||
| * Translate Crustdata-flavored function categories to FullEnrich's top-level | ||
| * function enum (see FE docs: Functions & Subfunctions). Unknown inputs are | ||
| * tracked so callers can see in `warnings` which values FE ignored. FE's |
| provider: z | ||
| .enum(['crustdata', 'fullenrich']) | ||
| .default('fullenrich') | ||
| .describe( | ||
| "Search provider. Default: 'fullenrich'. Supported filter sets differ between providers — unsupported filters are silently ignored. FullEnrich does not support: locationRadius, minYearsExperience/maxYearsExperience, functionCategories, schoolName, pastJobTitle, minYearsAtCompany, recentlyChangedJobs, minConnections, excludeCompanies, excludeProfiles, languages. Crustdata supports all filters. Email enrichment (enrichEmails) only applies to Crustdata provider." | ||
| "Search provider. Default: 'fullenrich'. Leave unset unless you need a filter FullEnrich cannot honor. On the FullEnrich provider every string filter is sent with exact_match=true so results are strictly scoped (e.g. companyName='Stripe' will NOT also return 'Stripes' or ex-Stripe folks). Filters the FullEnrich provider DOES NOT SUPPORT (the tool returns a hard error if any of these are set while provider='fullenrich'): locationRadius, minYearsExperience, maxYearsExperience, minConnections, excludeCompanies, excludeProfiles, companyLinkedinUrl. Enumerated filters (seniorityLevels, functionCategories, companyIndustries) are validated against FullEnrich's taxonomy and return a hard error with the accepted values if an unknown value is passed — no silent soft-match. Everything else — including languages, schoolName, pastJobTitle, minYearsAtCompany, recentlyChangedJobs — is supported on BOTH providers." | ||
| ), |
|
|
||
| **PROVIDERS:** | ||
| - \`fullenrich\` (DEFAULT, preferred): FullEnrich /people/search. Returns LinkedIn profiles, | ||
| titles, locations, employment history, and work emails when available. |
| * FullEnrich's /people/search; falls back to Crustdata PersonDB only when the | ||
| * caller opts in (provider='crustdata') or relies on a filter FullEnrich | ||
| * doesn't support (locationRadius, functionCategories, schoolName, pastJobTitle, | ||
| * minYearsExperience/maxYearsExperience, recentlyChangedJobs, minConnections, | ||
| * excludeCompanies, excludeProfiles, languages). |
| doesn't support — the tool will silently drop unsupported filters on FullEnrich, so | ||
| check the per-filter notes below before choosing. |
| const key = raw.trim().toLowerCase(); | ||
| const hit = table[key]; | ||
| if (hit) { | ||
| for (const v of hit) mapped.add(v); | ||
| continue; | ||
| } | ||
| if (feEnum.has(raw)) { | ||
| mapped.add(raw); |
| : { mapped: [] as string[], unknown: [] as string[] }; | ||
| if (seniorityResult.unknown.length > 0) { | ||
| return this.createErrorResult( | ||
| `Unknown seniorityLevels values for FullEnrich: ${seniorityResult.unknown.join(', ')}. FullEnrich accepts: Owner, Founder, C-level, Partner, VP, Head, Director, Manager, Senior. Set provider="crustdata" for the extra Crustdata-only values (Entry Level, In Training, Experienced Manager, Entry Level Manager, Strategic).` |
| // Top-level FE functions — any value here passes through. | ||
| const feFunctions = new Set([ | ||
| 'Administrative', | ||
| 'Agriculture & Environment', | ||
| 'Construction & Trades', | ||
| 'Consulting & Advisory', | ||
| 'Customer Service', | ||
| 'Design', | ||
| 'Education', | ||
| 'Energy & Utilities', | ||
| 'Entertainment & Gaming', | ||
| 'Executive & Leadership', | ||
| 'Finance', | ||
| 'Hospitality & Tourism', | ||
| 'Human Resources', | ||
| 'Legal', | ||
| 'Marketing', | ||
| 'Media & Communications', | ||
| 'Medical & Health', | ||
| 'Non-Profit & Government', | ||
| 'Not Employed', | ||
| 'Operations', | ||
| 'Personal & Home Services', | ||
| 'Product', | ||
| 'Project & Program Management', | ||
| 'Public Safety & Security', | ||
| 'Research & Science', | ||
| 'Retail & Consumer', | ||
| 'Sales', | ||
| 'Software', | ||
| 'Traditional Engineering', | ||
| 'Transportation & Logistics', | ||
| ]); |
| * filters, collecting warnings for any filter the provider cannot honor so | ||
| * callers can see why results may look narrower than expected. | ||
| * | ||
| * When `enrichEmails=true`, runs the FullEnrich bulk-enrichment pipeline as a | ||
| * post step (same path as the Crustdata branch) because /people/search does | ||
| * not return emails by itself. |
| warnings: z | ||
| .array(z.string()) | ||
| .optional() | ||
| .describe( | ||
| 'Non-fatal signals from the tool — e.g. filters that were requested but the chosen provider does not support (and were therefore ignored). Useful for surfacing why a search returned fewer results than expected.' | ||
| ), |
Summary
Default
PeopleSearchTool.providertofullenrichand overhaul the FullEnrich path so flow-authoring agents get high-quality leads on first try. Motivated by flow 11091 (LinkedIn Lead Gen): an agent hardcodedprovider: 'crustdata'six times despite the Zod default beingfullenrich, misled by docs that opened with "Uses the Crustdata PersonDB" and didn't tag Crustdata-only filters.What's in this PR
fullenrich—longDescription+ class JSDoc rewritten to position FullEnrich as default; everyproviderfilter note is accurate against actual coverage.languages,functionCategories,schoolName,pastJobTitle,minYearsAtCompany,recentlyChangedJobsare now passed through to FullEnrich (were previously only on the Crustdata branch).CXO→C-level,Vice President→VP, etc.), job functions (Crustdata enum → FE's top-level functions + accepted subfunctions), and industries (shorthand likeSaaS→Software Development,Fintech→Financial Services). Lookup tables derived from FE's published "Accepted Filter Values" reference.exact_match: trueon every string filter — fixes the company fuzz wherecompanyName: "Stripe"would also return "Stripes" and ex-Stripe folks. Verified against FullEnrich's API docs (https://docs.fullenrich.com/api/v2/people/search/post).enrichPeopleEmailswarnings — timeout, insufficient-credits, start-failure, and thrown errors are surfaced in the tool result'swarningsarray instead of silently returning un-enriched people. Poll interval bumped 3s → 5s.longDescription— recommends an explicitjobTitleslist over relying on ML-derivedseniorityLevels+functionCategoriesalone (FE's person-level tagging is imperfect even withexact_match: true).Testing
Used
bubblelab-test-bubbleto run 4 real-world scenarios through fresh agents across 4 iterations (v1 → v4). Quality scores:All four scenarios v4 succeeded on first or second attempt without any agent hand-holding beyond the tool's
get-bubbledescription.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes