@coldiq/mcp 0.1.0
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.
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +47 -0
- package/dist/client.js.map +1 -0
- package/dist/executor.d.ts +21 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +130 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +49 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +3104 -0
- package/dist/registry.js.map +1 -0
- package/dist/tools/enrich-company.d.ts +22 -0
- package/dist/tools/enrich-company.d.ts.map +1 -0
- package/dist/tools/enrich-company.js +21 -0
- package/dist/tools/enrich-company.js.map +1 -0
- package/dist/tools/enrich-email.d.ts +24 -0
- package/dist/tools/enrich-email.d.ts.map +1 -0
- package/dist/tools/enrich-email.js +19 -0
- package/dist/tools/enrich-email.js.map +1 -0
- package/dist/tools/enrich-emails.d.ts +31 -0
- package/dist/tools/enrich-emails.d.ts.map +1 -0
- package/dist/tools/enrich-emails.js +146 -0
- package/dist/tools/enrich-emails.js.map +1 -0
- package/dist/tools/enrich-person.d.ts +26 -0
- package/dist/tools/enrich-person.d.ts.map +1 -0
- package/dist/tools/enrich-person.js +23 -0
- package/dist/tools/enrich-person.js.map +1 -0
- package/dist/tools/fetch-page-content.d.ts +22 -0
- package/dist/tools/fetch-page-content.d.ts.map +1 -0
- package/dist/tools/fetch-page-content.js +32 -0
- package/dist/tools/fetch-page-content.js.map +1 -0
- package/dist/tools/find-email.d.ts +24 -0
- package/dist/tools/find-email.d.ts.map +1 -0
- package/dist/tools/find-email.js +19 -0
- package/dist/tools/find-email.js.map +1 -0
- package/dist/tools/find-emails.d.ts +31 -0
- package/dist/tools/find-emails.d.ts.map +1 -0
- package/dist/tools/find-emails.js +146 -0
- package/dist/tools/find-emails.js.map +1 -0
- package/dist/tools/find-influencers.d.ts +29 -0
- package/dist/tools/find-influencers.d.ts.map +1 -0
- package/dist/tools/find-influencers.js +30 -0
- package/dist/tools/find-influencers.js.map +1 -0
- package/dist/tools/find-people.d.ts +26 -0
- package/dist/tools/find-people.d.ts.map +1 -0
- package/dist/tools/find-people.js +61 -0
- package/dist/tools/find-people.js.map +1 -0
- package/dist/tools/find-phone.d.ts +24 -0
- package/dist/tools/find-phone.d.ts.map +1 -0
- package/dist/tools/find-phone.js +48 -0
- package/dist/tools/find-phone.js.map +1 -0
- package/dist/tools/find-signals.d.ts +26 -0
- package/dist/tools/find-signals.d.ts.map +1 -0
- package/dist/tools/find-signals.js +82 -0
- package/dist/tools/find-signals.js.map +1 -0
- package/dist/tools/search-ads.d.ts +33 -0
- package/dist/tools/search-ads.d.ts.map +1 -0
- package/dist/tools/search-ads.js +33 -0
- package/dist/tools/search-ads.js.map +1 -0
- package/dist/tools/search-companies.d.ts +42 -0
- package/dist/tools/search-companies.d.ts.map +1 -0
- package/dist/tools/search-companies.js +37 -0
- package/dist/tools/search-companies.js.map +1 -0
- package/dist/tools/search-jobs.d.ts +51 -0
- package/dist/tools/search-jobs.d.ts.map +1 -0
- package/dist/tools/search-jobs.js +64 -0
- package/dist/tools/search-jobs.js.map +1 -0
- package/dist/tools/search-places.d.ts +47 -0
- package/dist/tools/search-places.d.ts.map +1 -0
- package/dist/tools/search-places.js +42 -0
- package/dist/tools/search-places.js.map +1 -0
- package/dist/tools/search-reddit.d.ts +27 -0
- package/dist/tools/search-reddit.d.ts.map +1 -0
- package/dist/tools/search-reddit.js +30 -0
- package/dist/tools/search-reddit.js.map +1 -0
- package/dist/tools/search-seo.d.ts +37 -0
- package/dist/tools/search-seo.d.ts.map +1 -0
- package/dist/tools/search-seo.js +49 -0
- package/dist/tools/search-seo.js.map +1 -0
- package/dist/tools/search-web.d.ts +23 -0
- package/dist/tools/search-web.d.ts.map +1 -0
- package/dist/tools/search-web.js +20 -0
- package/dist/tools/search-web.js.map +1 -0
- package/dist/tools/verify-email.d.ts +20 -0
- package/dist/tools/verify-email.d.ts.map +1 -0
- package/dist/tools/verify-email.js +15 -0
- package/dist/tools/verify-email.js.map +1 -0
- package/package.json +28 -0
- package/src/client.ts +60 -0
- package/src/executor.ts +182 -0
- package/src/index.ts +155 -0
- package/src/registry.ts +3159 -0
- package/src/tools/enrich-company.ts +25 -0
- package/src/tools/enrich-person.ts +27 -0
- package/src/tools/fetch-page-content.ts +36 -0
- package/src/tools/find-email.ts +23 -0
- package/src/tools/find-emails.ts +190 -0
- package/src/tools/find-influencers.ts +34 -0
- package/src/tools/find-people.ts +69 -0
- package/src/tools/find-phone.ts +53 -0
- package/src/tools/find-signals.ts +93 -0
- package/src/tools/search-ads.ts +44 -0
- package/src/tools/search-companies.ts +41 -0
- package/src/tools/search-jobs.ts +73 -0
- package/src/tools/search-places.ts +52 -0
- package/src/tools/search-reddit.ts +34 -0
- package/src/tools/search-seo.ts +59 -0
- package/src/tools/search-web.ts +24 -0
- package/src/tools/verify-email.ts +19 -0
- package/test-ads-live.ts +77 -0
- package/test-company-live.ts +91 -0
- package/test-email-live.ts +171 -0
- package/test-influencers-live.ts +66 -0
- package/test-jobs-live.ts +69 -0
- package/test-linkupapi-live.ts +137 -0
- package/test-phone-live.ts +41 -0
- package/test-places-live.ts +89 -0
- package/test-reddit-live.ts +66 -0
- package/test-search-live.ts +79 -0
- package/test-seo-live.ts +68 -0
- package/test-web-live.ts +67 -0
- package/tests/client.test.ts +90 -0
- package/tests/executor.test.ts +83 -0
- package/tests/gtm/01-icp-to-emails.test.ts +43 -0
- package/tests/gtm/02-icp-bulk-emails.test.ts +38 -0
- package/tests/gtm/03-icp-to-phones.test.ts +39 -0
- package/tests/gtm/04-funding-signal-outreach.test.ts +42 -0
- package/tests/gtm/05-hiring-signal-decisionmakers.test.ts +41 -0
- package/tests/gtm/06-intent-signal-outreach.test.ts +44 -0
- package/tests/gtm/07-places-to-content.test.ts +50 -0
- package/tests/gtm/08-domain-to-account.test.ts +44 -0
- package/tests/gtm/09-linkedin-to-everything.test.ts +41 -0
- package/tests/gtm/10-jobs-vs-signals-routing.test.ts +38 -0
- package/tests/gtm/11-find-vs-enrich-routing.test.ts +39 -0
- package/tests/gtm/12-bogus-domain-graceful.test.ts +42 -0
- package/tests/gtm/13-private-linkedin-graceful.test.ts +44 -0
- package/tests/gtm/14-empty-handoff.test.ts +43 -0
- package/tests/gtm/15-seo-reddit-research.test.ts +38 -0
- package/tests/gtm/README.md +59 -0
- package/tests/gtm/harness.ts +217 -0
- package/tests/gtm/tools-bridge.ts +232 -0
- package/tests/gtm-scenarios.md +32 -0
- package/tests/live/smoke-report.ts +255 -0
- package/tests/live/smoke.test.ts +134 -0
- package/tests/registry-enrich-person.test.ts +447 -0
- package/tests/registry-fetch-page-content.test.ts +90 -0
- package/tests/registry-find-people.test.ts +467 -0
- package/tests/registry-find-signals.test.ts +470 -0
- package/tests/registry-linkupapi.test.ts +331 -0
- package/tests/registry-search-companies.test.ts +188 -0
- package/tests/registry-search-jobs.test.ts +116 -0
- package/tests/registry.test.ts +2210 -0
- package/tests/tools/enrich-company.test.ts +92 -0
- package/tests/tools/enrich-email.test.ts +94 -0
- package/tests/tools/enrich-emails.test.ts +271 -0
- package/tests/tools/enrich-person.test.ts +140 -0
- package/tests/tools/fetch-page-content.test.ts +108 -0
- package/tests/tools/find-influencers.test.ts +91 -0
- package/tests/tools/find-people.test.ts +344 -0
- package/tests/tools/find-phone.test.ts +100 -0
- package/tests/tools/find-signals.test.ts +110 -0
- package/tests/tools/search-ads.test.ts +182 -0
- package/tests/tools/search-companies.test.ts +58 -0
- package/tests/tools/search-jobs.test.ts +210 -0
- package/tests/tools/search-places.test.ts +114 -0
- package/tests/tools/search-reddit.test.ts +125 -0
- package/tests/tools/search-seo.test.ts +183 -0
- package/tests/tools/search-web.test.ts +79 -0
- package/tests/tools/verify-email.test.ts +68 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { initClient, callApi } from '../src/client.js'
|
|
3
|
+
|
|
4
|
+
describe('client', () => {
|
|
5
|
+
const originalFetch = globalThis.fetch
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
initClient('http://test-api.local', 'test-key-123')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
globalThis.fetch = originalFetch
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('throws if COLDIQ_API_KEY is empty', () => {
|
|
16
|
+
expect(() => initClient('http://localhost', '')).toThrow('COLDIQ_API_KEY is required')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('prefixes paths with /v1', async () => {
|
|
20
|
+
let capturedUrl = ''
|
|
21
|
+
globalThis.fetch = vi.fn(async (url: string | URL | Request) => {
|
|
22
|
+
capturedUrl = url.toString()
|
|
23
|
+
return new Response(JSON.stringify({ ok: true }), { status: 200 })
|
|
24
|
+
}) as typeof fetch
|
|
25
|
+
|
|
26
|
+
await callApi('POST', '/companyenrich/companies/search', { pageSize: 10 })
|
|
27
|
+
|
|
28
|
+
expect(capturedUrl).toBe('http://test-api.local/v1/companyenrich/companies/search')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('sets Authorization header with Bearer token', async () => {
|
|
32
|
+
let capturedHeaders: Record<string, string> = {}
|
|
33
|
+
globalThis.fetch = vi.fn(async (_url: string | URL | Request, init?: RequestInit) => {
|
|
34
|
+
capturedHeaders = Object.fromEntries(Object.entries(init?.headers ?? {}))
|
|
35
|
+
return new Response(JSON.stringify({}), { status: 200 })
|
|
36
|
+
}) as typeof fetch
|
|
37
|
+
|
|
38
|
+
await callApi('POST', '/test', { foo: 'bar' })
|
|
39
|
+
|
|
40
|
+
expect(capturedHeaders['Authorization']).toBe('Bearer test-key-123')
|
|
41
|
+
expect(capturedHeaders['Content-Type']).toBe('application/json')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('appends query params for GET requests', async () => {
|
|
45
|
+
let capturedUrl = ''
|
|
46
|
+
globalThis.fetch = vi.fn(async (url: string | URL | Request) => {
|
|
47
|
+
capturedUrl = url.toString()
|
|
48
|
+
return new Response(JSON.stringify({}), { status: 200 })
|
|
49
|
+
}) as typeof fetch
|
|
50
|
+
|
|
51
|
+
await callApi('GET', '/companies/enrich', undefined, { domain: 'stripe.com' })
|
|
52
|
+
|
|
53
|
+
expect(capturedUrl).toContain('domain=stripe.com')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('returns ok: false on network error', async () => {
|
|
57
|
+
globalThis.fetch = vi.fn(async () => {
|
|
58
|
+
throw new Error('Network timeout')
|
|
59
|
+
}) as typeof fetch
|
|
60
|
+
|
|
61
|
+
const result = await callApi('POST', '/test', {})
|
|
62
|
+
|
|
63
|
+
expect(result.ok).toBe(false)
|
|
64
|
+
expect(result.status).toBe(0)
|
|
65
|
+
expect((result.data as Record<string, unknown>).error).toBe('Network timeout')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('returns parsed JSON on success', async () => {
|
|
69
|
+
globalThis.fetch = vi.fn(async () =>
|
|
70
|
+
new Response(JSON.stringify({ companies: [{ name: 'ColdIQ' }] }), { status: 200 })
|
|
71
|
+
) as typeof fetch
|
|
72
|
+
|
|
73
|
+
const result = await callApi('POST', '/test', {})
|
|
74
|
+
|
|
75
|
+
expect(result.ok).toBe(true)
|
|
76
|
+
expect(result.status).toBe(200)
|
|
77
|
+
expect((result.data as Record<string, unknown>).companies).toEqual([{ name: 'ColdIQ' }])
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('returns ok: false for 4xx/5xx responses', async () => {
|
|
81
|
+
globalThis.fetch = vi.fn(async () =>
|
|
82
|
+
new Response(JSON.stringify({ error: 'Not found' }), { status: 404 })
|
|
83
|
+
) as typeof fetch
|
|
84
|
+
|
|
85
|
+
const result = await callApi('GET', '/test')
|
|
86
|
+
|
|
87
|
+
expect(result.ok).toBe(false)
|
|
88
|
+
expect(result.status).toBe(404)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { initClient } from '../src/client.js'
|
|
3
|
+
import { executeWithFallback } from '../src/executor.js'
|
|
4
|
+
|
|
5
|
+
describe('executor postFilter integration', () => {
|
|
6
|
+
const originalFetch = globalThis.fetch
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
initClient('http://test-api.local', 'test-key-123')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
globalThis.fetch = originalFetch
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('invokes Apollo postFilter — strips accounts and drops wrong-country orgs → falls through to error', async () => {
|
|
17
|
+
// Apollo is at priority 7. Mock all higher-priority providers to return empty so Apollo is reached.
|
|
18
|
+
// Apollo returns orgs with wrong country + accounts key.
|
|
19
|
+
// postFilter should: strip accounts, drop German org, keep no French orgs → hasResult false → error.
|
|
20
|
+
let callCount = 0
|
|
21
|
+
globalThis.fetch = vi.fn(async () => {
|
|
22
|
+
callCount++
|
|
23
|
+
if (callCount <= 5) {
|
|
24
|
+
// companyenrich, fullenrich, pdl, blitzapi, signalbase — all return empty
|
|
25
|
+
return new Response(JSON.stringify({}), { status: 200 })
|
|
26
|
+
}
|
|
27
|
+
if (callCount === 6) {
|
|
28
|
+
// theirstack — gated (no keywords in input), but if reached, return empty
|
|
29
|
+
return new Response(JSON.stringify({}), { status: 200 })
|
|
30
|
+
}
|
|
31
|
+
// Apollo (call 7 or beyond): returns organizations with wrong country + CRM accounts
|
|
32
|
+
return new Response(
|
|
33
|
+
JSON.stringify({
|
|
34
|
+
organizations: [{ name: 'GermanCo', country: 'Germany', estimated_num_employees: 100 }],
|
|
35
|
+
accounts: [{ id: 1, name: 'CRM leak' }],
|
|
36
|
+
}),
|
|
37
|
+
{ status: 200 },
|
|
38
|
+
)
|
|
39
|
+
}) as typeof fetch
|
|
40
|
+
|
|
41
|
+
// No keywords → TheirStack skipped; no min_founded_year → Apollo eligible
|
|
42
|
+
const result = await executeWithFallback('search_companies', {
|
|
43
|
+
countries: ['FR'],
|
|
44
|
+
min_employees: 50,
|
|
45
|
+
max_employees: 200,
|
|
46
|
+
limit: 5,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Apollo's postFilter should drop GermanCo (wrong country) → hasResult false → falls through
|
|
50
|
+
expect('error' in result).toBe(true)
|
|
51
|
+
expect(callCount).toBeGreaterThan(6)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('passes raw data through unchanged when postFilter is not set (enrich_company)', async () => {
|
|
55
|
+
// enrich_company providers (companyenrich, apollo, pdl) have no postFilter.
|
|
56
|
+
// Verify a successful response is returned as-is.
|
|
57
|
+
globalThis.fetch = vi.fn(async () =>
|
|
58
|
+
new Response(JSON.stringify({ name: 'ColdIQ', domain: 'coldiq.com' }), { status: 200 })
|
|
59
|
+
) as typeof fetch
|
|
60
|
+
|
|
61
|
+
const result = await executeWithFallback('enrich_company', { domain: 'coldiq.com' })
|
|
62
|
+
|
|
63
|
+
expect('data' in result).toBe(true)
|
|
64
|
+
if ('data' in result) {
|
|
65
|
+
expect((result.data as Record<string, unknown>).domain).toBe('coldiq.com')
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('passes raw data through when postFilter is set but no strict filters requested', async () => {
|
|
70
|
+
// search_companies with no year/employee/country filter → postFilter is a no-op.
|
|
71
|
+
globalThis.fetch = vi.fn(async () =>
|
|
72
|
+
new Response(JSON.stringify({ data: [{ name: 'ColdIQ' }] }), { status: 200 })
|
|
73
|
+
) as typeof fetch
|
|
74
|
+
|
|
75
|
+
const result = await executeWithFallback('search_companies', { keywords: ['SaaS'], limit: 5 })
|
|
76
|
+
|
|
77
|
+
expect('data' in result).toBe(true)
|
|
78
|
+
if ('data' in result) {
|
|
79
|
+
const orgs = (result.data as Record<string, unknown>).data as unknown[]
|
|
80
|
+
expect(orgs.length).toBe(1)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-01: Full classical ICP → email sequence (CSV-style)
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer builds a single-row lead list from scratch.
|
|
5
|
+
* Expected flow: search_companies → find_people → find_email → verify_email
|
|
6
|
+
* Forbidden: search_web (must use structured tools, not fallback to web)
|
|
7
|
+
* Est. credits: ~4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-01: ICP → verified emails (CSV-style)', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('routes through search → people → email → verify in order', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
"I'm building a lead CSV. Do this step by step: " +
|
|
26
|
+
'1) Find 1 B2B SaaS company in France with under 200 employees. ' +
|
|
27
|
+
'2) Find the CEO at that company (limit 1). ' +
|
|
28
|
+
'3) Get their professional email address. ' +
|
|
29
|
+
'4) Verify the email is deliverable. ' +
|
|
30
|
+
'Give me the final result as: Company | CEO name | Email | Deliverable (yes/no).',
|
|
31
|
+
maxToolCalls: 10,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expectTool(result.transcript, 'search_companies')
|
|
35
|
+
expectTool(result.transcript, 'find_people')
|
|
36
|
+
expectTool(result.transcript, 'find_email')
|
|
37
|
+
expectTool(result.transcript, 'verify_email')
|
|
38
|
+
expectOrder(result.transcript, ['search_companies', 'find_people', 'find_email', 'verify_email'])
|
|
39
|
+
expectNoTool(result.transcript, 'search_web')
|
|
40
|
+
|
|
41
|
+
writeArtifact('01-icp-to-emails', result)
|
|
42
|
+
}, 8 * 60_000)
|
|
43
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-02: Multi-company bulk email lookup
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer has 3 known target companies, wants emails in one batch.
|
|
5
|
+
* Expected flow: find_people → find_emails (batch, NOT per-person find_email loop)
|
|
6
|
+
* Forbidden: find_email (singular per person), search_companies (companies already known)
|
|
7
|
+
* Est. credits: ~4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-02: multi-company bulk email lookup', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('uses find_emails (batch) instead of individual find_email calls', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
'I have 3 target companies: Stripe (stripe.com), Notion (notion.so), and Vercel (vercel.com). ' +
|
|
26
|
+
'Find 1 Head of Sales, VP Sales, or CRO at each company (limit 1 per company). ' +
|
|
27
|
+
'Then use the bulk email finder to get all their professional emails in one batch — do NOT look them up one by one. ' +
|
|
28
|
+
'Return a table: Name | Company | Email.',
|
|
29
|
+
maxToolCalls: 8,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
expectTool(result.transcript, 'find_people')
|
|
33
|
+
expectTool(result.transcript, 'find_emails')
|
|
34
|
+
expectNoTool(result.transcript, 'search_companies')
|
|
35
|
+
|
|
36
|
+
writeArtifact('02-icp-bulk-emails', result)
|
|
37
|
+
}, 8 * 60_000)
|
|
38
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-03: Person → phone number (cold call enrichment)
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer needs a phone number for a known target person.
|
|
5
|
+
* Expected flow: find_people → find_phone
|
|
6
|
+
* Forbidden: enrich_person (they don't have a LinkedIn URL, not a full enrichment), search_web
|
|
7
|
+
* Est. credits: ~3
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-03: find person then phone number', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('finds CEO at ColdIQ then retrieves their phone number', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
"I need to cold call the CEO of ColdIQ (domain: coldiq.com). " +
|
|
26
|
+
'First find the CEO there (limit 1 result), then get their phone number using their LinkedIn profile URL from the search result. ' +
|
|
27
|
+
"If no phone is found, say so — that's fine.",
|
|
28
|
+
maxToolCalls: 6,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expectTool(result.transcript, 'find_people')
|
|
32
|
+
expectTool(result.transcript, 'find_phone')
|
|
33
|
+
expectOrder(result.transcript, ['find_people', 'find_phone'])
|
|
34
|
+
expectNoTool(result.transcript, 'enrich_person')
|
|
35
|
+
expectNoTool(result.transcript, 'search_web')
|
|
36
|
+
|
|
37
|
+
writeArtifact('03-icp-to-phones', result)
|
|
38
|
+
}, 5 * 60_000)
|
|
39
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-04: Funding signal → decision-maker → email
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer targets companies that just raised money.
|
|
5
|
+
* Expected flow: find_signals (funding) → find_people → find_email
|
|
6
|
+
* Forbidden: search_companies (signals, not firmographic search)
|
|
7
|
+
* Est. credits: ~4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, expectParam, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-04: funding signal → outreach contact', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('routes through signals (funding) → people → email', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
'I target companies right after they raise money. ' +
|
|
26
|
+
'Find 1 company that recently received Series A funding in France. ' +
|
|
27
|
+
'Then find their CEO or Founder (limit 1). ' +
|
|
28
|
+
'Then get the CEO/Founder email. ' +
|
|
29
|
+
'Return: Company | CEO name | Email.',
|
|
30
|
+
maxToolCalls: 8,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expectTool(result.transcript, 'find_signals')
|
|
34
|
+
expectTool(result.transcript, 'find_people')
|
|
35
|
+
expectTool(result.transcript, 'find_email')
|
|
36
|
+
expectOrder(result.transcript, ['find_signals', 'find_people', 'find_email'])
|
|
37
|
+
expectNoTool(result.transcript, 'search_companies')
|
|
38
|
+
expectParam(result.transcript, 'find_signals', 'signal_type', (v) => String(v) === 'funding')
|
|
39
|
+
|
|
40
|
+
writeArtifact('04-funding-signal-outreach', result)
|
|
41
|
+
}, 8 * 60_000)
|
|
42
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-05: Job posting signal → decision-maker lookup
|
|
3
|
+
*
|
|
4
|
+
* Prompt: "Companies hiring SDRs in NYC" should use live job postings (search_jobs),
|
|
5
|
+
* NOT the company-level hiring signal (find_signals). This is the key boundary pair.
|
|
6
|
+
* Then find VP Sales at the hiring company.
|
|
7
|
+
* Expected flow: search_jobs → find_people
|
|
8
|
+
* Forbidden: find_signals (wrong tool for individual job postings)
|
|
9
|
+
* Est. credits: ~4
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
13
|
+
import { initClient } from '../../src/client.js'
|
|
14
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, writeArtifact } from './harness.js'
|
|
15
|
+
|
|
16
|
+
const RUN_LIVE =
|
|
17
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
18
|
+
|
|
19
|
+
describe.runIf(RUN_LIVE)('gtm-05: job postings → decision-maker', () => {
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('uses search_jobs (not find_signals) to find hiring companies, then finds VP Sales', async () => {
|
|
25
|
+
const result = await runAgent({
|
|
26
|
+
prompt:
|
|
27
|
+
'Find companies in New York that are actively posting SDR or Business Development Rep job openings right now. ' +
|
|
28
|
+
'Use the job postings tool to find actual live listings (use the minimum allowed limit). ' +
|
|
29
|
+
'Pick the first company from the results, then find the VP of Sales or Head of Sales at that company (limit 1 person). ' +
|
|
30
|
+
'Return: Company | Job title of decision-maker | LinkedIn URL.',
|
|
31
|
+
maxToolCalls: 6,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expectTool(result.transcript, 'search_jobs')
|
|
35
|
+
expectTool(result.transcript, 'find_people')
|
|
36
|
+
expectOrder(result.transcript, ['search_jobs', 'find_people'])
|
|
37
|
+
expectNoTool(result.transcript, 'find_signals')
|
|
38
|
+
|
|
39
|
+
writeArtifact('05-hiring-signal-decisionmakers', result)
|
|
40
|
+
}, 8 * 60_000)
|
|
41
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-06: Buying intent signal → outreach contact
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer validates a known target company's buying signals before outreach.
|
|
5
|
+
* find_signals(intent) returns what topics a company is actively evaluating (based on job signals).
|
|
6
|
+
* Expected flow: find_signals (intent) → find_people → find_email
|
|
7
|
+
* Forbidden: search_companies (we already know the target company)
|
|
8
|
+
* Est. credits: ~4
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
12
|
+
import { initClient } from '../../src/client.js'
|
|
13
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, expectParam, writeArtifact } from './harness.js'
|
|
14
|
+
|
|
15
|
+
const RUN_LIVE =
|
|
16
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
17
|
+
|
|
18
|
+
describe.runIf(RUN_LIVE)('gtm-06: buying intent signal → outreach', () => {
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('routes through find_signals(intent) → find_people → find_email', async () => {
|
|
24
|
+
const result = await runAgent({
|
|
25
|
+
prompt:
|
|
26
|
+
'I want to personalize my outreach to ColdIQ before contacting them. ' +
|
|
27
|
+
'First, check their buying intent signals to understand what technologies they are actively evaluating (limit 1 signal). ' +
|
|
28
|
+
'Use the intent signal tool — not a company search. ' +
|
|
29
|
+
'Then find their CEO or VP Sales (limit 1 person). ' +
|
|
30
|
+
'Then get that person\'s professional email. ' +
|
|
31
|
+
'Return: Company | Top buying intent topic | Contact name | Email.',
|
|
32
|
+
maxToolCalls: 8,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
expectTool(result.transcript, 'find_signals')
|
|
36
|
+
expectTool(result.transcript, 'find_people')
|
|
37
|
+
expectTool(result.transcript, 'find_email')
|
|
38
|
+
expectOrder(result.transcript, ['find_signals', 'find_people', 'find_email'])
|
|
39
|
+
expectNoTool(result.transcript, 'search_companies')
|
|
40
|
+
expectParam(result.transcript, 'find_signals', 'signal_type', (v) => String(v) === 'intent')
|
|
41
|
+
|
|
42
|
+
writeArtifact('06-intent-signal-outreach', result)
|
|
43
|
+
}, 8 * 60_000)
|
|
44
|
+
})
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-07: Local place search → website content extraction
|
|
3
|
+
*
|
|
4
|
+
* Prompt: Local business research — find a place, then read its website.
|
|
5
|
+
* Expected flow: search_places → fetch_page_content
|
|
6
|
+
* Forbidden: search_web (we need structured place data first, not a web search)
|
|
7
|
+
* Est. credits: ~3
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-07: place search → website content', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('finds a coffee shop near Times Square then fetches its website content', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
'I am doing local business research. ' +
|
|
26
|
+
'Find 1 coffee shop near Times Square in New York (limit 1 result). ' +
|
|
27
|
+
'If the place has a website URL in the result, fetch the content of that website. ' +
|
|
28
|
+
"If there's no website URL in the result, just report what information you found about the place. " +
|
|
29
|
+
'Summarize what the place offers.',
|
|
30
|
+
maxToolCalls: 6,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expectTool(result.transcript, 'search_places')
|
|
34
|
+
expectNoTool(result.transcript, 'search_web')
|
|
35
|
+
|
|
36
|
+
const transcript = result.transcript
|
|
37
|
+
const hasPageFetch = transcript.some((c) => c.tool === 'fetch_page_content')
|
|
38
|
+
const searchPlacesResult = transcript.find((c) => c.tool === 'search_places')
|
|
39
|
+
// If search_places returned a website URL, the agent should have fetched it
|
|
40
|
+
if (hasPageFetch) {
|
|
41
|
+
expectOrder(transcript, ['search_places', 'fetch_page_content'])
|
|
42
|
+
} else {
|
|
43
|
+
// No website URL in result — acceptable, agent should report the place data
|
|
44
|
+
const hasData = searchPlacesResult && !searchPlacesResult.isError
|
|
45
|
+
if (!hasData) throw new Error('search_places returned an error and no fetch_page_content was attempted')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
writeArtifact('07-places-to-content', result)
|
|
49
|
+
}, 8 * 60_000)
|
|
50
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-08: Domain → full account intelligence
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer targets a specific known company, wants a full picture.
|
|
5
|
+
* Expected flow: enrich_company → find_people → find_email
|
|
6
|
+
* Forbidden: search_companies (we already know the domain, firmographic search is wasteful)
|
|
7
|
+
* Est. credits: ~4
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, expectParam, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-08: domain → account + contact + email', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('enriches company then finds contact then gets email', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
"I want to target ColdIQ for outbound. Their domain is coldiq.com. " +
|
|
26
|
+
'1) Enrich the company to understand what they do, their size, and industry. ' +
|
|
27
|
+
'2) Find 1 key decision-maker there — CEO, Founder, or VP Sales. ' +
|
|
28
|
+
'3) Get their professional email. ' +
|
|
29
|
+
'Return: a brief company summary + the contact name + email.',
|
|
30
|
+
maxToolCalls: 8,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expectTool(result.transcript, 'enrich_company')
|
|
34
|
+
expectTool(result.transcript, 'find_people')
|
|
35
|
+
expectTool(result.transcript, 'find_email')
|
|
36
|
+
expectOrder(result.transcript, ['enrich_company', 'find_people', 'find_email'])
|
|
37
|
+
expectNoTool(result.transcript, 'search_companies')
|
|
38
|
+
expectParam(result.transcript, 'enrich_company', 'domain', (v) =>
|
|
39
|
+
String(v).toLowerCase().includes('coldiq'),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
writeArtifact('08-domain-to-account', result)
|
|
43
|
+
}, 8 * 60_000)
|
|
44
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-09: LinkedIn URL → person enrichment + company enrichment
|
|
3
|
+
*
|
|
4
|
+
* Prompt: GTM engineer has a LinkedIn URL, wants full profile and company context.
|
|
5
|
+
* Expected flow: enrich_person → enrich_company (using domain from person enrichment)
|
|
6
|
+
* Forbidden: find_people (we have the URL already, no need to search)
|
|
7
|
+
* Est. credits: ~3
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
11
|
+
import { initClient } from '../../src/client.js'
|
|
12
|
+
import { runAgent, expectTool, expectNoTool, expectOrder, expectParam, writeArtifact } from './harness.js'
|
|
13
|
+
|
|
14
|
+
const RUN_LIVE =
|
|
15
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
16
|
+
|
|
17
|
+
describe.runIf(RUN_LIVE)('gtm-09: LinkedIn URL → person + company enrichment', () => {
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('enriches person then enriches their company', async () => {
|
|
23
|
+
const result = await runAgent({
|
|
24
|
+
prompt:
|
|
25
|
+
'Enrich this LinkedIn profile: https://www.linkedin.com/in/michel-lieben. ' +
|
|
26
|
+
'After getting the person data, also enrich their company using the domain from the person result. ' +
|
|
27
|
+
'Give me: full name, job title, company name, company size, and company industry.',
|
|
28
|
+
maxToolCalls: 6,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expectTool(result.transcript, 'enrich_person')
|
|
32
|
+
expectTool(result.transcript, 'enrich_company')
|
|
33
|
+
expectOrder(result.transcript, ['enrich_person', 'enrich_company'])
|
|
34
|
+
expectNoTool(result.transcript, 'find_people')
|
|
35
|
+
expectParam(result.transcript, 'enrich_person', 'linkedin_url', (v) =>
|
|
36
|
+
String(v).toLowerCase().includes('michel-lieben'),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
writeArtifact('09-linkedin-to-everything', result)
|
|
40
|
+
}, 6 * 60_000)
|
|
41
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-10: Boundary pair — search_jobs vs find_signals(hiring)
|
|
3
|
+
*
|
|
4
|
+
* "Companies hiring sales reps right now" should route to search_jobs (individual live postings),
|
|
5
|
+
* NOT find_signals (company-level aggregated hiring surge signal).
|
|
6
|
+
*
|
|
7
|
+
* This is a pure routing test — we care about WHICH tool is chosen, not the result.
|
|
8
|
+
* Expected: search_jobs
|
|
9
|
+
* Forbidden: find_signals
|
|
10
|
+
* Est. credits: ~2
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
14
|
+
import { initClient } from '../../src/client.js'
|
|
15
|
+
import { runAgent, expectTool, expectNoTool, writeArtifact } from './harness.js'
|
|
16
|
+
|
|
17
|
+
const RUN_LIVE =
|
|
18
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
19
|
+
|
|
20
|
+
describe.runIf(RUN_LIVE)('gtm-10: routing — search_jobs vs find_signals(hiring)', () => {
|
|
21
|
+
beforeAll(() => {
|
|
22
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('routes to search_jobs, not find_signals, for live job posting queries', async () => {
|
|
26
|
+
const result = await runAgent({
|
|
27
|
+
prompt:
|
|
28
|
+
'What companies in New York are posting job openings for Account Executive or Sales Rep right now? ' +
|
|
29
|
+
'I want to see the actual job listings — company name, job title, and posting date. Use the minimum allowed limit.',
|
|
30
|
+
maxToolCalls: 4,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expectTool(result.transcript, 'search_jobs')
|
|
34
|
+
expectNoTool(result.transcript, 'find_signals')
|
|
35
|
+
|
|
36
|
+
writeArtifact('10-jobs-vs-signals-routing', result)
|
|
37
|
+
}, 5 * 60_000)
|
|
38
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTM-11: Boundary pair — find_email vs enrich_person
|
|
3
|
+
*
|
|
4
|
+
* "What is X's email at company Y?" should route to find_email (name+domain → email),
|
|
5
|
+
* NOT enrich_person (which enriches a full profile from a LinkedIn URL or known email).
|
|
6
|
+
*
|
|
7
|
+
* Expected: find_email
|
|
8
|
+
* Forbidden: enrich_person
|
|
9
|
+
* Est. credits: ~1
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, beforeAll } from 'vitest'
|
|
13
|
+
import { initClient } from '../../src/client.js'
|
|
14
|
+
import { runAgent, expectTool, expectNoTool, expectParam, writeArtifact } from './harness.js'
|
|
15
|
+
|
|
16
|
+
const RUN_LIVE =
|
|
17
|
+
process.env.LIVE_TESTS === '1' && !!process.env.ANTHROPIC_API_KEY && !!process.env.COLDIQ_API_KEY
|
|
18
|
+
|
|
19
|
+
describe.runIf(RUN_LIVE)('gtm-11: routing — find_email vs enrich_person', () => {
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
initClient(process.env.COLDIQ_API_URL, process.env.COLDIQ_API_KEY)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('routes to find_email (not enrich_person) for name + domain → email queries', async () => {
|
|
25
|
+
const result = await runAgent({
|
|
26
|
+
prompt:
|
|
27
|
+
"What is Michel Lieben's professional email address? He is the CEO at ColdIQ — their domain is coldiq.com.",
|
|
28
|
+
maxToolCalls: 4,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expectTool(result.transcript, 'find_email')
|
|
32
|
+
expectNoTool(result.transcript, 'enrich_person')
|
|
33
|
+
expectParam(result.transcript, 'find_email', 'domain', (v) =>
|
|
34
|
+
String(v).toLowerCase().includes('coldiq'),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
writeArtifact('11-find-vs-enrich-routing', result)
|
|
38
|
+
}, 3 * 60_000)
|
|
39
|
+
})
|