@dbx-tools/appkit-web-search 0.3.44 → 0.4.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.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Provider detection + web-search tool-spec mapping for the Databricks
3
+ * Model Serving native web-search tool.
4
+ *
5
+ * Databricks exposes web search as a first-party tool that runs *inside* a
6
+ * model call: the model searches the web and folds the results into its
7
+ * answer. The tool spec is provider-specific (see the Databricks docs,
8
+ * `machine-learning/model-serving/web-search`):
9
+ *
10
+ * - OpenAI GPT models, via the Responses API (`/serving-endpoints/responses`):
11
+ * `tools: [{ "type": "web_search" }]`
12
+ * - Google Gemini models, via Chat Completions
13
+ * (`/serving-endpoints/chat/completions`): `tools: [{ "google_search": {} }]`
14
+ *
15
+ * (Anthropic exposes it over MCP, which needs a different call shape; only
16
+ * GPT + Gemini are wired here, matching what the platform supports today.)
17
+ *
18
+ * The provider family is detected from the endpoint id the same way
19
+ * `@dbx-tools/shared-model`'s `classifyByFamily` keys off name substrings
20
+ * (`gpt` / `gemini` / `claude`), so a resolved endpoint like
21
+ * `databricks-gpt-5` or `databricks-gemini-3-pro` maps to its API shape.
22
+ *
23
+ * @module
24
+ */
25
+ /** A web-search-capable model provider family. */
26
+ export type WebSearchProvider = "openai" | "gemini";
27
+ /** How a provider's native web-search call is shaped. */
28
+ export interface WebSearchProviderSpec {
29
+ /**
30
+ * Which serving REST surface to call. `"responses"` posts to
31
+ * `/serving-endpoints/responses` (OpenAI Responses API); `"chat"` posts to
32
+ * `/serving-endpoints/chat/completions`.
33
+ */
34
+ api: "responses" | "chat";
35
+ /** The tool entry appended to the request's `tools` array. */
36
+ tool: Record<string, unknown>;
37
+ }
38
+ /**
39
+ * Built-in provider -> tool-spec map. Operators can override or extend this
40
+ * per provider via the plugin's `webSearchTools` config (env
41
+ * `WEB_SEARCH_TOOLS`), which is merged over these defaults.
42
+ */
43
+ export declare const WEB_SEARCH_PROVIDERS: Readonly<Record<WebSearchProvider, WebSearchProviderSpec>>;
44
+ /**
45
+ * Detect the web-search provider family for an endpoint id, or `null` when
46
+ * the model is not one of the web-search-capable families. Anthropic
47
+ * (`opus`/`sonnet`/`haiku`) is intentionally excluded - it needs an MCP call
48
+ * shape this module doesn't implement - so a Claude endpoint returns `null`
49
+ * and is treated as unsupported.
50
+ */
51
+ export declare function detectWebSearchProvider(modelId: string): WebSearchProvider | null;
52
+ /** Whether `modelId` is a web-search-capable model. */
53
+ export declare function supportsWebSearch(modelId: string): boolean;
54
+ /**
55
+ * Resolve the effective {@link WebSearchProviderSpec} for a provider: the
56
+ * built-in default, with any operator override (the `webSearchTools` map,
57
+ * keyed by provider) shallow-merged over it. An override may replace just the
58
+ * `tool` (the common case - a new tool type) or also the `api`. An override
59
+ * that is not one of those two fields is a deployment mistake that would
60
+ * otherwise be silently dropped, so it throws.
61
+ */
62
+ export declare function webSearchToolSpec(provider: WebSearchProvider, overrides?: Record<string, unknown>): WebSearchProviderSpec;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Provider detection + web-search tool-spec mapping for the Databricks
3
+ * Model Serving native web-search tool.
4
+ *
5
+ * Databricks exposes web search as a first-party tool that runs *inside* a
6
+ * model call: the model searches the web and folds the results into its
7
+ * answer. The tool spec is provider-specific (see the Databricks docs,
8
+ * `machine-learning/model-serving/web-search`):
9
+ *
10
+ * - OpenAI GPT models, via the Responses API (`/serving-endpoints/responses`):
11
+ * `tools: [{ "type": "web_search" }]`
12
+ * - Google Gemini models, via Chat Completions
13
+ * (`/serving-endpoints/chat/completions`): `tools: [{ "google_search": {} }]`
14
+ *
15
+ * (Anthropic exposes it over MCP, which needs a different call shape; only
16
+ * GPT + Gemini are wired here, matching what the platform supports today.)
17
+ *
18
+ * The provider family is detected from the endpoint id the same way
19
+ * `@dbx-tools/shared-model`'s `classifyByFamily` keys off name substrings
20
+ * (`gpt` / `gemini` / `claude`), so a resolved endpoint like
21
+ * `databricks-gpt-5` or `databricks-gemini-3-pro` maps to its API shape.
22
+ *
23
+ * @module
24
+ */
25
+ import { ValidationError } from "@databricks/appkit";
26
+ import { z } from "zod";
27
+ /**
28
+ * Built-in provider -> tool-spec map. Operators can override or extend this
29
+ * per provider via the plugin's `webSearchTools` config (env
30
+ * `WEB_SEARCH_TOOLS`), which is merged over these defaults.
31
+ */
32
+ export const WEB_SEARCH_PROVIDERS = {
33
+ openai: { api: "responses", tool: { type: "web_search" } },
34
+ gemini: { api: "chat", tool: { google_search: {} } },
35
+ };
36
+ /**
37
+ * Detect the web-search provider family for an endpoint id, or `null` when
38
+ * the model is not one of the web-search-capable families. Anthropic
39
+ * (`opus`/`sonnet`/`haiku`) is intentionally excluded - it needs an MCP call
40
+ * shape this module doesn't implement - so a Claude endpoint returns `null`
41
+ * and is treated as unsupported.
42
+ */
43
+ export function detectWebSearchProvider(modelId) {
44
+ const n = modelId.toLowerCase();
45
+ // gpt-oss open-weights don't carry the hosted web-search tool; only the
46
+ // hosted GPT family (Responses API) does. Both contain "gpt", so exclude
47
+ // the open-weights explicitly.
48
+ if (n.includes("gpt") && !n.includes("gpt-oss"))
49
+ return "openai";
50
+ if (n.includes("gemini"))
51
+ return "gemini";
52
+ return null;
53
+ }
54
+ /** Whether `modelId` is a web-search-capable model. */
55
+ export function supportsWebSearch(modelId) {
56
+ return detectWebSearchProvider(modelId) !== null;
57
+ }
58
+ /**
59
+ * Runtime shape of one entry in the operator override map. The map arrives as
60
+ * parsed JSON (config or `WEB_SEARCH_TOOLS`), so it is validated rather than
61
+ * asserted.
62
+ */
63
+ const providerOverrideSchema = z.object({
64
+ api: z.enum(["responses", "chat"]).optional(),
65
+ tool: z.record(z.string(), z.unknown()).optional(),
66
+ });
67
+ /**
68
+ * Resolve the effective {@link WebSearchProviderSpec} for a provider: the
69
+ * built-in default, with any operator override (the `webSearchTools` map,
70
+ * keyed by provider) shallow-merged over it. An override may replace just the
71
+ * `tool` (the common case - a new tool type) or also the `api`. An override
72
+ * that is not one of those two fields is a deployment mistake that would
73
+ * otherwise be silently dropped, so it throws.
74
+ */
75
+ export function webSearchToolSpec(provider, overrides) {
76
+ const base = WEB_SEARCH_PROVIDERS[provider];
77
+ const raw = overrides?.[provider];
78
+ if (raw === undefined)
79
+ return base;
80
+ const parsed = providerOverrideSchema.safeParse(raw);
81
+ if (!parsed.success) {
82
+ throw ValidationError.invalidValue(`webSearchTools.${provider}`, raw, 'an object with an optional "api" ("responses" | "chat") and an optional "tool" object');
83
+ }
84
+ return {
85
+ api: parsed.data.api ?? base.api,
86
+ tool: parsed.data.tool ?? base.tool,
87
+ };
88
+ }
89
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUJHO0FBRUgsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFpQnhCOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBK0Q7SUFDOUYsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsWUFBWSxFQUFFLEVBQUU7SUFDMUQsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFBRSxhQUFhLEVBQUUsRUFBRSxFQUFFLEVBQUU7Q0FDckQsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILE1BQU0sVUFBVSx1QkFBdUIsQ0FBQyxPQUFlO0lBQ3JELE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNoQyx3RUFBd0U7SUFDeEUseUVBQXlFO0lBQ3pFLCtCQUErQjtJQUMvQixJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBQ2pFLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7UUFBRSxPQUFPLFFBQVEsQ0FBQztJQUMxQyxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCx1REFBdUQ7QUFDdkQsTUFBTSxVQUFVLGlCQUFpQixDQUFDLE9BQWU7SUFDL0MsT0FBTyx1QkFBdUIsQ0FBQyxPQUFPLENBQUMsS0FBSyxJQUFJLENBQUM7QUFDbkQsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDdEMsR0FBRyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUU7SUFDN0MsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRTtDQUNuRCxDQUFDLENBQUM7QUFFSDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixRQUEyQixFQUMzQixTQUFtQztJQUVuQyxNQUFNLElBQUksR0FBRyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUM1QyxNQUFNLEdBQUcsR0FBRyxTQUFTLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNsQyxJQUFJLEdBQUcsS0FBSyxTQUFTO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDbkMsTUFBTSxNQUFNLEdBQUcsc0JBQXNCLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3JELElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDcEIsTUFBTSxlQUFlLENBQUMsWUFBWSxDQUNoQyxrQkFBa0IsUUFBUSxFQUFFLEVBQzVCLEdBQUcsRUFDSCx1RkFBdUYsQ0FDeEYsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPO1FBQ0wsR0FBRyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxHQUFHO1FBQ2hDLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSTtLQUNwQyxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUHJvdmlkZXIgZGV0ZWN0aW9uICsgd2ViLXNlYXJjaCB0b29sLXNwZWMgbWFwcGluZyBmb3IgdGhlIERhdGFicmlja3NcbiAqIE1vZGVsIFNlcnZpbmcgbmF0aXZlIHdlYi1zZWFyY2ggdG9vbC5cbiAqXG4gKiBEYXRhYnJpY2tzIGV4cG9zZXMgd2ViIHNlYXJjaCBhcyBhIGZpcnN0LXBhcnR5IHRvb2wgdGhhdCBydW5zICppbnNpZGUqIGFcbiAqIG1vZGVsIGNhbGw6IHRoZSBtb2RlbCBzZWFyY2hlcyB0aGUgd2ViIGFuZCBmb2xkcyB0aGUgcmVzdWx0cyBpbnRvIGl0c1xuICogYW5zd2VyLiBUaGUgdG9vbCBzcGVjIGlzIHByb3ZpZGVyLXNwZWNpZmljIChzZWUgdGhlIERhdGFicmlja3MgZG9jcyxcbiAqIGBtYWNoaW5lLWxlYXJuaW5nL21vZGVsLXNlcnZpbmcvd2ViLXNlYXJjaGApOlxuICpcbiAqIC0gT3BlbkFJIEdQVCBtb2RlbHMsIHZpYSB0aGUgUmVzcG9uc2VzIEFQSSAoYC9zZXJ2aW5nLWVuZHBvaW50cy9yZXNwb25zZXNgKTpcbiAqICAgYHRvb2xzOiBbeyBcInR5cGVcIjogXCJ3ZWJfc2VhcmNoXCIgfV1gXG4gKiAtIEdvb2dsZSBHZW1pbmkgbW9kZWxzLCB2aWEgQ2hhdCBDb21wbGV0aW9uc1xuICogICAoYC9zZXJ2aW5nLWVuZHBvaW50cy9jaGF0L2NvbXBsZXRpb25zYCk6IGB0b29sczogW3sgXCJnb29nbGVfc2VhcmNoXCI6IHt9IH1dYFxuICpcbiAqIChBbnRocm9waWMgZXhwb3NlcyBpdCBvdmVyIE1DUCwgd2hpY2ggbmVlZHMgYSBkaWZmZXJlbnQgY2FsbCBzaGFwZTsgb25seVxuICogR1BUICsgR2VtaW5pIGFyZSB3aXJlZCBoZXJlLCBtYXRjaGluZyB3aGF0IHRoZSBwbGF0Zm9ybSBzdXBwb3J0cyB0b2RheS4pXG4gKlxuICogVGhlIHByb3ZpZGVyIGZhbWlseSBpcyBkZXRlY3RlZCBmcm9tIHRoZSBlbmRwb2ludCBpZCB0aGUgc2FtZSB3YXlcbiAqIGBAZGJ4LXRvb2xzL3NoYXJlZC1tb2RlbGAncyBgY2xhc3NpZnlCeUZhbWlseWAga2V5cyBvZmYgbmFtZSBzdWJzdHJpbmdzXG4gKiAoYGdwdGAgLyBgZ2VtaW5pYCAvIGBjbGF1ZGVgKSwgc28gYSByZXNvbHZlZCBlbmRwb2ludCBsaWtlXG4gKiBgZGF0YWJyaWNrcy1ncHQtNWAgb3IgYGRhdGFicmlja3MtZ2VtaW5pLTMtcHJvYCBtYXBzIHRvIGl0cyBBUEkgc2hhcGUuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IFZhbGlkYXRpb25FcnJvciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5cbi8qKiBBIHdlYi1zZWFyY2gtY2FwYWJsZSBtb2RlbCBwcm92aWRlciBmYW1pbHkuICovXG5leHBvcnQgdHlwZSBXZWJTZWFyY2hQcm92aWRlciA9IFwib3BlbmFpXCIgfCBcImdlbWluaVwiO1xuXG4vKiogSG93IGEgcHJvdmlkZXIncyBuYXRpdmUgd2ViLXNlYXJjaCBjYWxsIGlzIHNoYXBlZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgV2ViU2VhcmNoUHJvdmlkZXJTcGVjIHtcbiAgLyoqXG4gICAqIFdoaWNoIHNlcnZpbmcgUkVTVCBzdXJmYWNlIHRvIGNhbGwuIGBcInJlc3BvbnNlc1wiYCBwb3N0cyB0b1xuICAgKiBgL3NlcnZpbmctZW5kcG9pbnRzL3Jlc3BvbnNlc2AgKE9wZW5BSSBSZXNwb25zZXMgQVBJKTsgYFwiY2hhdFwiYCBwb3N0cyB0b1xuICAgKiBgL3NlcnZpbmctZW5kcG9pbnRzL2NoYXQvY29tcGxldGlvbnNgLlxuICAgKi9cbiAgYXBpOiBcInJlc3BvbnNlc1wiIHwgXCJjaGF0XCI7XG4gIC8qKiBUaGUgdG9vbCBlbnRyeSBhcHBlbmRlZCB0byB0aGUgcmVxdWVzdCdzIGB0b29sc2AgYXJyYXkuICovXG4gIHRvb2w6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xufVxuXG4vKipcbiAqIEJ1aWx0LWluIHByb3ZpZGVyIC0+IHRvb2wtc3BlYyBtYXAuIE9wZXJhdG9ycyBjYW4gb3ZlcnJpZGUgb3IgZXh0ZW5kIHRoaXNcbiAqIHBlciBwcm92aWRlciB2aWEgdGhlIHBsdWdpbidzIGB3ZWJTZWFyY2hUb29sc2AgY29uZmlnIChlbnZcbiAqIGBXRUJfU0VBUkNIX1RPT0xTYCksIHdoaWNoIGlzIG1lcmdlZCBvdmVyIHRoZXNlIGRlZmF1bHRzLlxuICovXG5leHBvcnQgY29uc3QgV0VCX1NFQVJDSF9QUk9WSURFUlM6IFJlYWRvbmx5PFJlY29yZDxXZWJTZWFyY2hQcm92aWRlciwgV2ViU2VhcmNoUHJvdmlkZXJTcGVjPj4gPSB7XG4gIG9wZW5haTogeyBhcGk6IFwicmVzcG9uc2VzXCIsIHRvb2w6IHsgdHlwZTogXCJ3ZWJfc2VhcmNoXCIgfSB9LFxuICBnZW1pbmk6IHsgYXBpOiBcImNoYXRcIiwgdG9vbDogeyBnb29nbGVfc2VhcmNoOiB7fSB9IH0sXG59O1xuXG4vKipcbiAqIERldGVjdCB0aGUgd2ViLXNlYXJjaCBwcm92aWRlciBmYW1pbHkgZm9yIGFuIGVuZHBvaW50IGlkLCBvciBgbnVsbGAgd2hlblxuICogdGhlIG1vZGVsIGlzIG5vdCBvbmUgb2YgdGhlIHdlYi1zZWFyY2gtY2FwYWJsZSBmYW1pbGllcy4gQW50aHJvcGljXG4gKiAoYG9wdXNgL2Bzb25uZXRgL2BoYWlrdWApIGlzIGludGVudGlvbmFsbHkgZXhjbHVkZWQgLSBpdCBuZWVkcyBhbiBNQ1AgY2FsbFxuICogc2hhcGUgdGhpcyBtb2R1bGUgZG9lc24ndCBpbXBsZW1lbnQgLSBzbyBhIENsYXVkZSBlbmRwb2ludCByZXR1cm5zIGBudWxsYFxuICogYW5kIGlzIHRyZWF0ZWQgYXMgdW5zdXBwb3J0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZXRlY3RXZWJTZWFyY2hQcm92aWRlcihtb2RlbElkOiBzdHJpbmcpOiBXZWJTZWFyY2hQcm92aWRlciB8IG51bGwge1xuICBjb25zdCBuID0gbW9kZWxJZC50b0xvd2VyQ2FzZSgpO1xuICAvLyBncHQtb3NzIG9wZW4td2VpZ2h0cyBkb24ndCBjYXJyeSB0aGUgaG9zdGVkIHdlYi1zZWFyY2ggdG9vbDsgb25seSB0aGVcbiAgLy8gaG9zdGVkIEdQVCBmYW1pbHkgKFJlc3BvbnNlcyBBUEkpIGRvZXMuIEJvdGggY29udGFpbiBcImdwdFwiLCBzbyBleGNsdWRlXG4gIC8vIHRoZSBvcGVuLXdlaWdodHMgZXhwbGljaXRseS5cbiAgaWYgKG4uaW5jbHVkZXMoXCJncHRcIikgJiYgIW4uaW5jbHVkZXMoXCJncHQtb3NzXCIpKSByZXR1cm4gXCJvcGVuYWlcIjtcbiAgaWYgKG4uaW5jbHVkZXMoXCJnZW1pbmlcIikpIHJldHVybiBcImdlbWluaVwiO1xuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqIFdoZXRoZXIgYG1vZGVsSWRgIGlzIGEgd2ViLXNlYXJjaC1jYXBhYmxlIG1vZGVsLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN1cHBvcnRzV2ViU2VhcmNoKG1vZGVsSWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gZGV0ZWN0V2ViU2VhcmNoUHJvdmlkZXIobW9kZWxJZCkgIT09IG51bGw7XG59XG5cbi8qKlxuICogUnVudGltZSBzaGFwZSBvZiBvbmUgZW50cnkgaW4gdGhlIG9wZXJhdG9yIG92ZXJyaWRlIG1hcC4gVGhlIG1hcCBhcnJpdmVzIGFzXG4gKiBwYXJzZWQgSlNPTiAoY29uZmlnIG9yIGBXRUJfU0VBUkNIX1RPT0xTYCksIHNvIGl0IGlzIHZhbGlkYXRlZCByYXRoZXIgdGhhblxuICogYXNzZXJ0ZWQuXG4gKi9cbmNvbnN0IHByb3ZpZGVyT3ZlcnJpZGVTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGFwaTogei5lbnVtKFtcInJlc3BvbnNlc1wiLCBcImNoYXRcIl0pLm9wdGlvbmFsKCksXG4gIHRvb2w6IHoucmVjb3JkKHouc3RyaW5nKCksIHoudW5rbm93bigpKS5vcHRpb25hbCgpLFxufSk7XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgZWZmZWN0aXZlIHtAbGluayBXZWJTZWFyY2hQcm92aWRlclNwZWN9IGZvciBhIHByb3ZpZGVyOiB0aGVcbiAqIGJ1aWx0LWluIGRlZmF1bHQsIHdpdGggYW55IG9wZXJhdG9yIG92ZXJyaWRlICh0aGUgYHdlYlNlYXJjaFRvb2xzYCBtYXAsXG4gKiBrZXllZCBieSBwcm92aWRlcikgc2hhbGxvdy1tZXJnZWQgb3ZlciBpdC4gQW4gb3ZlcnJpZGUgbWF5IHJlcGxhY2UganVzdCB0aGVcbiAqIGB0b29sYCAodGhlIGNvbW1vbiBjYXNlIC0gYSBuZXcgdG9vbCB0eXBlKSBvciBhbHNvIHRoZSBgYXBpYC4gQW4gb3ZlcnJpZGVcbiAqIHRoYXQgaXMgbm90IG9uZSBvZiB0aG9zZSB0d28gZmllbGRzIGlzIGEgZGVwbG95bWVudCBtaXN0YWtlIHRoYXQgd291bGRcbiAqIG90aGVyd2lzZSBiZSBzaWxlbnRseSBkcm9wcGVkLCBzbyBpdCB0aHJvd3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3ZWJTZWFyY2hUb29sU3BlYyhcbiAgcHJvdmlkZXI6IFdlYlNlYXJjaFByb3ZpZGVyLFxuICBvdmVycmlkZXM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbik6IFdlYlNlYXJjaFByb3ZpZGVyU3BlYyB7XG4gIGNvbnN0IGJhc2UgPSBXRUJfU0VBUkNIX1BST1ZJREVSU1twcm92aWRlcl07XG4gIGNvbnN0IHJhdyA9IG92ZXJyaWRlcz8uW3Byb3ZpZGVyXTtcbiAgaWYgKHJhdyA9PT0gdW5kZWZpbmVkKSByZXR1cm4gYmFzZTtcbiAgY29uc3QgcGFyc2VkID0gcHJvdmlkZXJPdmVycmlkZVNjaGVtYS5zYWZlUGFyc2UocmF3KTtcbiAgaWYgKCFwYXJzZWQuc3VjY2Vzcykge1xuICAgIHRocm93IFZhbGlkYXRpb25FcnJvci5pbnZhbGlkVmFsdWUoXG4gICAgICBgd2ViU2VhcmNoVG9vbHMuJHtwcm92aWRlcn1gLFxuICAgICAgcmF3LFxuICAgICAgJ2FuIG9iamVjdCB3aXRoIGFuIG9wdGlvbmFsIFwiYXBpXCIgKFwicmVzcG9uc2VzXCIgfCBcImNoYXRcIikgYW5kIGFuIG9wdGlvbmFsIFwidG9vbFwiIG9iamVjdCcsXG4gICAgKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIGFwaTogcGFyc2VkLmRhdGEuYXBpID8/IGJhc2UuYXBpLFxuICAgIHRvb2w6IHBhcnNlZC5kYXRhLnRvb2wgPz8gYmFzZS50b29sLFxuICB9O1xufVxuIl19
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The web-search runtime: a lazily-resolved, process-wide config shared by
3
+ * the plugin and the `web_search` / `web_fetch` tools, so both read one
4
+ * resolved allow-list / cap / timeout set. The first caller (normally the
5
+ * plugin at setup) primes it from the plugin's config; later callers (the
6
+ * tools' `execute`) reuse it.
7
+ *
8
+ * The runtime also carries the {@link WebSearchExecutor} every outbound call
9
+ * runs through. The plugin installs its own `execute()` there at setup, which
10
+ * is how the tools - plain functions with no plugin instance in scope - still
11
+ * get AppKit's cache / retry / timeout / telemetry chain. Without a
12
+ * registered plugin (a direct call from a script or a test) the calls still
13
+ * run, just without interceptors.
14
+ *
15
+ * Unlike the email runtime there is no connection to pool - the backend is
16
+ * stateless HTTP per call - so the runtime holds only the resolved config and
17
+ * that executor.
18
+ *
19
+ * @module
20
+ */
21
+ import { type ExecutionResult } from "@databricks/appkit";
22
+ import { type ResolvedWebSearchConfig, type WebSearchPluginConfig } from "./config.js";
23
+ import type { WebSearchExecutionSettings } from "./defaults.js";
24
+ /**
25
+ * Runs one outbound call through AppKit's interceptor chain. Matches
26
+ * `Plugin.execute()`, which never throws: a failure comes back as
27
+ * `{ ok: false }`.
28
+ */
29
+ export type WebSearchExecutor = <T>(fn: (signal?: AbortSignal) => Promise<T>, settings: WebSearchExecutionSettings) => Promise<ExecutionResult<T>>;
30
+ /** The shared resolved config plus the executor outbound calls run through. */
31
+ export interface WebSearchRuntime {
32
+ config: ResolvedWebSearchConfig;
33
+ execute: WebSearchExecutor;
34
+ }
35
+ /**
36
+ * Return the shared runtime, building it on first use from the supplied
37
+ * config layered over environment defaults. Overrides are only read when the
38
+ * runtime is first created, so prime it from the plugin's config at setup;
39
+ * subsequent calls (the tools' `execute`) pass nothing and get the same
40
+ * instance.
41
+ */
42
+ export declare function getWebSearchRuntime(overrides?: WebSearchPluginConfig): WebSearchRuntime;
43
+ /**
44
+ * Install the executor outbound calls run through. The plugin calls this at
45
+ * setup with its own `execute()`; a second call replaces the previous one, so
46
+ * a re-registered plugin does not leave the tools bound to a dead instance.
47
+ */
48
+ export declare function setWebSearchExecutor(execute: WebSearchExecutor): void;
49
+ /** Drop the memoized runtime so the next {@link getWebSearchRuntime} rebuilds it. */
50
+ export declare function resetWebSearchRuntime(): void;
51
+ /**
52
+ * Run one idempotent read through the shared executor and unwrap it.
53
+ *
54
+ * `execute()` never throws, so a failed call arrives as `{ ok: false }` with
55
+ * a status the interceptors already sanitized; it is logged here and re-raised
56
+ * as a stable {@link ExecutionError} so an upstream message never becomes the
57
+ * caller's error text. `signal` is the caller's own cancellation (an agent
58
+ * run, a request teardown); it is merged with the signal the timeout
59
+ * interceptor supplies so either one unwinds the I/O.
60
+ */
61
+ export declare function executeRead<T>(operation: string, settings: WebSearchExecutionSettings, fn: (signal?: AbortSignal) => Promise<T>, signal?: AbortSignal): Promise<T>;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The web-search runtime: a lazily-resolved, process-wide config shared by
3
+ * the plugin and the `web_search` / `web_fetch` tools, so both read one
4
+ * resolved allow-list / cap / timeout set. The first caller (normally the
5
+ * plugin at setup) primes it from the plugin's config; later callers (the
6
+ * tools' `execute`) reuse it.
7
+ *
8
+ * The runtime also carries the {@link WebSearchExecutor} every outbound call
9
+ * runs through. The plugin installs its own `execute()` there at setup, which
10
+ * is how the tools - plain functions with no plugin instance in scope - still
11
+ * get AppKit's cache / retry / timeout / telemetry chain. Without a
12
+ * registered plugin (a direct call from a script or a test) the calls still
13
+ * run, just without interceptors.
14
+ *
15
+ * Unlike the email runtime there is no connection to pool - the backend is
16
+ * stateless HTTP per call - so the runtime holds only the resolved config and
17
+ * that executor.
18
+ *
19
+ * @module
20
+ */
21
+ import { AppKitError, ExecutionError } from "@databricks/appkit";
22
+ import { async, error, log } from "@dbx-tools/shared-core";
23
+ import { resolveWebSearchConfig, } from "./config.js";
24
+ const logger = log.logger("web-search/runtime");
25
+ /**
26
+ * Executor used until (or unless) the plugin installs its own: run the call
27
+ * directly, mapping a throw onto the same {@link ExecutionResult} shape so
28
+ * call sites branch on `ok` either way.
29
+ */
30
+ const directExecute = async (fn) => {
31
+ try {
32
+ return { ok: true, data: await fn() };
33
+ }
34
+ catch (err) {
35
+ return {
36
+ ok: false,
37
+ status: err instanceof AppKitError ? err.statusCode : 500,
38
+ message: error.errorMessage(err),
39
+ };
40
+ }
41
+ };
42
+ let runtime;
43
+ /**
44
+ * Return the shared runtime, building it on first use from the supplied
45
+ * config layered over environment defaults. Overrides are only read when the
46
+ * runtime is first created, so prime it from the plugin's config at setup;
47
+ * subsequent calls (the tools' `execute`) pass nothing and get the same
48
+ * instance.
49
+ */
50
+ export function getWebSearchRuntime(overrides) {
51
+ if (!runtime) {
52
+ runtime = { config: resolveWebSearchConfig(overrides), execute: directExecute };
53
+ }
54
+ return runtime;
55
+ }
56
+ /**
57
+ * Install the executor outbound calls run through. The plugin calls this at
58
+ * setup with its own `execute()`; a second call replaces the previous one, so
59
+ * a re-registered plugin does not leave the tools bound to a dead instance.
60
+ */
61
+ export function setWebSearchExecutor(execute) {
62
+ getWebSearchRuntime().execute = execute;
63
+ }
64
+ /** Drop the memoized runtime so the next {@link getWebSearchRuntime} rebuilds it. */
65
+ export function resetWebSearchRuntime() {
66
+ runtime = undefined;
67
+ }
68
+ /**
69
+ * Run one idempotent read through the shared executor and unwrap it.
70
+ *
71
+ * `execute()` never throws, so a failed call arrives as `{ ok: false }` with
72
+ * a status the interceptors already sanitized; it is logged here and re-raised
73
+ * as a stable {@link ExecutionError} so an upstream message never becomes the
74
+ * caller's error text. `signal` is the caller's own cancellation (an agent
75
+ * run, a request teardown); it is merged with the signal the timeout
76
+ * interceptor supplies so either one unwinds the I/O.
77
+ */
78
+ export async function executeRead(operation, settings, fn, signal) {
79
+ const { execute } = getWebSearchRuntime();
80
+ const result = await execute((executeSignal) => fn(async.combineAbortSignals(executeSignal, signal)), settings);
81
+ if (result.ok)
82
+ return result.data;
83
+ // A caller that cancelled is not a failure worth reporting as one.
84
+ if (signal?.aborted)
85
+ throw ExecutionError.canceled();
86
+ logger.warn("execution-failed", {
87
+ operation,
88
+ status: result.status,
89
+ error: result.message,
90
+ });
91
+ throw new ExecutionError(`web-search: ${operation} failed`, {
92
+ context: { operation, status: result.status },
93
+ });
94
+ }
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVudGltZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsT0FBTyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQXdCLE1BQU0sb0JBQW9CLENBQUM7QUFDdkYsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDM0QsT0FBTyxFQUNMLHNCQUFzQixHQUd2QixNQUFNLFVBQVUsQ0FBQztBQUdsQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFrQmhEOzs7O0dBSUc7QUFDSCxNQUFNLGFBQWEsR0FBc0IsS0FBSyxFQUFFLEVBQUUsRUFBRSxFQUFFO0lBQ3BELElBQUksQ0FBQztRQUNILE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDeEMsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixPQUFPO1lBQ0wsRUFBRSxFQUFFLEtBQUs7WUFDVCxNQUFNLEVBQUUsR0FBRyxZQUFZLFdBQVcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsR0FBRztZQUN6RCxPQUFPLEVBQUUsS0FBSyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUM7U0FDakMsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixJQUFJLE9BQXFDLENBQUM7QUFFMUM7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUFDLFNBQWlDO0lBQ25FLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLE9BQU8sR0FBRyxFQUFFLE1BQU0sRUFBRSxzQkFBc0IsQ0FBQyxTQUFTLENBQUMsRUFBRSxPQUFPLEVBQUUsYUFBYSxFQUFFLENBQUM7SUFDbEYsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLE9BQTBCO0lBQzdELG1CQUFtQixFQUFFLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztBQUMxQyxDQUFDO0FBRUQscUZBQXFGO0FBQ3JGLE1BQU0sVUFBVSxxQkFBcUI7SUFDbkMsT0FBTyxHQUFHLFNBQVMsQ0FBQztBQUN0QixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQy9CLFNBQWlCLEVBQ2pCLFFBQW9DLEVBQ3BDLEVBQXdDLEVBQ3hDLE1BQW9CO0lBRXBCLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxtQkFBbUIsRUFBRSxDQUFDO0lBQzFDLE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUMxQixDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxhQUFhLEVBQUUsTUFBTSxDQUFDLENBQUMsRUFDdkUsUUFBUSxDQUNULENBQUM7SUFDRixJQUFJLE1BQU0sQ0FBQyxFQUFFO1FBQUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ2xDLG1FQUFtRTtJQUNuRSxJQUFJLE1BQU0sRUFBRSxPQUFPO1FBQUUsTUFBTSxjQUFjLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDckQsTUFBTSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRTtRQUM5QixTQUFTO1FBQ1QsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1FBQ3JCLEtBQUssRUFBRSxNQUFNLENBQUMsT0FBTztLQUN0QixDQUFDLENBQUM7SUFDSCxNQUFNLElBQUksY0FBYyxDQUFDLGVBQWUsU0FBUyxTQUFTLEVBQUU7UUFDMUQsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFO0tBQzlDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRoZSB3ZWItc2VhcmNoIHJ1bnRpbWU6IGEgbGF6aWx5LXJlc29sdmVkLCBwcm9jZXNzLXdpZGUgY29uZmlnIHNoYXJlZCBieVxuICogdGhlIHBsdWdpbiBhbmQgdGhlIGB3ZWJfc2VhcmNoYCAvIGB3ZWJfZmV0Y2hgIHRvb2xzLCBzbyBib3RoIHJlYWQgb25lXG4gKiByZXNvbHZlZCBhbGxvdy1saXN0IC8gY2FwIC8gdGltZW91dCBzZXQuIFRoZSBmaXJzdCBjYWxsZXIgKG5vcm1hbGx5IHRoZVxuICogcGx1Z2luIGF0IHNldHVwKSBwcmltZXMgaXQgZnJvbSB0aGUgcGx1Z2luJ3MgY29uZmlnOyBsYXRlciBjYWxsZXJzICh0aGVcbiAqIHRvb2xzJyBgZXhlY3V0ZWApIHJldXNlIGl0LlxuICpcbiAqIFRoZSBydW50aW1lIGFsc28gY2FycmllcyB0aGUge0BsaW5rIFdlYlNlYXJjaEV4ZWN1dG9yfSBldmVyeSBvdXRib3VuZCBjYWxsXG4gKiBydW5zIHRocm91Z2guIFRoZSBwbHVnaW4gaW5zdGFsbHMgaXRzIG93biBgZXhlY3V0ZSgpYCB0aGVyZSBhdCBzZXR1cCwgd2hpY2hcbiAqIGlzIGhvdyB0aGUgdG9vbHMgLSBwbGFpbiBmdW5jdGlvbnMgd2l0aCBubyBwbHVnaW4gaW5zdGFuY2UgaW4gc2NvcGUgLSBzdGlsbFxuICogZ2V0IEFwcEtpdCdzIGNhY2hlIC8gcmV0cnkgLyB0aW1lb3V0IC8gdGVsZW1ldHJ5IGNoYWluLiBXaXRob3V0IGFcbiAqIHJlZ2lzdGVyZWQgcGx1Z2luIChhIGRpcmVjdCBjYWxsIGZyb20gYSBzY3JpcHQgb3IgYSB0ZXN0KSB0aGUgY2FsbHMgc3RpbGxcbiAqIHJ1biwganVzdCB3aXRob3V0IGludGVyY2VwdG9ycy5cbiAqXG4gKiBVbmxpa2UgdGhlIGVtYWlsIHJ1bnRpbWUgdGhlcmUgaXMgbm8gY29ubmVjdGlvbiB0byBwb29sIC0gdGhlIGJhY2tlbmQgaXNcbiAqIHN0YXRlbGVzcyBIVFRQIHBlciBjYWxsIC0gc28gdGhlIHJ1bnRpbWUgaG9sZHMgb25seSB0aGUgcmVzb2x2ZWQgY29uZmlnIGFuZFxuICogdGhhdCBleGVjdXRvci5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgQXBwS2l0RXJyb3IsIEV4ZWN1dGlvbkVycm9yLCB0eXBlIEV4ZWN1dGlvblJlc3VsdCB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IGFzeW5jLCBlcnJvciwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7XG4gIHJlc29sdmVXZWJTZWFyY2hDb25maWcsXG4gIHR5cGUgUmVzb2x2ZWRXZWJTZWFyY2hDb25maWcsXG4gIHR5cGUgV2ViU2VhcmNoUGx1Z2luQ29uZmlnLFxufSBmcm9tIFwiLi9jb25maWdcIjtcbmltcG9ydCB0eXBlIHsgV2ViU2VhcmNoRXhlY3V0aW9uU2V0dGluZ3MgfSBmcm9tIFwiLi9kZWZhdWx0c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwid2ViLXNlYXJjaC9ydW50aW1lXCIpO1xuXG4vKipcbiAqIFJ1bnMgb25lIG91dGJvdW5kIGNhbGwgdGhyb3VnaCBBcHBLaXQncyBpbnRlcmNlcHRvciBjaGFpbi4gTWF0Y2hlc1xuICogYFBsdWdpbi5leGVjdXRlKClgLCB3aGljaCBuZXZlciB0aHJvd3M6IGEgZmFpbHVyZSBjb21lcyBiYWNrIGFzXG4gKiBgeyBvazogZmFsc2UgfWAuXG4gKi9cbmV4cG9ydCB0eXBlIFdlYlNlYXJjaEV4ZWN1dG9yID0gPFQ+KFxuICBmbjogKHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PiBQcm9taXNlPFQ+LFxuICBzZXR0aW5nczogV2ViU2VhcmNoRXhlY3V0aW9uU2V0dGluZ3MsXG4pID0+IFByb21pc2U8RXhlY3V0aW9uUmVzdWx0PFQ+PjtcblxuLyoqIFRoZSBzaGFyZWQgcmVzb2x2ZWQgY29uZmlnIHBsdXMgdGhlIGV4ZWN1dG9yIG91dGJvdW5kIGNhbGxzIHJ1biB0aHJvdWdoLiAqL1xuZXhwb3J0IGludGVyZmFjZSBXZWJTZWFyY2hSdW50aW1lIHtcbiAgY29uZmlnOiBSZXNvbHZlZFdlYlNlYXJjaENvbmZpZztcbiAgZXhlY3V0ZTogV2ViU2VhcmNoRXhlY3V0b3I7XG59XG5cbi8qKlxuICogRXhlY3V0b3IgdXNlZCB1bnRpbCAob3IgdW5sZXNzKSB0aGUgcGx1Z2luIGluc3RhbGxzIGl0cyBvd246IHJ1biB0aGUgY2FsbFxuICogZGlyZWN0bHksIG1hcHBpbmcgYSB0aHJvdyBvbnRvIHRoZSBzYW1lIHtAbGluayBFeGVjdXRpb25SZXN1bHR9IHNoYXBlIHNvXG4gKiBjYWxsIHNpdGVzIGJyYW5jaCBvbiBgb2tgIGVpdGhlciB3YXkuXG4gKi9cbmNvbnN0IGRpcmVjdEV4ZWN1dGU6IFdlYlNlYXJjaEV4ZWN1dG9yID0gYXN5bmMgKGZuKSA9PiB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIHsgb2s6IHRydWUsIGRhdGE6IGF3YWl0IGZuKCkgfTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG9rOiBmYWxzZSxcbiAgICAgIHN0YXR1czogZXJyIGluc3RhbmNlb2YgQXBwS2l0RXJyb3IgPyBlcnIuc3RhdHVzQ29kZSA6IDUwMCxcbiAgICAgIG1lc3NhZ2U6IGVycm9yLmVycm9yTWVzc2FnZShlcnIpLFxuICAgIH07XG4gIH1cbn07XG5cbmxldCBydW50aW1lOiBXZWJTZWFyY2hSdW50aW1lIHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIFJldHVybiB0aGUgc2hhcmVkIHJ1bnRpbWUsIGJ1aWxkaW5nIGl0IG9uIGZpcnN0IHVzZSBmcm9tIHRoZSBzdXBwbGllZFxuICogY29uZmlnIGxheWVyZWQgb3ZlciBlbnZpcm9ubWVudCBkZWZhdWx0cy4gT3ZlcnJpZGVzIGFyZSBvbmx5IHJlYWQgd2hlbiB0aGVcbiAqIHJ1bnRpbWUgaXMgZmlyc3QgY3JlYXRlZCwgc28gcHJpbWUgaXQgZnJvbSB0aGUgcGx1Z2luJ3MgY29uZmlnIGF0IHNldHVwO1xuICogc3Vic2VxdWVudCBjYWxscyAodGhlIHRvb2xzJyBgZXhlY3V0ZWApIHBhc3Mgbm90aGluZyBhbmQgZ2V0IHRoZSBzYW1lXG4gKiBpbnN0YW5jZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFdlYlNlYXJjaFJ1bnRpbWUob3ZlcnJpZGVzPzogV2ViU2VhcmNoUGx1Z2luQ29uZmlnKTogV2ViU2VhcmNoUnVudGltZSB7XG4gIGlmICghcnVudGltZSkge1xuICAgIHJ1bnRpbWUgPSB7IGNvbmZpZzogcmVzb2x2ZVdlYlNlYXJjaENvbmZpZyhvdmVycmlkZXMpLCBleGVjdXRlOiBkaXJlY3RFeGVjdXRlIH07XG4gIH1cbiAgcmV0dXJuIHJ1bnRpbWU7XG59XG5cbi8qKlxuICogSW5zdGFsbCB0aGUgZXhlY3V0b3Igb3V0Ym91bmQgY2FsbHMgcnVuIHRocm91Z2guIFRoZSBwbHVnaW4gY2FsbHMgdGhpcyBhdFxuICogc2V0dXAgd2l0aCBpdHMgb3duIGBleGVjdXRlKClgOyBhIHNlY29uZCBjYWxsIHJlcGxhY2VzIHRoZSBwcmV2aW91cyBvbmUsIHNvXG4gKiBhIHJlLXJlZ2lzdGVyZWQgcGx1Z2luIGRvZXMgbm90IGxlYXZlIHRoZSB0b29scyBib3VuZCB0byBhIGRlYWQgaW5zdGFuY2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZXRXZWJTZWFyY2hFeGVjdXRvcihleGVjdXRlOiBXZWJTZWFyY2hFeGVjdXRvcik6IHZvaWQge1xuICBnZXRXZWJTZWFyY2hSdW50aW1lKCkuZXhlY3V0ZSA9IGV4ZWN1dGU7XG59XG5cbi8qKiBEcm9wIHRoZSBtZW1vaXplZCBydW50aW1lIHNvIHRoZSBuZXh0IHtAbGluayBnZXRXZWJTZWFyY2hSdW50aW1lfSByZWJ1aWxkcyBpdC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNldFdlYlNlYXJjaFJ1bnRpbWUoKTogdm9pZCB7XG4gIHJ1bnRpbWUgPSB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUnVuIG9uZSBpZGVtcG90ZW50IHJlYWQgdGhyb3VnaCB0aGUgc2hhcmVkIGV4ZWN1dG9yIGFuZCB1bndyYXAgaXQuXG4gKlxuICogYGV4ZWN1dGUoKWAgbmV2ZXIgdGhyb3dzLCBzbyBhIGZhaWxlZCBjYWxsIGFycml2ZXMgYXMgYHsgb2s6IGZhbHNlIH1gIHdpdGhcbiAqIGEgc3RhdHVzIHRoZSBpbnRlcmNlcHRvcnMgYWxyZWFkeSBzYW5pdGl6ZWQ7IGl0IGlzIGxvZ2dlZCBoZXJlIGFuZCByZS1yYWlzZWRcbiAqIGFzIGEgc3RhYmxlIHtAbGluayBFeGVjdXRpb25FcnJvcn0gc28gYW4gdXBzdHJlYW0gbWVzc2FnZSBuZXZlciBiZWNvbWVzIHRoZVxuICogY2FsbGVyJ3MgZXJyb3IgdGV4dC4gYHNpZ25hbGAgaXMgdGhlIGNhbGxlcidzIG93biBjYW5jZWxsYXRpb24gKGFuIGFnZW50XG4gKiBydW4sIGEgcmVxdWVzdCB0ZWFyZG93bik7IGl0IGlzIG1lcmdlZCB3aXRoIHRoZSBzaWduYWwgdGhlIHRpbWVvdXRcbiAqIGludGVyY2VwdG9yIHN1cHBsaWVzIHNvIGVpdGhlciBvbmUgdW53aW5kcyB0aGUgSS9PLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZVJlYWQ8VD4oXG4gIG9wZXJhdGlvbjogc3RyaW5nLFxuICBzZXR0aW5nczogV2ViU2VhcmNoRXhlY3V0aW9uU2V0dGluZ3MsXG4gIGZuOiAoc2lnbmFsPzogQWJvcnRTaWduYWwpID0+IFByb21pc2U8VD4sXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTxUPiB7XG4gIGNvbnN0IHsgZXhlY3V0ZSB9ID0gZ2V0V2ViU2VhcmNoUnVudGltZSgpO1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBleGVjdXRlKFxuICAgIChleGVjdXRlU2lnbmFsKSA9PiBmbihhc3luYy5jb21iaW5lQWJvcnRTaWduYWxzKGV4ZWN1dGVTaWduYWwsIHNpZ25hbCkpLFxuICAgIHNldHRpbmdzLFxuICApO1xuICBpZiAocmVzdWx0Lm9rKSByZXR1cm4gcmVzdWx0LmRhdGE7XG4gIC8vIEEgY2FsbGVyIHRoYXQgY2FuY2VsbGVkIGlzIG5vdCBhIGZhaWx1cmUgd29ydGggcmVwb3J0aW5nIGFzIG9uZS5cbiAgaWYgKHNpZ25hbD8uYWJvcnRlZCkgdGhyb3cgRXhlY3V0aW9uRXJyb3IuY2FuY2VsZWQoKTtcbiAgbG9nZ2VyLndhcm4oXCJleGVjdXRpb24tZmFpbGVkXCIsIHtcbiAgICBvcGVyYXRpb24sXG4gICAgc3RhdHVzOiByZXN1bHQuc3RhdHVzLFxuICAgIGVycm9yOiByZXN1bHQubWVzc2FnZSxcbiAgfSk7XG4gIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihgd2ViLXNlYXJjaDogJHtvcGVyYXRpb259IGZhaWxlZGAsIHtcbiAgICBjb250ZXh0OiB7IG9wZXJhdGlvbiwgc3RhdHVzOiByZXN1bHQuc3RhdHVzIH0sXG4gIH0pO1xufVxuIl19
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Wire-format contract for the web-search add-on: the two tool inputs a
3
+ * model fills in (`web_search`, `web_fetch`) and the results handed back.
4
+ * Pure zod + inferred types (no Node-only imports) so the tool layer, the
5
+ * plugin, and any future UI validate / type against one definition.
6
+ *
7
+ * `web_search` is backed by the Databricks Model Serving native web-search
8
+ * tool (see `provider.ts`): the model searches the web server-side and
9
+ * returns a synthesized answer plus the sources it used. `web_fetch` reads a
10
+ * single page's contents via got-scraping.
11
+ *
12
+ * Array fields intentionally avoid `.min()` / `.nonempty()`: those emit
13
+ * `minItems` in the JSON schema, which some Model Serving endpoints reject
14
+ * ("array types do not support minItems") when the schema is forwarded as a
15
+ * tool definition - the same constraint the email add-on documents.
16
+ *
17
+ * @module
18
+ */
19
+ import { z } from "zod";
20
+ /**
21
+ * Description the model reads for `web_search`. Shared by the Mastra tool and
22
+ * the AppKit tool provider so both hosts describe the tool identically.
23
+ */
24
+ export declare const WEB_SEARCH_TOOL_DESCRIPTION: string;
25
+ /** Description the model reads for `web_fetch`. Shared like {@link WEB_SEARCH_TOOL_DESCRIPTION}. */
26
+ export declare const WEB_FETCH_TOOL_DESCRIPTION: string;
27
+ /** Schema for the `web_search` tool input. */
28
+ export declare const webSearchRequestSchema: z.ZodObject<{
29
+ query: z.ZodString;
30
+ model: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ /** A validated `web_search` request. */
33
+ export type WebSearchRequest = z.infer<typeof webSearchRequestSchema>;
34
+ /** Schema for a single source the model cited while answering. */
35
+ export declare const webSearchCitationSchema: z.ZodObject<{
36
+ url: z.ZodString;
37
+ title: z.ZodOptional<z.ZodString>;
38
+ snippet: z.ZodOptional<z.ZodString>;
39
+ }, z.core.$strip>;
40
+ /** A single cited source ({@link webSearchCitationSchema}). */
41
+ export type WebSearchCitation = z.infer<typeof webSearchCitationSchema>;
42
+ /** Schema for the `web_search` tool output. */
43
+ export declare const webSearchResultSchema: z.ZodObject<{
44
+ query: z.ZodString;
45
+ answer: z.ZodString;
46
+ citations: z.ZodArray<z.ZodObject<{
47
+ url: z.ZodString;
48
+ title: z.ZodOptional<z.ZodString>;
49
+ snippet: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strip>>;
51
+ model: z.ZodString;
52
+ }, z.core.$strip>;
53
+ /** The outcome of a `web_search` call ({@link webSearchResultSchema}). */
54
+ export type WebSearchResult = z.infer<typeof webSearchResultSchema>;
55
+ /** Schema for the `web_fetch` tool input. */
56
+ export declare const webFetchRequestSchema: z.ZodObject<{
57
+ url: z.ZodString;
58
+ format: z.ZodOptional<z.ZodEnum<{
59
+ text: "text";
60
+ html: "html";
61
+ }>>;
62
+ maxLength: z.ZodOptional<z.ZodNumber>;
63
+ }, z.core.$strip>;
64
+ /** A validated `web_fetch` request. */
65
+ export type WebFetchRequest = z.infer<typeof webFetchRequestSchema>;
66
+ /** Schema for the `web_fetch` tool output. */
67
+ export declare const webFetchResultSchema: z.ZodObject<{
68
+ url: z.ZodString;
69
+ status: z.ZodNumber;
70
+ contentType: z.ZodOptional<z.ZodString>;
71
+ title: z.ZodOptional<z.ZodString>;
72
+ content: z.ZodString;
73
+ truncated: z.ZodBoolean;
74
+ }, z.core.$strip>;
75
+ /** The outcome of a `web_fetch` call ({@link webFetchResultSchema}). */
76
+ export type WebFetchResult = z.infer<typeof webFetchResultSchema>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Wire-format contract for the web-search add-on: the two tool inputs a
3
+ * model fills in (`web_search`, `web_fetch`) and the results handed back.
4
+ * Pure zod + inferred types (no Node-only imports) so the tool layer, the
5
+ * plugin, and any future UI validate / type against one definition.
6
+ *
7
+ * `web_search` is backed by the Databricks Model Serving native web-search
8
+ * tool (see `provider.ts`): the model searches the web server-side and
9
+ * returns a synthesized answer plus the sources it used. `web_fetch` reads a
10
+ * single page's contents via got-scraping.
11
+ *
12
+ * Array fields intentionally avoid `.min()` / `.nonempty()`: those emit
13
+ * `minItems` in the JSON schema, which some Model Serving endpoints reject
14
+ * ("array types do not support minItems") when the schema is forwarded as a
15
+ * tool definition - the same constraint the email add-on documents.
16
+ *
17
+ * @module
18
+ */
19
+ import { string } from "@dbx-tools/shared-core";
20
+ import { z } from "zod";
21
+ /**
22
+ * Description the model reads for `web_search`. Shared by the Mastra tool and
23
+ * the AppKit tool provider so both hosts describe the tool identically.
24
+ */
25
+ export const WEB_SEARCH_TOOL_DESCRIPTION = string.toDescription(`
26
+ Search the web for current information and get an answer synthesized from
27
+ live results, with the sources it used. Pass a natural-language query;
28
+ the search runs inside a web-search-capable model (chosen independently
29
+ of your own model). Optionally pass a model name to use a specific
30
+ web-search model. Use it whenever a question needs up-to-date or external
31
+ information you don't already have.
32
+ `);
33
+ /** Description the model reads for `web_fetch`. Shared like {@link WEB_SEARCH_TOOL_DESCRIPTION}. */
34
+ export const WEB_FETCH_TOOL_DESCRIPTION = string.toDescription(`
35
+ Fetch a single web page and return its readable contents. Pass an
36
+ absolute URL (including https://); set format to "html" for raw markup
37
+ instead of extracted text. Use it to read a page returned by web_search
38
+ or provided by the user. Content is length-capped; fetching a URL outside
39
+ the configured allow-list is refused.
40
+ `);
41
+ /** Schema for the `web_search` tool input. */
42
+ export const webSearchRequestSchema = z.object({
43
+ query: z
44
+ .string()
45
+ .describe("What to search the web for, phrased as a natural-language question or request. The model searches and answers in one step."),
46
+ model: z
47
+ .string()
48
+ .optional()
49
+ .describe(string.toDescription(`
50
+ Optional web-search-capable model to use (a Databricks serving
51
+ endpoint name like "databricks-gemini-3-pro", a loose name like "gpt"
52
+ or "gemini", or a capability class). Defaults to the plugin's
53
+ configured web-search model. The web-search tool resolves its own
54
+ model independently of the calling agent's chat model, since not
55
+ every chat model supports web search.
56
+ `)),
57
+ });
58
+ /** Schema for a single source the model cited while answering. */
59
+ export const webSearchCitationSchema = z.object({
60
+ url: z.string().describe("The source URL the answer drew on."),
61
+ title: z.string().optional().describe("The source page title, when available."),
62
+ snippet: z.string().optional().describe("A short excerpt from the source, when available."),
63
+ });
64
+ /** Schema for the `web_search` tool output. */
65
+ export const webSearchResultSchema = z.object({
66
+ query: z.string().describe("Echo of the query that was searched."),
67
+ answer: z.string().describe("The model's answer, synthesized from live web results."),
68
+ citations: z
69
+ .array(webSearchCitationSchema)
70
+ .describe("Sources the answer drew on. When an allow-list is configured, citations whose URL is not permitted are silently omitted."),
71
+ model: z.string().describe("The serving endpoint that produced the answer."),
72
+ });
73
+ /** Schema for the `web_fetch` tool input. */
74
+ export const webFetchRequestSchema = z.object({
75
+ url: z
76
+ .string()
77
+ .describe("The absolute URL to fetch (must include the scheme, e.g. https://). When an allow-list is configured, a URL it does not permit is refused."),
78
+ format: z
79
+ .enum(["text", "html"])
80
+ .optional()
81
+ .describe(string.toDescription(`
82
+ Return format: "text" (default) strips the page to readable plain
83
+ text; "html" returns the raw response body. Prefer "text" unless you
84
+ need the markup.
85
+ `)),
86
+ maxLength: z
87
+ .number()
88
+ .int()
89
+ .positive()
90
+ .optional()
91
+ .describe("Truncate the returned content to at most this many characters (the plugin caps this at its configured limit)."),
92
+ });
93
+ /** Schema for the `web_fetch` tool output. */
94
+ export const webFetchResultSchema = z.object({
95
+ url: z.string().describe("The final URL fetched (after redirects)."),
96
+ status: z.number().describe("HTTP status code of the response."),
97
+ contentType: z.string().optional().describe("Response `Content-Type`, when the server sent one."),
98
+ title: z.string().optional().describe("The page <title>, when one was present."),
99
+ content: z.string().describe("The page content in the requested format (text or html)."),
100
+ truncated: z.boolean().describe("True when `content` was cut off at the length cap."),
101
+ });
102
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NjaGVtYS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDaEQsT0FBTyxFQUFFLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUV4Qjs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDOzs7Ozs7O0NBTy9ELENBQUMsQ0FBQztBQUVILG9HQUFvRztBQUNwRyxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDOzs7Ozs7Q0FNOUQsQ0FBQyxDQUFDO0FBRUgsOENBQThDO0FBQzlDLE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDN0MsS0FBSyxFQUFFLENBQUM7U0FDTCxNQUFNLEVBQUU7U0FDUixRQUFRLENBQ1AsNEhBQTRILENBQzdIO0lBQ0gsS0FBSyxFQUFFLENBQUM7U0FDTCxNQUFNLEVBQUU7U0FDUixRQUFRLEVBQUU7U0FDVixRQUFRLENBQ1AsTUFBTSxDQUFDLGFBQWEsQ0FBQzs7Ozs7OztPQU9wQixDQUFDLENBQ0g7Q0FDSixDQUFDLENBQUM7QUFLSCxrRUFBa0U7QUFDbEUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM5QyxHQUFHLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxvQ0FBb0MsQ0FBQztJQUM5RCxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQyx3Q0FBd0MsQ0FBQztJQUMvRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsQ0FBQyxrREFBa0QsQ0FBQztDQUM1RixDQUFDLENBQUM7QUFLSCwrQ0FBK0M7QUFDL0MsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM1QyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxzQ0FBc0MsQ0FBQztJQUNsRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyx3REFBd0QsQ0FBQztJQUNyRixTQUFTLEVBQUUsQ0FBQztTQUNULEtBQUssQ0FBQyx1QkFBdUIsQ0FBQztTQUM5QixRQUFRLENBQ1AsMEhBQTBILENBQzNIO0lBQ0gsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0RBQWdELENBQUM7Q0FDN0UsQ0FBQyxDQUFDO0FBS0gsNkNBQTZDO0FBQzdDLE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDNUMsR0FBRyxFQUFFLENBQUM7U0FDSCxNQUFNLEVBQUU7U0FDUixRQUFRLENBQ1AsNElBQTRJLENBQzdJO0lBQ0gsTUFBTSxFQUFFLENBQUM7U0FDTixJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7U0FDdEIsUUFBUSxFQUFFO1NBQ1YsUUFBUSxDQUNQLE1BQU0sQ0FBQyxhQUFhLENBQUM7Ozs7T0FJcEIsQ0FBQyxDQUNIO0lBQ0gsU0FBUyxFQUFFLENBQUM7U0FDVCxNQUFNLEVBQUU7U0FDUixHQUFHLEVBQUU7U0FDTCxRQUFRLEVBQUU7U0FDVixRQUFRLEVBQUU7U0FDVixRQUFRLENBQ1AsK0dBQStHLENBQ2hIO0NBQ0osQ0FBQyxDQUFDO0FBS0gsOENBQThDO0FBQzlDLE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDM0MsR0FBRyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsMENBQTBDLENBQUM7SUFDcEUsTUFBTSxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsbUNBQW1DLENBQUM7SUFDaEUsV0FBVyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsb0RBQW9ELENBQUM7SUFDakcsS0FBSyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMseUNBQXlDLENBQUM7SUFDaEYsT0FBTyxFQUFFLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsMERBQTBELENBQUM7SUFDeEYsU0FBUyxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsb0RBQW9ELENBQUM7Q0FDdEYsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBXaXJlLWZvcm1hdCBjb250cmFjdCBmb3IgdGhlIHdlYi1zZWFyY2ggYWRkLW9uOiB0aGUgdHdvIHRvb2wgaW5wdXRzIGFcbiAqIG1vZGVsIGZpbGxzIGluIChgd2ViX3NlYXJjaGAsIGB3ZWJfZmV0Y2hgKSBhbmQgdGhlIHJlc3VsdHMgaGFuZGVkIGJhY2suXG4gKiBQdXJlIHpvZCArIGluZmVycmVkIHR5cGVzIChubyBOb2RlLW9ubHkgaW1wb3J0cykgc28gdGhlIHRvb2wgbGF5ZXIsIHRoZVxuICogcGx1Z2luLCBhbmQgYW55IGZ1dHVyZSBVSSB2YWxpZGF0ZSAvIHR5cGUgYWdhaW5zdCBvbmUgZGVmaW5pdGlvbi5cbiAqXG4gKiBgd2ViX3NlYXJjaGAgaXMgYmFja2VkIGJ5IHRoZSBEYXRhYnJpY2tzIE1vZGVsIFNlcnZpbmcgbmF0aXZlIHdlYi1zZWFyY2hcbiAqIHRvb2wgKHNlZSBgcHJvdmlkZXIudHNgKTogdGhlIG1vZGVsIHNlYXJjaGVzIHRoZSB3ZWIgc2VydmVyLXNpZGUgYW5kXG4gKiByZXR1cm5zIGEgc3ludGhlc2l6ZWQgYW5zd2VyIHBsdXMgdGhlIHNvdXJjZXMgaXQgdXNlZC4gYHdlYl9mZXRjaGAgcmVhZHMgYVxuICogc2luZ2xlIHBhZ2UncyBjb250ZW50cyB2aWEgZ290LXNjcmFwaW5nLlxuICpcbiAqIEFycmF5IGZpZWxkcyBpbnRlbnRpb25hbGx5IGF2b2lkIGAubWluKClgIC8gYC5ub25lbXB0eSgpYDogdGhvc2UgZW1pdFxuICogYG1pbkl0ZW1zYCBpbiB0aGUgSlNPTiBzY2hlbWEsIHdoaWNoIHNvbWUgTW9kZWwgU2VydmluZyBlbmRwb2ludHMgcmVqZWN0XG4gKiAoXCJhcnJheSB0eXBlcyBkbyBub3Qgc3VwcG9ydCBtaW5JdGVtc1wiKSB3aGVuIHRoZSBzY2hlbWEgaXMgZm9yd2FyZGVkIGFzIGFcbiAqIHRvb2wgZGVmaW5pdGlvbiAtIHRoZSBzYW1lIGNvbnN0cmFpbnQgdGhlIGVtYWlsIGFkZC1vbiBkb2N1bWVudHMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuXG4vKipcbiAqIERlc2NyaXB0aW9uIHRoZSBtb2RlbCByZWFkcyBmb3IgYHdlYl9zZWFyY2hgLiBTaGFyZWQgYnkgdGhlIE1hc3RyYSB0b29sIGFuZFxuICogdGhlIEFwcEtpdCB0b29sIHByb3ZpZGVyIHNvIGJvdGggaG9zdHMgZGVzY3JpYmUgdGhlIHRvb2wgaWRlbnRpY2FsbHkuXG4gKi9cbmV4cG9ydCBjb25zdCBXRUJfU0VBUkNIX1RPT0xfREVTQ1JJUFRJT04gPSBzdHJpbmcudG9EZXNjcmlwdGlvbihgXG4gIFNlYXJjaCB0aGUgd2ViIGZvciBjdXJyZW50IGluZm9ybWF0aW9uIGFuZCBnZXQgYW4gYW5zd2VyIHN5bnRoZXNpemVkIGZyb21cbiAgbGl2ZSByZXN1bHRzLCB3aXRoIHRoZSBzb3VyY2VzIGl0IHVzZWQuIFBhc3MgYSBuYXR1cmFsLWxhbmd1YWdlIHF1ZXJ5O1xuICB0aGUgc2VhcmNoIHJ1bnMgaW5zaWRlIGEgd2ViLXNlYXJjaC1jYXBhYmxlIG1vZGVsIChjaG9zZW4gaW5kZXBlbmRlbnRseVxuICBvZiB5b3VyIG93biBtb2RlbCkuIE9wdGlvbmFsbHkgcGFzcyBhIG1vZGVsIG5hbWUgdG8gdXNlIGEgc3BlY2lmaWNcbiAgd2ViLXNlYXJjaCBtb2RlbC4gVXNlIGl0IHdoZW5ldmVyIGEgcXVlc3Rpb24gbmVlZHMgdXAtdG8tZGF0ZSBvciBleHRlcm5hbFxuICBpbmZvcm1hdGlvbiB5b3UgZG9uJ3QgYWxyZWFkeSBoYXZlLlxuYCk7XG5cbi8qKiBEZXNjcmlwdGlvbiB0aGUgbW9kZWwgcmVhZHMgZm9yIGB3ZWJfZmV0Y2hgLiBTaGFyZWQgbGlrZSB7QGxpbmsgV0VCX1NFQVJDSF9UT09MX0RFU0NSSVBUSU9OfS4gKi9cbmV4cG9ydCBjb25zdCBXRUJfRkVUQ0hfVE9PTF9ERVNDUklQVElPTiA9IHN0cmluZy50b0Rlc2NyaXB0aW9uKGBcbiAgRmV0Y2ggYSBzaW5nbGUgd2ViIHBhZ2UgYW5kIHJldHVybiBpdHMgcmVhZGFibGUgY29udGVudHMuIFBhc3MgYW5cbiAgYWJzb2x1dGUgVVJMIChpbmNsdWRpbmcgaHR0cHM6Ly8pOyBzZXQgZm9ybWF0IHRvIFwiaHRtbFwiIGZvciByYXcgbWFya3VwXG4gIGluc3RlYWQgb2YgZXh0cmFjdGVkIHRleHQuIFVzZSBpdCB0byByZWFkIGEgcGFnZSByZXR1cm5lZCBieSB3ZWJfc2VhcmNoXG4gIG9yIHByb3ZpZGVkIGJ5IHRoZSB1c2VyLiBDb250ZW50IGlzIGxlbmd0aC1jYXBwZWQ7IGZldGNoaW5nIGEgVVJMIG91dHNpZGVcbiAgdGhlIGNvbmZpZ3VyZWQgYWxsb3ctbGlzdCBpcyByZWZ1c2VkLlxuYCk7XG5cbi8qKiBTY2hlbWEgZm9yIHRoZSBgd2ViX3NlYXJjaGAgdG9vbCBpbnB1dC4gKi9cbmV4cG9ydCBjb25zdCB3ZWJTZWFyY2hSZXF1ZXN0U2NoZW1hID0gei5vYmplY3Qoe1xuICBxdWVyeTogelxuICAgIC5zdHJpbmcoKVxuICAgIC5kZXNjcmliZShcbiAgICAgIFwiV2hhdCB0byBzZWFyY2ggdGhlIHdlYiBmb3IsIHBocmFzZWQgYXMgYSBuYXR1cmFsLWxhbmd1YWdlIHF1ZXN0aW9uIG9yIHJlcXVlc3QuIFRoZSBtb2RlbCBzZWFyY2hlcyBhbmQgYW5zd2VycyBpbiBvbmUgc3RlcC5cIixcbiAgICApLFxuICBtb2RlbDogelxuICAgIC5zdHJpbmcoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgc3RyaW5nLnRvRGVzY3JpcHRpb24oYFxuICAgICAgICBPcHRpb25hbCB3ZWItc2VhcmNoLWNhcGFibGUgbW9kZWwgdG8gdXNlIChhIERhdGFicmlja3Mgc2VydmluZ1xuICAgICAgICBlbmRwb2ludCBuYW1lIGxpa2UgXCJkYXRhYnJpY2tzLWdlbWluaS0zLXByb1wiLCBhIGxvb3NlIG5hbWUgbGlrZSBcImdwdFwiXG4gICAgICAgIG9yIFwiZ2VtaW5pXCIsIG9yIGEgY2FwYWJpbGl0eSBjbGFzcykuIERlZmF1bHRzIHRvIHRoZSBwbHVnaW4nc1xuICAgICAgICBjb25maWd1cmVkIHdlYi1zZWFyY2ggbW9kZWwuIFRoZSB3ZWItc2VhcmNoIHRvb2wgcmVzb2x2ZXMgaXRzIG93blxuICAgICAgICBtb2RlbCBpbmRlcGVuZGVudGx5IG9mIHRoZSBjYWxsaW5nIGFnZW50J3MgY2hhdCBtb2RlbCwgc2luY2Ugbm90XG4gICAgICAgIGV2ZXJ5IGNoYXQgbW9kZWwgc3VwcG9ydHMgd2ViIHNlYXJjaC5cbiAgICAgIGApLFxuICAgICksXG59KTtcblxuLyoqIEEgdmFsaWRhdGVkIGB3ZWJfc2VhcmNoYCByZXF1ZXN0LiAqL1xuZXhwb3J0IHR5cGUgV2ViU2VhcmNoUmVxdWVzdCA9IHouaW5mZXI8dHlwZW9mIHdlYlNlYXJjaFJlcXVlc3RTY2hlbWE+O1xuXG4vKiogU2NoZW1hIGZvciBhIHNpbmdsZSBzb3VyY2UgdGhlIG1vZGVsIGNpdGVkIHdoaWxlIGFuc3dlcmluZy4gKi9cbmV4cG9ydCBjb25zdCB3ZWJTZWFyY2hDaXRhdGlvblNjaGVtYSA9IHoub2JqZWN0KHtcbiAgdXJsOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVGhlIHNvdXJjZSBVUkwgdGhlIGFuc3dlciBkcmV3IG9uLlwiKSxcbiAgdGl0bGU6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZXNjcmliZShcIlRoZSBzb3VyY2UgcGFnZSB0aXRsZSwgd2hlbiBhdmFpbGFibGUuXCIpLFxuICBzbmlwcGV0OiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJBIHNob3J0IGV4Y2VycHQgZnJvbSB0aGUgc291cmNlLCB3aGVuIGF2YWlsYWJsZS5cIiksXG59KTtcblxuLyoqIEEgc2luZ2xlIGNpdGVkIHNvdXJjZSAoe0BsaW5rIHdlYlNlYXJjaENpdGF0aW9uU2NoZW1hfSkuICovXG5leHBvcnQgdHlwZSBXZWJTZWFyY2hDaXRhdGlvbiA9IHouaW5mZXI8dHlwZW9mIHdlYlNlYXJjaENpdGF0aW9uU2NoZW1hPjtcblxuLyoqIFNjaGVtYSBmb3IgdGhlIGB3ZWJfc2VhcmNoYCB0b29sIG91dHB1dC4gKi9cbmV4cG9ydCBjb25zdCB3ZWJTZWFyY2hSZXN1bHRTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIHF1ZXJ5OiB6LnN0cmluZygpLmRlc2NyaWJlKFwiRWNobyBvZiB0aGUgcXVlcnkgdGhhdCB3YXMgc2VhcmNoZWQuXCIpLFxuICBhbnN3ZXI6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJUaGUgbW9kZWwncyBhbnN3ZXIsIHN5bnRoZXNpemVkIGZyb20gbGl2ZSB3ZWIgcmVzdWx0cy5cIiksXG4gIGNpdGF0aW9uczogelxuICAgIC5hcnJheSh3ZWJTZWFyY2hDaXRhdGlvblNjaGVtYSlcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIlNvdXJjZXMgdGhlIGFuc3dlciBkcmV3IG9uLiBXaGVuIGFuIGFsbG93LWxpc3QgaXMgY29uZmlndXJlZCwgY2l0YXRpb25zIHdob3NlIFVSTCBpcyBub3QgcGVybWl0dGVkIGFyZSBzaWxlbnRseSBvbWl0dGVkLlwiLFxuICAgICksXG4gIG1vZGVsOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVGhlIHNlcnZpbmcgZW5kcG9pbnQgdGhhdCBwcm9kdWNlZCB0aGUgYW5zd2VyLlwiKSxcbn0pO1xuXG4vKiogVGhlIG91dGNvbWUgb2YgYSBgd2ViX3NlYXJjaGAgY2FsbCAoe0BsaW5rIHdlYlNlYXJjaFJlc3VsdFNjaGVtYX0pLiAqL1xuZXhwb3J0IHR5cGUgV2ViU2VhcmNoUmVzdWx0ID0gei5pbmZlcjx0eXBlb2Ygd2ViU2VhcmNoUmVzdWx0U2NoZW1hPjtcblxuLyoqIFNjaGVtYSBmb3IgdGhlIGB3ZWJfZmV0Y2hgIHRvb2wgaW5wdXQuICovXG5leHBvcnQgY29uc3Qgd2ViRmV0Y2hSZXF1ZXN0U2NoZW1hID0gei5vYmplY3Qoe1xuICB1cmw6IHpcbiAgICAuc3RyaW5nKClcbiAgICAuZGVzY3JpYmUoXG4gICAgICBcIlRoZSBhYnNvbHV0ZSBVUkwgdG8gZmV0Y2ggKG11c3QgaW5jbHVkZSB0aGUgc2NoZW1lLCBlLmcuIGh0dHBzOi8vKS4gV2hlbiBhbiBhbGxvdy1saXN0IGlzIGNvbmZpZ3VyZWQsIGEgVVJMIGl0IGRvZXMgbm90IHBlcm1pdCBpcyByZWZ1c2VkLlwiLFxuICAgICksXG4gIGZvcm1hdDogelxuICAgIC5lbnVtKFtcInRleHRcIiwgXCJodG1sXCJdKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgc3RyaW5nLnRvRGVzY3JpcHRpb24oYFxuICAgICAgICBSZXR1cm4gZm9ybWF0OiBcInRleHRcIiAoZGVmYXVsdCkgc3RyaXBzIHRoZSBwYWdlIHRvIHJlYWRhYmxlIHBsYWluXG4gICAgICAgIHRleHQ7IFwiaHRtbFwiIHJldHVybnMgdGhlIHJhdyByZXNwb25zZSBib2R5LiBQcmVmZXIgXCJ0ZXh0XCIgdW5sZXNzIHlvdVxuICAgICAgICBuZWVkIHRoZSBtYXJrdXAuXG4gICAgICBgKSxcbiAgICApLFxuICBtYXhMZW5ndGg6IHpcbiAgICAubnVtYmVyKClcbiAgICAuaW50KClcbiAgICAucG9zaXRpdmUoKVxuICAgIC5vcHRpb25hbCgpXG4gICAgLmRlc2NyaWJlKFxuICAgICAgXCJUcnVuY2F0ZSB0aGUgcmV0dXJuZWQgY29udGVudCB0byBhdCBtb3N0IHRoaXMgbWFueSBjaGFyYWN0ZXJzICh0aGUgcGx1Z2luIGNhcHMgdGhpcyBhdCBpdHMgY29uZmlndXJlZCBsaW1pdCkuXCIsXG4gICAgKSxcbn0pO1xuXG4vKiogQSB2YWxpZGF0ZWQgYHdlYl9mZXRjaGAgcmVxdWVzdC4gKi9cbmV4cG9ydCB0eXBlIFdlYkZldGNoUmVxdWVzdCA9IHouaW5mZXI8dHlwZW9mIHdlYkZldGNoUmVxdWVzdFNjaGVtYT47XG5cbi8qKiBTY2hlbWEgZm9yIHRoZSBgd2ViX2ZldGNoYCB0b29sIG91dHB1dC4gKi9cbmV4cG9ydCBjb25zdCB3ZWJGZXRjaFJlc3VsdFNjaGVtYSA9IHoub2JqZWN0KHtcbiAgdXJsOiB6LnN0cmluZygpLmRlc2NyaWJlKFwiVGhlIGZpbmFsIFVSTCBmZXRjaGVkIChhZnRlciByZWRpcmVjdHMpLlwiKSxcbiAgc3RhdHVzOiB6Lm51bWJlcigpLmRlc2NyaWJlKFwiSFRUUCBzdGF0dXMgY29kZSBvZiB0aGUgcmVzcG9uc2UuXCIpLFxuICBjb250ZW50VHlwZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLmRlc2NyaWJlKFwiUmVzcG9uc2UgYENvbnRlbnQtVHlwZWAsIHdoZW4gdGhlIHNlcnZlciBzZW50IG9uZS5cIiksXG4gIHRpdGxlOiB6LnN0cmluZygpLm9wdGlvbmFsKCkuZGVzY3JpYmUoXCJUaGUgcGFnZSA8dGl0bGU+LCB3aGVuIG9uZSB3YXMgcHJlc2VudC5cIiksXG4gIGNvbnRlbnQ6IHouc3RyaW5nKCkuZGVzY3JpYmUoXCJUaGUgcGFnZSBjb250ZW50IGluIHRoZSByZXF1ZXN0ZWQgZm9ybWF0ICh0ZXh0IG9yIGh0bWwpLlwiKSxcbiAgdHJ1bmNhdGVkOiB6LmJvb2xlYW4oKS5kZXNjcmliZShcIlRydWUgd2hlbiBgY29udGVudGAgd2FzIGN1dCBvZmYgYXQgdGhlIGxlbmd0aCBjYXAuXCIpLFxufSk7XG5cbi8qKiBUaGUgb3V0Y29tZSBvZiBhIGB3ZWJfZmV0Y2hgIGNhbGwgKHtAbGluayB3ZWJGZXRjaFJlc3VsdFNjaGVtYX0pLiAqL1xuZXhwb3J0IHR5cGUgV2ViRmV0Y2hSZXN1bHQgPSB6LmluZmVyPHR5cGVvZiB3ZWJGZXRjaFJlc3VsdFNjaGVtYT47XG4iXX0=
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Last-resort scraping fallback for `web_search`, used ONLY when the
3
+ * workspace has no Databricks web-search-capable model deployed (no GPT /
4
+ * Gemini serving endpoint). The native Databricks web-search tool is always
5
+ * preferred (see `search.ts`); this exists so the tool still returns useful
6
+ * results in an environment that can't run it, rather than erroring on every
7
+ * call.
8
+ *
9
+ * It queries DuckDuckGo's no-JS HTML endpoint through `got-scraping`
10
+ * (browser-like fingerprints so the request isn't blocked) via a GET with the
11
+ * query in the query string - a POST to the same endpoint trips DDG's bot
12
+ * challenge (HTTP 202), while the GET returns normal result markup. It then
13
+ * parses the result anchors + snippets. Unlike the native tool there is no
14
+ * model synthesizing an answer, so `answer` is a short lead-in over the top
15
+ * snippets and the substance rides in `citations` - the calling agent reads
16
+ * those and writes its own answer.
17
+ *
18
+ * @module
19
+ */
20
+ import type { ResolvedWebSearchConfig } from "./config.js";
21
+ import type { WebSearchRequest, WebSearchResult } from "./schema.js";
22
+ /**
23
+ * Run a scraping search over DuckDuckGo. Returns the same
24
+ * {@link WebSearchResult} shape as the native path, with `model` set to
25
+ * `"scrape:duckduckgo"` so callers can tell how the result was produced.
26
+ * Citations are filtered through the configured URL allow-list. `signal`
27
+ * cancels the in-flight request.
28
+ */
29
+ export declare function runScrapeSearch(request: WebSearchRequest, config: ResolvedWebSearchConfig, signal?: AbortSignal): Promise<WebSearchResult>;