agentic-pi 0.2.2 → 0.2.4

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.
Files changed (46) hide show
  1. package/README.md +140 -29
  2. package/dist/args.d.ts +27 -0
  3. package/dist/args.js +46 -0
  4. package/dist/args.js.map +1 -1
  5. package/dist/extensions/file-search/index.d.ts +66 -0
  6. package/dist/extensions/file-search/index.js +86 -0
  7. package/dist/extensions/file-search/index.js.map +1 -0
  8. package/dist/extensions/web-search/extract.d.ts +18 -0
  9. package/dist/extensions/web-search/extract.js +110 -0
  10. package/dist/extensions/web-search/extract.js.map +1 -0
  11. package/dist/extensions/web-search/index.d.ts +43 -0
  12. package/dist/extensions/web-search/index.js +86 -0
  13. package/dist/extensions/web-search/index.js.map +1 -0
  14. package/dist/extensions/web-search/providers/brave.d.ts +21 -0
  15. package/dist/extensions/web-search/providers/brave.js +73 -0
  16. package/dist/extensions/web-search/providers/brave.js.map +1 -0
  17. package/dist/extensions/web-search/providers/exa.d.ts +16 -0
  18. package/dist/extensions/web-search/providers/exa.js +85 -0
  19. package/dist/extensions/web-search/providers/exa.js.map +1 -0
  20. package/dist/extensions/web-search/providers/tavily.d.ts +18 -0
  21. package/dist/extensions/web-search/providers/tavily.js +85 -0
  22. package/dist/extensions/web-search/providers/tavily.js.map +1 -0
  23. package/dist/extensions/web-search/rate-limit.d.ts +14 -0
  24. package/dist/extensions/web-search/rate-limit.js +24 -0
  25. package/dist/extensions/web-search/rate-limit.js.map +1 -0
  26. package/dist/extensions/web-search/safe-fetch.d.ts +54 -0
  27. package/dist/extensions/web-search/safe-fetch.js +172 -0
  28. package/dist/extensions/web-search/safe-fetch.js.map +1 -0
  29. package/dist/extensions/web-search/selection.d.ts +42 -0
  30. package/dist/extensions/web-search/selection.js +64 -0
  31. package/dist/extensions/web-search/selection.js.map +1 -0
  32. package/dist/extensions/web-search/tools.d.ts +13 -0
  33. package/dist/extensions/web-search/tools.js +136 -0
  34. package/dist/extensions/web-search/tools.js.map +1 -0
  35. package/dist/extensions/web-search/types.d.ts +65 -0
  36. package/dist/extensions/web-search/types.js +10 -0
  37. package/dist/extensions/web-search/types.js.map +1 -0
  38. package/dist/run.d.ts +45 -0
  39. package/dist/run.js +24 -0
  40. package/dist/run.js.map +1 -1
  41. package/dist/runner.js +85 -2
  42. package/dist/runner.js.map +1 -1
  43. package/dist/sandbox/gondolin.d.ts +13 -4
  44. package/dist/sandbox/gondolin.js +10 -3
  45. package/dist/sandbox/gondolin.js.map +1 -1
  46. package/package.json +2 -1
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Minimal HTML → readable text extractor. No dependencies.
3
+ *
4
+ * Approach:
5
+ * 1. Strip <script>, <style>, <noscript>, <iframe>, and HTML comments
6
+ * so the agent never sees code or hidden trackers.
7
+ * 2. Replace block-level tags with newlines so paragraphs stay separated.
8
+ * 3. Drop all other tags.
9
+ * 4. Decode the common named entities and any &#NN; / &#xHH; numeric
10
+ * escapes.
11
+ * 5. Collapse runs of whitespace; cap output at MAX_BYTES.
12
+ *
13
+ * Not a Readability-style content-only extractor — for that, use Tavily or
14
+ * Exa's native extraction, which the provider clients invoke directly.
15
+ */
16
+ export const EXTRACT_MAX_BYTES = 200 * 1024;
17
+ const NAMED_ENTITIES = {
18
+ amp: "&",
19
+ lt: "<",
20
+ gt: ">",
21
+ quot: '"',
22
+ apos: "'",
23
+ nbsp: " ",
24
+ copy: "(c)",
25
+ reg: "(R)",
26
+ trade: "(TM)",
27
+ hellip: "…",
28
+ mdash: "—",
29
+ ndash: "–",
30
+ lsquo: "‘",
31
+ rsquo: "’",
32
+ ldquo: "“",
33
+ rdquo: "”",
34
+ };
35
+ function decodeEntities(s) {
36
+ return s.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (_m, body) => {
37
+ if (body.startsWith("#x") || body.startsWith("#X")) {
38
+ const code = parseInt(body.slice(2), 16);
39
+ return Number.isFinite(code) ? safeFromCodePoint(code) : _m;
40
+ }
41
+ if (body.startsWith("#")) {
42
+ const code = parseInt(body.slice(1), 10);
43
+ return Number.isFinite(code) ? safeFromCodePoint(code) : _m;
44
+ }
45
+ const mapped = NAMED_ENTITIES[body];
46
+ return mapped ?? _m;
47
+ });
48
+ }
49
+ function safeFromCodePoint(code) {
50
+ if (code < 0 || code > 0x10ffff)
51
+ return "";
52
+ try {
53
+ return String.fromCodePoint(code);
54
+ }
55
+ catch {
56
+ return "";
57
+ }
58
+ }
59
+ const BLOCK_TAGS = new Set([
60
+ "p", "br", "div", "section", "article", "header", "footer", "main",
61
+ "nav", "aside", "ul", "ol", "li", "table", "tr", "td", "th",
62
+ "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "hr", "pre", "code",
63
+ "dl", "dt", "dd", "figure", "figcaption",
64
+ ]);
65
+ export function extractTitle(html) {
66
+ const m = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
67
+ if (!m)
68
+ return undefined;
69
+ const t = decodeEntities(m[1]).replace(/\s+/g, " ").trim();
70
+ return t.length > 0 ? t : undefined;
71
+ }
72
+ export function htmlToText(html, maxBytes = EXTRACT_MAX_BYTES) {
73
+ // 1. Strip dangerous / noise sections wholesale.
74
+ let s = html.replace(/<!--[\s\S]*?-->/g, "");
75
+ s = s.replace(/<script\b[\s\S]*?<\/script\s*>/gi, "");
76
+ s = s.replace(/<style\b[\s\S]*?<\/style\s*>/gi, "");
77
+ s = s.replace(/<noscript\b[\s\S]*?<\/noscript\s*>/gi, "");
78
+ s = s.replace(/<iframe\b[\s\S]*?<\/iframe\s*>/gi, "");
79
+ // 2. Convert block-level open/close tags to newlines so paragraphs survive.
80
+ s = s.replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>/g, (_match, tag) => {
81
+ if (BLOCK_TAGS.has(tag.toLowerCase()))
82
+ return "\n";
83
+ return "";
84
+ });
85
+ // 3. Decode entities.
86
+ s = decodeEntities(s);
87
+ // 4. Normalize whitespace: collapse runs of spaces/tabs; trim per line;
88
+ // collapse runs of blank lines.
89
+ s = s.replace(/\r\n?/g, "\n");
90
+ s = s
91
+ .split("\n")
92
+ .map((line) => line.replace(/[ \t\f\v]+/g, " ").trim())
93
+ .filter((line, i, arr) => {
94
+ // collapse 2+ blank lines down to 1
95
+ if (line !== "")
96
+ return true;
97
+ return arr[i - 1] !== "";
98
+ })
99
+ .join("\n")
100
+ .trim();
101
+ // 5. Byte cap. UTF-8 size approximation by encoding; on overflow, slice
102
+ // on code-point boundary then re-decode.
103
+ const encoder = new TextEncoder();
104
+ const bytes = encoder.encode(s);
105
+ if (bytes.byteLength <= maxBytes)
106
+ return s;
107
+ // Decode just the prefix.
108
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes.subarray(0, maxBytes));
109
+ }
110
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/extensions/web-search/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Web-search extension entry point.
3
+ *
4
+ * Mirrors `src/extensions/github/index.ts`:
5
+ * - silent skip when no provider is selected / no key present
6
+ * - loud (warning-worthy) skip when the user explicitly asked for a
7
+ * provider but didn't supply its key, or set an unknown name
8
+ * - on success, hands a typed customTools list back to the runner
9
+ *
10
+ * Selection logic lives in `selection.ts`; this module wires the chosen
11
+ * provider to its tool builder and a per-run RateLimiter.
12
+ */
13
+ import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
14
+ import type { ProviderName, WebSearchSkipReason } from "./types.js";
15
+ export declare const DEFAULT_MAX_CALLS = 30;
16
+ export interface WebSearchExtensionConfig {
17
+ /** When false, the extension is force-skipped (disabled-by-flag). Default: true. */
18
+ webSearch?: boolean;
19
+ /** Explicit provider override. */
20
+ webSearchProvider?: string;
21
+ /** Per-run call budget shared across web_search + web_fetch. Default: 30. */
22
+ webSearchMaxCalls?: number;
23
+ /** Env override (defaults to process.env). Injected by tests. */
24
+ env?: Record<string, string | undefined>;
25
+ }
26
+ export interface WebSearchExtensionResult {
27
+ /** Tools to merge into createAgentSession({ customTools }). */
28
+ customTools: ToolDefinition<any>[];
29
+ toolNames: string[];
30
+ status: "configured" | "skipped";
31
+ reason?: WebSearchSkipReason;
32
+ message?: string;
33
+ provider?: ProviderName;
34
+ /** The cap actually enforced (echoed for observability). */
35
+ maxCalls?: number;
36
+ }
37
+ export declare function loadWebSearchExtension(config?: WebSearchExtensionConfig): WebSearchExtensionResult;
38
+ /**
39
+ * True if the skip is something the user almost certainly wants surfaced
40
+ * as a warning (vs. the silent "no keys set" case).
41
+ */
42
+ export declare function isMisconfigurationSkip(result: WebSearchExtensionResult): boolean;
43
+ export type { ProviderName, WebSearchSkipReason } from "./types.js";
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Web-search extension entry point.
3
+ *
4
+ * Mirrors `src/extensions/github/index.ts`:
5
+ * - silent skip when no provider is selected / no key present
6
+ * - loud (warning-worthy) skip when the user explicitly asked for a
7
+ * provider but didn't supply its key, or set an unknown name
8
+ * - on success, hands a typed customTools list back to the runner
9
+ *
10
+ * Selection logic lives in `selection.ts`; this module wires the chosen
11
+ * provider to its tool builder and a per-run RateLimiter.
12
+ */
13
+ import { createBraveProvider } from "./providers/brave.js";
14
+ import { createExaProvider } from "./providers/exa.js";
15
+ import { createTavilyProvider } from "./providers/tavily.js";
16
+ import { RateLimiter } from "./rate-limit.js";
17
+ import { selectProvider } from "./selection.js";
18
+ import { buildWebSearchTools } from "./tools.js";
19
+ export const DEFAULT_MAX_CALLS = 30;
20
+ export function loadWebSearchExtension(config = {}) {
21
+ const env = config.env ?? process.env;
22
+ const input = {
23
+ webSearch: config.webSearch ?? true,
24
+ webSearchProvider: config.webSearchProvider,
25
+ env,
26
+ };
27
+ const selection = selectProvider(input);
28
+ if (selection.status === "skipped") {
29
+ return {
30
+ customTools: [],
31
+ toolNames: [],
32
+ status: "skipped",
33
+ reason: selection.reason,
34
+ message: selection.message,
35
+ provider: selection.provider,
36
+ };
37
+ }
38
+ const provider = instantiateProvider(selection.provider, selection.apiKey);
39
+ const maxCalls = clampMaxCalls(config.webSearchMaxCalls);
40
+ const limiter = new RateLimiter(maxCalls);
41
+ const tools = buildWebSearchTools(provider, limiter);
42
+ return {
43
+ customTools: tools,
44
+ toolNames: tools.map((t) => t.name),
45
+ status: "configured",
46
+ provider: selection.provider,
47
+ message: selection.message,
48
+ maxCalls,
49
+ };
50
+ }
51
+ /**
52
+ * True if the skip is something the user almost certainly wants surfaced
53
+ * as a warning (vs. the silent "no keys set" case).
54
+ */
55
+ export function isMisconfigurationSkip(result) {
56
+ if (result.status !== "skipped")
57
+ return false;
58
+ if (result.reason === "invalid-config")
59
+ return true;
60
+ // Explicit provider asked for, but no key — louder than the generic
61
+ // "no creds at all" skip.
62
+ if (result.reason === "no-credentials" && result.provider !== undefined)
63
+ return true;
64
+ return false;
65
+ }
66
+ function instantiateProvider(name, apiKey) {
67
+ switch (name) {
68
+ case "tavily":
69
+ return createTavilyProvider({ apiKey });
70
+ case "brave":
71
+ return createBraveProvider({ apiKey });
72
+ case "exa":
73
+ return createExaProvider({ apiKey });
74
+ }
75
+ }
76
+ function clampMaxCalls(v) {
77
+ if (v === undefined || !Number.isFinite(v))
78
+ return DEFAULT_MAX_CALLS;
79
+ const n = Math.floor(v);
80
+ if (n < 1)
81
+ return 1;
82
+ if (n > 1000)
83
+ return 1000;
84
+ return n;
85
+ }
86
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/web-search/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Brave Search provider. https://api.search.brave.com/app/documentation
3
+ *
4
+ * Endpoint used:
5
+ * GET https://api.search.brave.com/res/v1/web/search?q=…&count=…
6
+ * Header: X-Subscription-Token: <key>
7
+ *
8
+ * Brave has no content-extraction endpoint, so this provider has no
9
+ * `fetch()` method — the tool layer falls back to safeFetch + the HTML
10
+ * extractor for `web_fetch`.
11
+ *
12
+ * `include_domains` / `exclude_domains` are honored via client-side
13
+ * post-filtering so the tool's schema behaves uniformly across providers.
14
+ */
15
+ import type { FetchImpl, Provider } from "../types.js";
16
+ export interface BraveOptions {
17
+ apiKey: string;
18
+ fetchImpl?: FetchImpl;
19
+ baseUrl?: string;
20
+ }
21
+ export declare function createBraveProvider(options: BraveOptions): Provider;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Brave Search provider. https://api.search.brave.com/app/documentation
3
+ *
4
+ * Endpoint used:
5
+ * GET https://api.search.brave.com/res/v1/web/search?q=…&count=…
6
+ * Header: X-Subscription-Token: <key>
7
+ *
8
+ * Brave has no content-extraction endpoint, so this provider has no
9
+ * `fetch()` method — the tool layer falls back to safeFetch + the HTML
10
+ * extractor for `web_fetch`.
11
+ *
12
+ * `include_domains` / `exclude_domains` are honored via client-side
13
+ * post-filtering so the tool's schema behaves uniformly across providers.
14
+ */
15
+ function hostMatches(url, pattern) {
16
+ let host;
17
+ try {
18
+ host = new URL(url).hostname.toLowerCase();
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ const p = pattern.toLowerCase();
24
+ return host === p || host.endsWith(`.${p}`);
25
+ }
26
+ export function createBraveProvider(options) {
27
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
28
+ const baseUrl = options.baseUrl ?? "https://api.search.brave.com/res/v1";
29
+ return {
30
+ name: "brave",
31
+ supportsExtractedContent: false,
32
+ async search(params) {
33
+ const url = new URL(`${baseUrl}/web/search`);
34
+ url.searchParams.set("q", params.query);
35
+ // Brave's `count` caps at 20; we then post-filter and slice.
36
+ url.searchParams.set("count", String(Math.min(20, Math.max(1, params.maxResults * 2))));
37
+ const r = await fetchImpl(url.toString(), {
38
+ method: "GET",
39
+ headers: {
40
+ accept: "application/json",
41
+ "x-subscription-token": options.apiKey,
42
+ },
43
+ });
44
+ if (!r.ok) {
45
+ const text = await r.text().catch(() => "");
46
+ throw new Error(`brave search failed: http ${r.status} ${text.slice(0, 200)}`);
47
+ }
48
+ const data = (await r.json());
49
+ const items = (data.web?.results ?? []).map((it) => ({
50
+ title: it.title ?? "",
51
+ url: it.url ?? "",
52
+ snippet: it.description,
53
+ publishedDate: it.age,
54
+ }));
55
+ let filtered = items;
56
+ if (params.includeDomains?.length) {
57
+ const list = params.includeDomains;
58
+ filtered = filtered.filter((it) => it.url && list.some((d) => hostMatches(it.url, d)));
59
+ }
60
+ if (params.excludeDomains?.length) {
61
+ const list = params.excludeDomains;
62
+ filtered = filtered.filter((it) => !(it.url && list.some((d) => hostMatches(it.url, d))));
63
+ }
64
+ filtered = filtered.slice(0, params.maxResults);
65
+ return {
66
+ provider: "brave",
67
+ query: params.query,
68
+ results: filtered,
69
+ };
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=brave.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brave.js","sourceRoot":"","sources":["../../../../src/extensions/web-search/providers/brave.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Exa provider. https://docs.exa.ai/
3
+ *
4
+ * Endpoints used:
5
+ * POST https://api.exa.ai/search — neural/keyword search
6
+ * POST https://api.exa.ai/contents — fetch extracted content for one or more URLs
7
+ *
8
+ * Auth: `x-api-key: <key>`.
9
+ */
10
+ import type { FetchImpl, Provider } from "../types.js";
11
+ export interface ExaOptions {
12
+ apiKey: string;
13
+ fetchImpl?: FetchImpl;
14
+ baseUrl?: string;
15
+ }
16
+ export declare function createExaProvider(options: ExaOptions): Provider;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Exa provider. https://docs.exa.ai/
3
+ *
4
+ * Endpoints used:
5
+ * POST https://api.exa.ai/search — neural/keyword search
6
+ * POST https://api.exa.ai/contents — fetch extracted content for one or more URLs
7
+ *
8
+ * Auth: `x-api-key: <key>`.
9
+ */
10
+ export function createExaProvider(options) {
11
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
12
+ const baseUrl = options.baseUrl ?? "https://api.exa.ai";
13
+ return {
14
+ name: "exa",
15
+ supportsExtractedContent: true,
16
+ async search(params) {
17
+ const body = {
18
+ query: params.query,
19
+ numResults: params.maxResults,
20
+ type: params.searchDepth === "advanced" ? "neural" : "auto",
21
+ };
22
+ if (params.includeDomains?.length)
23
+ body.includeDomains = params.includeDomains;
24
+ if (params.excludeDomains?.length)
25
+ body.excludeDomains = params.excludeDomains;
26
+ if (params.includeContent === true) {
27
+ body.contents = { text: { maxCharacters: 4000 } };
28
+ }
29
+ const r = await fetchImpl(`${baseUrl}/search`, {
30
+ method: "POST",
31
+ headers: {
32
+ "content-type": "application/json",
33
+ "x-api-key": options.apiKey,
34
+ },
35
+ body: JSON.stringify(body),
36
+ });
37
+ if (!r.ok) {
38
+ const text = await r.text().catch(() => "");
39
+ throw new Error(`exa search failed: http ${r.status} ${text.slice(0, 200)}`);
40
+ }
41
+ const data = (await r.json());
42
+ return {
43
+ provider: "exa",
44
+ query: params.query,
45
+ results: (data.results ?? []).map((it) => ({
46
+ title: it.title ?? "",
47
+ url: it.url ?? "",
48
+ content: it.text,
49
+ score: it.score,
50
+ publishedDate: it.publishedDate,
51
+ })),
52
+ };
53
+ },
54
+ async fetch(params) {
55
+ const r = await fetchImpl(`${baseUrl}/contents`, {
56
+ method: "POST",
57
+ headers: {
58
+ "content-type": "application/json",
59
+ "x-api-key": options.apiKey,
60
+ },
61
+ body: JSON.stringify({
62
+ urls: [params.url],
63
+ text: true,
64
+ }),
65
+ });
66
+ if (!r.ok) {
67
+ const text = await r.text().catch(() => "");
68
+ throw new Error(`exa contents failed: http ${r.status} ${text.slice(0, 200)}`);
69
+ }
70
+ const data = (await r.json());
71
+ const hit = data.results?.[0];
72
+ if (!hit?.text) {
73
+ throw new Error(`exa contents returned no text for ${params.url}`);
74
+ }
75
+ return {
76
+ provider: "exa",
77
+ url: params.url,
78
+ resolvedUrl: hit.url,
79
+ text: hit.text,
80
+ title: hit.title,
81
+ };
82
+ },
83
+ };
84
+ }
85
+ //# sourceMappingURL=exa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exa.js","sourceRoot":"","sources":["../../../../src/extensions/web-search/providers/exa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tavily provider. https://docs.tavily.com/
3
+ *
4
+ * Endpoints used:
5
+ * POST https://api.tavily.com/search — ranked links + optional content + optional answer
6
+ * POST https://api.tavily.com/extract — bulk fetch + extracted text for a list of URLs
7
+ *
8
+ * API key passed as `api_key` in the JSON body (Tavily's documented
9
+ * convention; the Bearer header is rejected on /search).
10
+ */
11
+ import type { FetchImpl, Provider } from "../types.js";
12
+ export interface TavilyOptions {
13
+ apiKey: string;
14
+ fetchImpl?: FetchImpl;
15
+ /** Override for tests. */
16
+ baseUrl?: string;
17
+ }
18
+ export declare function createTavilyProvider(options: TavilyOptions): Provider;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Tavily provider. https://docs.tavily.com/
3
+ *
4
+ * Endpoints used:
5
+ * POST https://api.tavily.com/search — ranked links + optional content + optional answer
6
+ * POST https://api.tavily.com/extract — bulk fetch + extracted text for a list of URLs
7
+ *
8
+ * API key passed as `api_key` in the JSON body (Tavily's documented
9
+ * convention; the Bearer header is rejected on /search).
10
+ */
11
+ export function createTavilyProvider(options) {
12
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
13
+ const baseUrl = options.baseUrl ?? "https://api.tavily.com";
14
+ return {
15
+ name: "tavily",
16
+ supportsExtractedContent: true,
17
+ async search(params) {
18
+ const body = {
19
+ api_key: options.apiKey,
20
+ query: params.query,
21
+ max_results: params.maxResults,
22
+ search_depth: params.searchDepth === "advanced" ? "advanced" : "basic",
23
+ include_answer: true,
24
+ include_raw_content: params.includeContent === true,
25
+ };
26
+ if (params.includeDomains?.length)
27
+ body.include_domains = params.includeDomains;
28
+ if (params.excludeDomains?.length)
29
+ body.exclude_domains = params.excludeDomains;
30
+ const r = await fetchImpl(`${baseUrl}/search`, {
31
+ method: "POST",
32
+ headers: { "content-type": "application/json" },
33
+ body: JSON.stringify(body),
34
+ });
35
+ if (!r.ok) {
36
+ const text = await r.text().catch(() => "");
37
+ throw new Error(`tavily search failed: http ${r.status} ${text.slice(0, 200)}`);
38
+ }
39
+ const data = (await r.json());
40
+ return {
41
+ provider: "tavily",
42
+ query: data.query ?? params.query,
43
+ answer: data.answer,
44
+ results: (data.results ?? []).map((it) => ({
45
+ title: it.title ?? "",
46
+ url: it.url ?? "",
47
+ snippet: it.content,
48
+ content: it.raw_content ?? undefined,
49
+ score: it.score,
50
+ publishedDate: it.published_date,
51
+ })),
52
+ };
53
+ },
54
+ async fetch(params) {
55
+ const r = await fetchImpl(`${baseUrl}/extract`, {
56
+ method: "POST",
57
+ headers: { "content-type": "application/json" },
58
+ body: JSON.stringify({
59
+ api_key: options.apiKey,
60
+ urls: [params.url],
61
+ }),
62
+ });
63
+ if (!r.ok) {
64
+ const text = await r.text().catch(() => "");
65
+ throw new Error(`tavily extract failed: http ${r.status} ${text.slice(0, 200)}`);
66
+ }
67
+ const data = (await r.json());
68
+ const hit = data.results?.[0];
69
+ if (!hit?.raw_content) {
70
+ const fail = data.failed_results?.[0];
71
+ throw new Error(fail?.error
72
+ ? `tavily extract returned no content for ${params.url}: ${fail.error}`
73
+ : `tavily extract returned no content for ${params.url}`);
74
+ }
75
+ return {
76
+ provider: "tavily",
77
+ url: params.url,
78
+ resolvedUrl: hit.url,
79
+ text: hit.raw_content,
80
+ title: hit.title,
81
+ };
82
+ },
83
+ };
84
+ }
85
+ //# sourceMappingURL=tavily.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tavily.js","sourceRoot":"","sources":["../../../../src/extensions/web-search/providers/tavily.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Tiny per-run call counter. Shared between web_search and web_fetch so the
3
+ * combined call count is bounded.
4
+ *
5
+ * When `max` is exceeded `consume()` returns false; the tool layer surfaces
6
+ * this as a structured error result (never throws).
7
+ */
8
+ export declare class RateLimiter {
9
+ readonly max: number;
10
+ private used;
11
+ constructor(max: number);
12
+ consume(): boolean;
13
+ get remaining(): number;
14
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tiny per-run call counter. Shared between web_search and web_fetch so the
3
+ * combined call count is bounded.
4
+ *
5
+ * When `max` is exceeded `consume()` returns false; the tool layer surfaces
6
+ * this as a structured error result (never throws).
7
+ */
8
+ export class RateLimiter {
9
+ max;
10
+ used = 0;
11
+ constructor(max) {
12
+ this.max = max;
13
+ }
14
+ consume() {
15
+ if (this.used >= this.max)
16
+ return false;
17
+ this.used += 1;
18
+ return true;
19
+ }
20
+ get remaining() {
21
+ return Math.max(0, this.max - this.used);
22
+ }
23
+ }
24
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../src/extensions/web-search/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,OAAO,WAAW;IAED;IADb,IAAI,GAAG,CAAC,CAAC;IACjB,YAAqB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEpC,OAAO;QACL,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO,KAAK,CAAC;QACxC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;CACF"}