aieo 0.1.41 → 0.2.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/index.d.cts DELETED
@@ -1,846 +0,0 @@
1
- import { ModelMessage, LanguageModel, ToolSet } from 'ai';
2
- export { ModelMessage, Tool, ToolSet } from 'ai';
3
- import { AnthropicProviderOptions } from '@ai-sdk/anthropic';
4
-
5
- interface Conversation {
6
- id: string;
7
- summary: string;
8
- timestamp: number;
9
- }
10
- interface ConversationData {
11
- id: string;
12
- summary: string;
13
- messages: ModelMessage[];
14
- lastUpdated: number;
15
- }
16
- declare const MAX_CONVERSATIONS = 100;
17
- declare abstract class ConversationStorage {
18
- abstract currentConversationId(): Promise<string>;
19
- abstract selectConversation(conversationId: string): Promise<void>;
20
- abstract listConversations(): Promise<Conversation[]>;
21
- abstract getConversation(conversationId: string): Promise<ConversationData | null>;
22
- abstract storeConversationData(conversationId: string, data: ConversationData): Promise<void>;
23
- abstract deleteConversationData(conversationId: string): Promise<boolean>;
24
- createConversation(messages: ModelMessage[]): Promise<ConversationData>;
25
- addMessageToConversation(conversationId: string, message: ModelMessage): Promise<ConversationData>;
26
- getCurrentConversation(): Promise<ConversationData | null>;
27
- getLatestConversation(): Promise<ConversationData | null>;
28
- updateConversationSummary(conversationId: string, summary: string): Promise<ConversationData>;
29
- private pruneOldConversations;
30
- protected generateSummary(content: string): string;
31
- }
32
-
33
- interface Logger {
34
- info(message: string): void;
35
- error(message: string): void;
36
- warn?(message: string): void;
37
- debug?(message: string): void;
38
- trace?(message: string): void;
39
- fatal?(message: string): void;
40
- }
41
- declare function consoleLogger(name: string): Logger;
42
-
43
- type Provider = "anthropic" | "google" | "openai" | "openrouter" | "xai";
44
- /**
45
- * Per-provider path Bifrost (and compatible gateways) expose for each
46
- * provider's drop-in SDK. Exported so spawners can build per-provider
47
- * URLs without hand-rolling the suffix.
48
- *
49
- * NOTE: OpenRouter has no dedicated Bifrost route and rides the OpenAI
50
- * path (model id prefixed `openrouter/` in getModel so Bifrost routes it
51
- * upstream). If you spawn an agent in a way where the runtime would
52
- * override the model to an OpenRouter model, you want the OpenAI suffix
53
- * here too. xAI likewise rides the OpenAI-compat path (model id prefixed
54
- * `xai/` in getModel), since Bifrost has no dedicated Grok route.
55
- */
56
- declare const GATEWAY_PATHS: Record<Provider, string>;
57
- declare function getGatewayBaseURL(provider: Provider): string | undefined;
58
- /**
59
- * Build the fully-formed `<base>/<provider-path>` URL that an official
60
- * SDK (or Goose, or a workflow LLM node) expects.
61
- *
62
- * This is the single source of truth for "which suffix do I add to the
63
- * gateway root for this provider." Spawners (Hive, Stakwork, anyone
64
- * launching an agent) should call this when stamping `ANTHROPIC_BASE_URL`,
65
- * `OPENAI_BASE_URL`, `GOOGLE_BASE_URL`, etc. on a child process or
66
- * workflow context.
67
- *
68
- * Behavior:
69
- * - Trims a trailing slash.
70
- * - Returns the URL unchanged if it already ends with the provider's
71
- * gateway path (`/anthropic/v1`, `/openai/v1`, `/genai/v1beta`), so
72
- * the function is idempotent.
73
- * - Returns the URL unchanged if it already targets a provider/version
74
- * path (e.g. `.../v1`, `.../v1beta`, `.../anthropic/...`). This lets
75
- * callers pass fully-qualified URLs through without surprise.
76
- * - Otherwise appends `GATEWAY_PATHS[provider]`.
77
- *
78
- * Examples
79
- * --------
80
- * gatewayUrlFor("anthropic", "https://swarm38.sphinx.chat:8181")
81
- * => "https://swarm38.sphinx.chat:8181/anthropic/v1"
82
- *
83
- * gatewayUrlFor("openai", "https://swarm38.sphinx.chat:8181/")
84
- * => "https://swarm38.sphinx.chat:8181/openai/v1"
85
- *
86
- * gatewayUrlFor("anthropic", "https://swarm38.sphinx.chat:8181/anthropic/v1")
87
- * => "https://swarm38.sphinx.chat:8181/anthropic/v1" (idempotent)
88
- *
89
- * gatewayUrlFor("openai", "https://api.openai.com/v1")
90
- * => "https://api.openai.com/v1" (left alone)
91
- */
92
- declare function gatewayUrlFor(provider: Provider, baseUrl: string): string;
93
- /**
94
- * Like {@link gatewayUrlFor} but takes a model name (shortcut like
95
- * `"sonnet"`, namespaced like `"anthropic/claude-sonnet-5"`, or a full
96
- * model id like `"claude-sonnet-5"`) and resolves the provider for you.
97
- *
98
- * Convenient for spawners that have a model name in hand but not a
99
- * provider — e.g. Hive picking up a user's chosen model and needing to
100
- * tell the agent which Bifrost route to use.
101
- */
102
- declare function gatewayUrlForModel(modelName: string | undefined, baseUrl: string): string;
103
- /**
104
- * Build a `{ ANTHROPIC_BASE_URL, OPENAI_BASE_URL, GOOGLE_BASE_URL }`-style
105
- * env map for spawning agents (Goose, Python, raw SDK callers) that
106
- * don't normalize the URL themselves. Pass the gateway root once; get
107
- * back the per-provider URLs.
108
- *
109
- * The keys match the env var names the official SDKs read by default:
110
- * - `ANTHROPIC_BASE_URL` (anthropic-sdk-{ts,python,go})
111
- * - `OPENAI_BASE_URL` (openai SDK)
112
- * - `GOOGLE_GENERATIVE_AI_BASE_URL` (google-genai SDK / `@ai-sdk/google`)
113
- *
114
- * Spawners can spread the result into the child's env. Callers that
115
- * only need one provider should use {@link gatewayUrlFor} directly.
116
- */
117
- declare function gatewayEnvForProviders(baseUrl: string): {
118
- ANTHROPIC_BASE_URL: string;
119
- OPENAI_BASE_URL: string;
120
- GOOGLE_GENERATIVE_AI_BASE_URL: string;
121
- };
122
- /**
123
- * @deprecated Use {@link gatewayUrlFor}. Kept as an alias for internal
124
- * call sites; both have identical behavior.
125
- */
126
- declare const normalizeCallerBaseURL: typeof gatewayUrlFor;
127
- declare const PROVIDERS: Provider[];
128
- type ModelName = "sonnet" | "opus" | "haiku" | "gemini" | "gpt" | "kimi" | "glm" | "grok";
129
- type ModelId = string;
130
- declare const MODELS: Record<Provider, Partial<Record<ModelName, ModelId>>>;
131
- declare const DEFAULT_MODELS: Record<Provider, string>;
132
- declare function getLightModelForProvider(provider: Provider): string;
133
- interface TokenPricing {
134
- inputTokenPrice: number;
135
- outputTokenPrice: number;
136
- cacheReadPrice?: number;
137
- cacheWritePrice?: number;
138
- }
139
- interface TokenUsageForCost {
140
- input: number;
141
- cache_read: number;
142
- cache_write: number;
143
- output: number;
144
- }
145
- declare function computeSessionCost(provider: Provider, usage: TokenUsageForCost, modelId?: string, actualCost?: number): number;
146
- declare function getProviderForModel(modelName?: ModelName | string): Provider;
147
- /**
148
- * Treat blank/whitespace-only keys as absent.
149
- *
150
- * A truthy-but-blank key (a request body with `apiKey: " "`, an env var set to
151
- * an empty-ish value) otherwise sails past every `!!key` check and reaches the
152
- * provider SDK, which happily builds `Authorization: Bearer ` and gets a
153
- * confusing 401 ("Missing Authentication header" from OpenRouter) on every
154
- * single call instead of failing fast. Also trims stray whitespace so a key
155
- * pasted with a trailing space still authenticates.
156
- */
157
- declare function normalizeApiKey(key?: string | null): string | undefined;
158
- /**
159
- * The env var each provider's API key is read from. Exported so a host with
160
- * its own secret store knows which NAME to look up (see resolve.ts).
161
- */
162
- declare const API_KEY_ENV: Record<Provider, string>;
163
- declare function hasApiKeyForProvider(provider: Provider | string): boolean;
164
- declare function getApiKeyForProvider(provider: Provider | string): string;
165
- interface GetModelOptions {
166
- apiKey?: string;
167
- baseUrl?: string;
168
- modelName?: ModelName | string;
169
- cwd?: string;
170
- executablePath?: string;
171
- logger?: Logger;
172
- /**
173
- * Custom HTTP headers to attach to every request the provider client makes
174
- * to the LLM endpoint. Useful for gateway auth, tenant IDs, etc.
175
- */
176
- headers?: Record<string, string>;
177
- /**
178
- * AbortSignal from the run's AbortController. When fired (busy timeout /
179
- * `/repo/agent/abort`), in-flight model calls and streams are cancelled.
180
- * Stays armed for the entire request/stream lifetime.
181
- */
182
- abortSignal?: AbortSignal;
183
- /**
184
- * Per-request timeout in milliseconds for the connect / first-response
185
- * phase only. Once the provider replies with headers the timer is disarmed,
186
- * so a slow-but-progressing stream is never killed. Defaults to the
187
- * `LLM_HTTP_TIMEOUT_MS` env var when not supplied.
188
- */
189
- timeoutMs?: number;
190
- }
191
- interface ModelDetails {
192
- provider: Provider;
193
- apiKey: string;
194
- model: LanguageModel;
195
- modelId: string;
196
- contextLimit: number;
197
- }
198
- declare function getModelDetails(modelName?: ModelName | string, apiKeyIn?: string, baseUrl?: string, headers?: Record<string, string>, abortSignal?: AbortSignal, timeoutMs?: number): ModelDetails;
199
- declare function getModel(provider: Provider, opts?: string | GetModelOptions): LanguageModel;
200
- /**
201
- * Get the context window size (max input tokens) for a given model.
202
- */
203
- declare function getContextLimit(modelId: string, provider: Provider): number;
204
- declare function getTokenPricing(provider: Provider): TokenPricing;
205
- declare function loadModelPricing(): Promise<void>;
206
- type ThinkingSpeed = "thinking" | "fast";
207
- declare function getProviderOptions(provider: Provider, thinkingSpeed?: ThinkingSpeed, modelName?: string): {
208
- anthropic: AnthropicProviderOptions;
209
- google?: undefined;
210
- openai?: undefined;
211
- openrouter?: undefined;
212
- xai?: undefined;
213
- } | {
214
- google: {
215
- thinkingConfig: {
216
- thinkingBudget: number;
217
- };
218
- };
219
- anthropic?: undefined;
220
- openai?: undefined;
221
- openrouter?: undefined;
222
- xai?: undefined;
223
- } | {
224
- openai: {};
225
- anthropic?: undefined;
226
- google?: undefined;
227
- openrouter?: undefined;
228
- xai?: undefined;
229
- } | {
230
- openrouter: {
231
- usage: {
232
- include: true;
233
- };
234
- };
235
- anthropic?: undefined;
236
- google?: undefined;
237
- openai?: undefined;
238
- xai?: undefined;
239
- } | {
240
- xai: {};
241
- anthropic?: undefined;
242
- google?: undefined;
243
- openai?: undefined;
244
- openrouter?: undefined;
245
- };
246
- interface LLMConfig {
247
- provider: Provider;
248
- apiKey: string;
249
- model: LanguageModel;
250
- modelName?: string;
251
- }
252
- /**
253
- * Resolve LLM configuration from request params with env fallback.
254
- * Priority: request body/query → env vars → defaults.
255
- * Use `light: true` for batch/cheap operations (descriptions, learnings, etc.)
256
- */
257
- declare function resolveLLMConfig(opts?: {
258
- model?: string;
259
- apiKey?: string;
260
- provider?: string;
261
- light?: boolean;
262
- }): LLMConfig;
263
-
264
- interface ModelOption {
265
- provider: Provider;
266
- alias: ModelName;
267
- modelId: string;
268
- /** The provider's default — what you get with no model name at all. */
269
- default: boolean;
270
- }
271
- /**
272
- * Every alias in the MODELS table, in PROVIDERS order. Pure and keyless,
273
- * which is what a UI picker or a server-config endpoint needs.
274
- */
275
- declare function listModels(): ModelOption[];
276
- interface ParsedModelName {
277
- /** Set only when the name carries an explicit "<provider>/" prefix. */
278
- provider?: Provider;
279
- /** The name with that prefix stripped. Aliases are NOT resolved here.
280
- * undefined for an empty name (or a bare "<provider>/"). */
281
- modelId?: string;
282
- }
283
- /** Pure syntax: strip an explicit provider prefix, nothing else. */
284
- declare function parseModelName(name?: string): ParsedModelName;
285
- interface ModelRef {
286
- provider: Provider;
287
- /** Concrete id as the provider knows it (alias resolved, prefix stripped). */
288
- modelId: string;
289
- /** Canonical "<provider>/<modelId>" — unambiguous, itself a valid model name. */
290
- name: string;
291
- }
292
- /**
293
- * Resolve a model name to its provider + concrete id WITHOUT an API key.
294
- * Follows getModel's rules — explicit `provider` wins, then an explicit
295
- * prefix, then getProviderForModel's inference; aliases map through MODELS;
296
- * no name → the provider's default — so what this reports is what
297
- * resolveModel / getModel will build. (One deliberate extra: an alias after
298
- * a prefix, "anthropic/sonnet", resolves too.)
299
- *
300
- * Rejects the one input getModel silently gets wrong: a slashed name whose
301
- * first segment is NOT a provider ("moonshotai/kimi-k2.6") with nothing
302
- * else naming the provider — inference falls to the default and the request
303
- * 404s at the wrong API. Say so up front, with the fix. LLM_PROVIDER, when
304
- * set, counts as "something naming the provider", exactly as it does for
305
- * getProviderForModel.
306
- */
307
- declare function canonicalModelName(model?: string, provider?: Provider | string): ModelRef;
308
- /**
309
- * Per-call output-token cap. An explicit MAX_OUTPUT_TOKENS env always wins.
310
- * Otherwise the default is provider-aware: Anthropic runs at 128k because
311
- * @ai-sdk/anthropic clamps known models down to their true per-model max
312
- * instead of erroring, while OpenAI-compatible hosts (OpenRouter et al)
313
- * reject max_tokens above the model limit rather than clamping, so they keep
314
- * the conservative 64k default. Thinking tokens count against this same
315
- * budget, so headroom matters more than the visible text length suggests.
316
- */
317
- declare function maxOutputTokensFor(provider?: string): number;
318
- interface ResolveOptions {
319
- /** alias | id | "provider/id" | "openrouter/org/id". Omit for the provider's default. */
320
- model?: string;
321
- /** Explicit provider; otherwise inferred from `model`. */
322
- provider?: Provider | string;
323
- apiKey?: string;
324
- /**
325
- * Async key source consulted by env-var NAME ("OPENAI_API_KEY", …) before
326
- * process.env — a host's own secret store. Only called when `apiKey` is
327
- * absent.
328
- */
329
- getSecret?: (envName: string) => Promise<string | undefined>;
330
- baseUrl?: string;
331
- headers?: Record<string, string>;
332
- abortSignal?: AbortSignal;
333
- timeoutMs?: number;
334
- }
335
- interface ResolvedModel extends ModelRef {
336
- model: LanguageModel;
337
- apiKey: string;
338
- contextLimit: number;
339
- maxOutputTokens: number;
340
- }
341
- /**
342
- * Name → everything a caller needs to run: provider, concrete id, canonical
343
- * name, the API key that was used, the LanguageModel, its context window,
344
- * and the output-token cap. Key precedence: `apiKey` → `getSecret(envName)`
345
- * → process.env; none → an error that names the env var. Logs nothing.
346
- */
347
- declare function resolveModel(opts?: ResolveOptions): Promise<ResolvedModel>;
348
-
349
- interface AiUsage {
350
- input: number;
351
- cache_read: number;
352
- cache_write: number;
353
- output: number;
354
- total: number;
355
- }
356
- interface AiUsageWithLegacy extends AiUsage {
357
- inputTokens: number;
358
- outputTokens: number;
359
- totalTokens: number;
360
- }
361
- type RawUsage = {
362
- input?: number;
363
- cache_read?: number;
364
- cache_write?: number;
365
- output?: number;
366
- total?: number;
367
- inputTokens?: number;
368
- inputTokenDetails?: {
369
- noCacheTokens?: number;
370
- cacheReadTokens?: number;
371
- cacheWriteTokens?: number;
372
- };
373
- cachedInputTokens?: number;
374
- outputTokens?: number;
375
- totalTokens?: number;
376
- };
377
- declare function emptyUsage(): AiUsage;
378
- declare function withLegacyUsage(usage: AiUsage): AiUsageWithLegacy;
379
- declare function normalizeUsage(raw?: RawUsage | null): AiUsageWithLegacy;
380
- /**
381
- * Fold provider-metadata cache info into a normalized usage. The v6-alpha
382
- * OpenRouter provider drops inputTokensDetails from the SDK usage object
383
- * (cached-token counts survive only in providerMetadata.openrouter.usage),
384
- * so without this every OpenRouter step reports cache_read 0 regardless of
385
- * whether the upstream host actually cached.
386
- */
387
- declare function withProviderCacheUsage(usage: AiUsageWithLegacy, providerMetadata?: Record<string, any> | null): AiUsageWithLegacy;
388
- declare function addUsage(...usages: Array<AiUsage | undefined | null>): AiUsage;
389
-
390
- interface CallModelOptions {
391
- provider: Provider;
392
- apiKey: string;
393
- messages: ModelMessage[];
394
- tools?: ToolSet;
395
- parser?: (fullResponse: string) => void;
396
- thinkingSpeed?: ThinkingSpeed;
397
- cwd?: string;
398
- executablePath?: string;
399
- modelName?: ModelName;
400
- }
401
- declare function callModel(opts: CallModelOptions): Promise<{
402
- text: string;
403
- usage: AiUsageWithLegacy;
404
- }>;
405
- interface GenerateObjectArgs {
406
- provider: Provider;
407
- apiKey: string;
408
- prompt: string;
409
- schema: any;
410
- }
411
- declare function callGenerateObject(args: GenerateObjectArgs): Promise<{
412
- object: any;
413
- usage: AiUsageWithLegacy;
414
- }>;
415
- interface GenerateTextArgs {
416
- provider: Provider;
417
- apiKey: string;
418
- prompt: string;
419
- thinkingSpeed?: ThinkingSpeed;
420
- }
421
- declare function callGenerateText(args: GenerateTextArgs): Promise<{
422
- text: string;
423
- usage: AiUsageWithLegacy;
424
- }>;
425
-
426
- declare const SYSTEM = "You are an expert dev. You are given a codebase and a task. You need to write the code for the task. You can also suggest changes to the codebase if needed.\n\nAvoid assuming that certain functions are available in the codebase. Don't use to_timestamp or to_date in SQL if you don't see examples of them being used. DO NOT make up functions like a logger or other utils. Only use utility functions that you ABSOLUTELY know exist in the code.\n\nTry to write as simple and straightforward code as possible. Make a real implementation, do NOT do a mockup or add sample data. Assume there is already mock data in the database. If you see snippets of backend code, that means you have access to the backend codebase and can add new backend endpoints or other functionality as needed.\n\nYou will be provided with code snippets to edit or create. Please preserve the EXACT file paths when you make edits. Do not truncate the file paths.\n\nYou may see multiple code snippets from the same file! In that case, please organize the code properly: if you need to add an import statement, do it in the snippet that has other imports in it! Since you won't always be able to see the whole file, try to be careful and avoid adding new code just above a snippet, since you might not know exactly what other code is there.\n\nAlways remember to add correct code that will compile!!! Make sure to properly add function signatures if needed, such as to Go interfaces or Rust traits (if you see those in the code snippets).\n\nIf asked to make further changes after already writing code, use the <content> blocks from your previous edits in order to identify code snippets to replace.\n";
427
- declare const OUTRO = "\nPlease write all the necessary code to fully implement the feature end-to-end. Do not make mock data or example placeholders!!!\n";
428
-
429
- type ProviderTool = "webSearch" | "webFetch" | "bash";
430
- /**
431
- * Provider-native tool by name.
432
- *
433
- * `webSearch` and `webFetch` are special: only Anthropic has native
434
- * ones, so every other provider gets a shim of the same name and result
435
- * shape instead of an exception — Exa-backed search from `./search.js`,
436
- * a guarded HTTP GET from `./fetch.js`. That keeps both tools available
437
- * (as `web_search` / `web_fetch`) on any model. This entry point returns
438
- * the bare tool — for the result bookkeeping, citation indices and
439
- * prompt snippet, call `createWebSearch` / `createWebFetch` directly.
440
- *
441
- * Returns `undefined` for those two when the chosen backend has no key
442
- * configured; callers should drop the tool rather than fail the request.
443
- * Other tools still throw for unsupported providers.
444
- */
445
- declare function getProviderTool(provider: Provider, apiKey: string, toolName: ProviderTool): any;
446
-
447
- /**
448
- * Web search, uniform across providers.
449
- *
450
- * Anthropic ships a server-executed `web_search` tool: the search loop
451
- * runs inside their API, results never round-trip through us, and the
452
- * model emits `<cite index="N-M">` tags referencing a flat, 1-based list
453
- * of every result returned across the whole turn. It's the best option
454
- * when it's available — no extra model hop, no per-search bill on us.
455
- *
456
- * Nobody else has an equivalent we can drop in. OpenAI, Google,
457
- * OpenRouter and xAI each expose *some* server-side search, but every
458
- * one has a different tool name, a different result shape, and a
459
- * different citation mechanism (Google's sources arrive as
460
- * `groundingMetadata`, OpenRouter's as message `annotations` — neither
461
- * is a tool-result at all). Adapting four of those into one pipeline is
462
- * four adapters and four citation normalizers.
463
- *
464
- * So: keep Anthropic on its native tool, and give every other provider a
465
- * client-executed tool of the same name, backed by Exa, that returns the
466
- * same shape. Consumers see one `web_search` tool, one result type, one
467
- * citation convention, regardless of which model is driving.
468
- *
469
- * Usage:
470
- *
471
- * const ws = createWebSearch({ provider, apiKey });
472
- * const tools = { ...(ws.tool ? { [WEB_SEARCH_TOOL_NAME]: ws.tool } : {}) };
473
- * const system = basePrompt + ws.promptSnippet;
474
- * // in onStepFinish: ws.capture(step.content)
475
- * // when writing up: linkifyCitations(markdown, ws.results)
476
- *
477
- * `ws.results` ends the run holding every result in citation order on
478
- * both paths, so downstream code never branches on the backend.
479
- */
480
- /** Tool name registered with the model. Load-bearing: consumers key UI
481
- * and step-walking off this exact string. */
482
- declare const WEB_SEARCH_TOOL_NAME = "web_search";
483
- /** Which implementation backs the tool. */
484
- type SearchBackend = "anthropic" | "exa";
485
- /**
486
- * One search hit. Field-compatible with Anthropic's
487
- * `web_search_result` output (`url` / `title` / `pageAge` / `type`), so
488
- * code that walks Anthropic tool-results parses Exa results unchanged.
489
- */
490
- interface WebSearchResult {
491
- url: string;
492
- title: string | null;
493
- /** Publish date when the backend reports one. */
494
- pageAge: string | null;
495
- /**
496
- * Extracted page text. Exa path only — Anthropic returns
497
- * `encryptedContent` that only their model can read, so on the native
498
- * path the text is never visible to us (or to this process).
499
- */
500
- text?: string;
501
- /**
502
- * 1-based citation index, flat across every `web_search` call in the
503
- * run — the number the model is told to cite. Exa path only; on the
504
- * Anthropic path the model derives indices itself.
505
- */
506
- index?: number;
507
- type: "web_search_result";
508
- }
509
- interface WebSearchOptions {
510
- /** Max `web_search` calls per run. Default 3, matching the previous
511
- * Anthropic-only default. Enforced in-process on the Exa path. */
512
- maxUses?: number;
513
- /** Results per call on the Exa path. Default 5. */
514
- numResults?: number;
515
- /** Per-result text budget on the Exa path. Default 4000. Raise for
516
- * research writeups, lower for quick factual lookups. */
517
- maxCharacters?: number;
518
- allowedDomains?: string[];
519
- blockedDomains?: string[];
520
- }
521
- interface CreateWebSearchOptions extends WebSearchOptions {
522
- /** LLM provider driving the run — decides the backend. */
523
- provider: Provider;
524
- /** LLM API key. Only used on the Anthropic path. Falls back to env. */
525
- apiKey?: string;
526
- /** Exa key. Falls back to `EXA_API_KEY`. */
527
- searchApiKey?: string;
528
- /**
529
- * Force a backend regardless of provider. `"exa"` is how you A/B the
530
- * shim against Anthropic's native tool on identical prompts.
531
- */
532
- backend?: SearchBackend;
533
- /**
534
- * Ask the model to cite sources as `<cite index="N">` tags, so
535
- * `formatOutput` can turn them into markdown links. Default `false`:
536
- * no citation instructions go out, and any tag the model emits anyway
537
- * is stripped to plain prose.
538
- *
539
- * Only turn this on for surfaces that actually render source links
540
- * (a research writeup). Claude honors the instruction unreliably —
541
- * measured 0/3 runs against claude-sonnet-5, which writes bare
542
- * parentheticals like `(Bitcoin Magazine)` instead — so a chat reply
543
- * asking for citations tends to get prose with no links either way.
544
- * See `npm run cite-rate`.
545
- */
546
- citations?: boolean;
547
- abortSignal?: AbortSignal;
548
- }
549
- interface WebSearchHandle {
550
- /** Register under {@link WEB_SEARCH_TOOL_NAME}. `undefined` when no
551
- * key is configured for the chosen backend — drop the tool rather
552
- * than failing the request. */
553
- tool: any | undefined;
554
- backend: SearchBackend | undefined;
555
- /** True when the model runs the search server-side (Anthropic). */
556
- native: boolean;
557
- /** Every result from the run, in citation order. */
558
- results: WebSearchResult[];
559
- /**
560
- * Feed each step's content here (AI SDK `onStepFinish`). Walks
561
- * Anthropic tool-results into `results`; a no-op on the Exa path,
562
- * where `execute` already appended them. Safe to call either way.
563
- */
564
- capture(stepContent: unknown): void;
565
- /** Citation instructions to append to the system prompt. Empty unless
566
- * `citations: true` was requested. */
567
- promptSnippet: string;
568
- /**
569
- * Run the model's final text through the right citation treatment for
570
- * this handle: markdown links when `citations` is on, plain prose when
571
- * it isn't. Either way no raw `<cite>` markup survives — a leftover
572
- * tag renders as literal text in any GFM viewer.
573
- */
574
- formatOutput(markdown: string): {
575
- content: string;
576
- converted: number;
577
- skipped: number;
578
- };
579
- }
580
- declare function getSearchApiKey(): string | undefined;
581
- declare function hasSearchApiKey(): boolean;
582
- /**
583
- * Which backend a provider gets. Anthropic keeps its native tool;
584
- * everything else falls to Exa.
585
- */
586
- declare function resolveSearchBackend(provider: Provider): SearchBackend;
587
- /**
588
- * Raw Exa search. Exposed for callers that want results without an LLM
589
- * in the loop (a research pre-fetch, a URL enrichment pass).
590
- *
591
- * Throws on transport/HTTP failure — {@link createWebSearch} catches and
592
- * hands the model a readable error instead of failing the whole turn.
593
- */
594
- declare function searchWeb(query: string, opts?: WebSearchOptions & {
595
- apiKey?: string;
596
- abortSignal?: AbortSignal;
597
- }): Promise<WebSearchResult[]>;
598
- /**
599
- * Build the `web_search` tool for a run, plus the citation bookkeeping
600
- * that goes with it. See the module header for the usage shape.
601
- */
602
- declare function createWebSearch(opts: CreateWebSearchOptions): WebSearchHandle;
603
- /**
604
- * Walk one AI SDK step's content for `web_search` tool-results and
605
- * append each hit to `target`, in order.
606
- *
607
- * Order is load-bearing: Anthropic's `<cite index="N-M">` tags index
608
- * this flat list 1-based across the entire turn.
609
- *
610
- * Tolerates both result shapes (`output` and `result`) and skips any
611
- * non-array body — adapters vary across AI SDK versions, and a shape we
612
- * don't recognize should cost us citations, not the run.
613
- */
614
- declare function captureNativeResults(stepContent: unknown, target: WebSearchResult[]): void;
615
- /**
616
- * Remove citation markup, leaving readable prose.
617
- *
618
- * The default treatment when `citations` is off. Models emit `<cite`
619
- * tags unprompted often enough that we can't just hope: Claude produces
620
- * them spontaneously with server-side search, and a raw tag renders as
621
- * literal text in any GFM viewer, which looks broken.
622
- *
623
- * Three shapes, in order:
624
- * 1. Empty markers — `a claim. <cite index="4"></cite>` — are
625
- * trailing footnotes with nothing to say. They take their leading
626
- * whitespace with them, so the sentence closes up cleanly.
627
- * 2. Anchored tags collapse to their anchor text, which is the words
628
- * the model was sourcing and reads as normal prose.
629
- * 3. Any orphan `<cite ...>` or `</cite>` left by a truncated stream
630
- * is swept, so no half-tag survives a cut-off response.
631
- */
632
- declare function stripCitations(markdown: string): string;
633
- /**
634
- * Replace `<cite index="N">anchor</cite>` tags with markdown links into
635
- * `results`. Handles Anthropic's multi-part form (`index="2-1"`,
636
- * `index="2-1,3-4"`) by keying off the leading number, which is the
637
- * flat result index.
638
- *
639
- * Out-of-range or unresolvable indices collapse to the bare anchor
640
- * text. That span loses its link, but no raw `<cite>` markup survives
641
- * into the output — persisted markdown is usually rendered as plain
642
- * GFM, where a leftover tag shows up as literal text.
643
- *
644
- * Models regularly ignore the "anchor text is required" instruction and
645
- * emit a trailing `<cite index="4"></cite>` marker instead (grok-4 does
646
- * it consistently). An empty anchor would linkify to `[](url)` — a
647
- * broken, invisible link — so it falls back to a `[N]` footnote marker.
648
- */
649
- declare function linkifyCitations(markdown: string, results: WebSearchResult[]): {
650
- content: string;
651
- converted: number;
652
- skipped: number;
653
- };
654
-
655
- /**
656
- * Web fetch, uniform across providers. Sibling of `./search.js`.
657
- *
658
- * Anthropic ships a server-executed `web_fetch` tool: given a URL, their
659
- * API retrieves the page (HTML or PDF), turns it into text and hands it
660
- * to the model with no round-trip through us. Same reasons that make it
661
- * the best option when available: no extra hop, nothing to host,
662
- * nothing to secure.
663
- *
664
- * Nobody else has one. So, as with search: keep Anthropic native, and
665
- * give every other provider a client-executed tool of the same name and
666
- * result shape, backed by a plain HTTP GET plus an HTML-to-text pass.
667
- *
668
- * Two things differ from search that consumers should know:
669
- *
670
- * 1. On the Anthropic path the model can only fetch URLs that already
671
- * appeared in the conversation — pasted by the user, or returned by
672
- * an earlier `web_search` / `web_fetch`. It refuses URLs it made
673
- * up. The HTTP path has no such memory and fetches whatever the
674
- * model asks for; what it WON'T do is reach anything private.
675
- *
676
- * 2. The HTTP path runs in *our* process, so the model can point it at
677
- * our network. Every URL — and every redirect hop — is checked
678
- * before connecting: http(s) only, no credentials, and the host
679
- * must resolve exclusively to public unicast addresses (loopback,
680
- * RFC 1918, link-local incl. cloud metadata, CGNAT, ULA and the
681
- * v4-in-v6 forms are all refused). There is a DNS-rebinding window
682
- * between our lookup and the socket's own; pin `allowedDomains` if
683
- * the deployment cares.
684
- *
685
- * Usage:
686
- *
687
- * const wf = createWebFetch({ provider, apiKey });
688
- * const tools = { ...(wf.tool ? { [WEB_FETCH_TOOL_NAME]: wf.tool } : {}) };
689
- * // in onStepFinish: wf.capture(step.content)
690
- * // afterwards: wf.results — every page fetched, in order
691
- */
692
- /** Tool name registered with the model. Matches Anthropic's native name
693
- * so consumers key UI and step-walking off one string on both paths. */
694
- declare const WEB_FETCH_TOOL_NAME = "web_fetch";
695
- /** Which implementation backs the tool. */
696
- type FetchBackend = "anthropic" | "http";
697
- /**
698
- * One fetched page. Same fields on both paths; the notes say where the
699
- * backends differ in what they put in them.
700
- */
701
- interface WebFetchResult {
702
- /** Where the content came from. The final URL after redirects on the
703
- * HTTP path; the URL Anthropic reports on the native path. */
704
- url: string;
705
- title: string | null;
706
- /**
707
- * Extracted text. Absent for a PDF on the Anthropic path — their API
708
- * returns it base64-encoded, which is only useful to their model.
709
- */
710
- text?: string;
711
- /** Original `content-type` on the HTTP path; Anthropic's normalized
712
- * `text/plain` / `application/pdf` on the native path. */
713
- mediaType: string | null;
714
- retrievedAt: string | null;
715
- /** True when the HTTP path cut the text at `maxCharacters`. */
716
- truncated?: boolean;
717
- type: "web_fetch_result";
718
- }
719
- interface WebFetchOptions {
720
- /** Max `web_fetch` calls per run. Default 5. Enforced in-process on
721
- * the HTTP path, passed as `max_uses` to Anthropic. */
722
- maxUses?: number;
723
- /**
724
- * Text budget per page on the HTTP path, in characters. Default
725
- * 40000 — about 10k tokens. When only `maxContentTokens` is given,
726
- * derived from it at 4 chars/token so one option covers both paths.
727
- */
728
- maxCharacters?: number;
729
- /**
730
- * Content budget per page on the Anthropic path, in tokens (their
731
- * `max_content_tokens`). Unset means Anthropic's own default. When
732
- * only `maxCharacters` is given, derived from it.
733
- */
734
- maxContentTokens?: number;
735
- /** Only fetch from these domains. Subdomains are included, so
736
- * `example.com` admits `docs.example.com`. */
737
- allowedDomains?: string[];
738
- blockedDomains?: string[];
739
- }
740
- /** Resolve a hostname to every address it answers with. */
741
- type HostLookup = (hostname: string) => Promise<string[]>;
742
- interface CreateWebFetchOptions extends WebFetchOptions {
743
- /** LLM provider driving the run — decides the backend. */
744
- provider: Provider;
745
- /** LLM API key. Only used on the Anthropic path. Falls back to env. */
746
- apiKey?: string;
747
- /** Force a backend regardless of provider. `"http"` is how you A/B
748
- * the shim against Anthropic's native tool on identical prompts. */
749
- backend?: FetchBackend;
750
- abortSignal?: AbortSignal;
751
- /**
752
- * Override hostname resolution on the HTTP path. Tests inject a stub
753
- * here; a deployment with its own resolver policy can too. Must return
754
- * every address the host resolves to — the guard refuses a host if ANY
755
- * of them is private.
756
- */
757
- lookup?: HostLookup;
758
- }
759
- interface WebFetchHandle {
760
- /** Register under {@link WEB_FETCH_TOOL_NAME}. `undefined` only when
761
- * the Anthropic path has no API key — drop the tool rather than
762
- * failing the request. The HTTP path needs no key. */
763
- tool: any | undefined;
764
- backend: FetchBackend | undefined;
765
- /** True when the fetch runs server-side (Anthropic). */
766
- native: boolean;
767
- /** Every page fetched during the run, in order. */
768
- results: WebFetchResult[];
769
- /**
770
- * Feed each step's content here (AI SDK `onStepFinish`). Walks
771
- * Anthropic tool-results into `results`; a no-op on the HTTP path,
772
- * where `execute` already appended them. Safe to call either way.
773
- */
774
- capture(stepContent: unknown): void;
775
- }
776
- /**
777
- * Which backend a provider gets. Anthropic keeps its native tool;
778
- * everything else falls to the HTTP shim.
779
- */
780
- declare function resolveFetchBackend(provider: Provider): FetchBackend;
781
- /**
782
- * True for an IP literal (v4 or v6, brackets tolerated) the HTTP path
783
- * refuses to connect to. Anything that isn't an IP literal is `true`
784
- * too: an address we can't classify isn't one we connect to.
785
- */
786
- declare function isPrivateAddress(ip: string): boolean;
787
- /**
788
- * Validate a URL before the HTTP path connects to it. Throws a readable
789
- * error on any rejection — the model gets the message back as the
790
- * tool's `error` and can pick a different URL.
791
- *
792
- * A hostname is resolved and refused if ANY of its addresses is
793
- * private: a host that answers with a mix is misconfigured or hostile,
794
- * and we'd have no say in which address the socket picks.
795
- */
796
- declare function validateFetchUrl(raw: string, opts?: {
797
- allowedDomains?: string[];
798
- blockedDomains?: string[];
799
- lookup?: HostLookup;
800
- }): Promise<URL>;
801
- interface FetchUrlOptions {
802
- maxCharacters?: number;
803
- allowedDomains?: string[];
804
- blockedDomains?: string[];
805
- abortSignal?: AbortSignal;
806
- timeoutMs?: number;
807
- lookup?: HostLookup;
808
- }
809
- /**
810
- * Raw fetch-and-extract. Exposed for callers that want a page without
811
- * an LLM in the loop (a URL enrichment pass, a link preview).
812
- *
813
- * Throws on any rejection or failure — {@link createWebFetch} catches
814
- * and hands the model a readable error instead of failing the turn.
815
- */
816
- declare function fetchUrl(rawUrl: string, opts?: FetchUrlOptions): Promise<WebFetchResult>;
817
- /**
818
- * Dependency-free HTML to text. Good enough for a model to read a page;
819
- * not a renderer. Scripts, styles and SVG go away entirely; block-level
820
- * boundaries become line breaks; list items get a leading dash; the
821
- * rest of the markup is dropped and entities decoded. Whitespace is
822
- * collapsed except for line breaks, so `<pre>` keeps its lines but not
823
- * its indentation.
824
- */
825
- declare function htmlToText(html: string): {
826
- title: string | null;
827
- text: string;
828
- };
829
- /**
830
- * Build the `web_fetch` tool for a run. See the module header for the
831
- * usage shape.
832
- */
833
- declare function createWebFetch(opts: CreateWebFetchOptions): WebFetchHandle;
834
- /**
835
- * Walk one AI SDK step's content for `web_fetch` tool-results and
836
- * append each page to `target`, in order.
837
- *
838
- * Tolerates both result shapes (`output` and `result`) and both key
839
- * casings for the nested fields — adapters vary across AI SDK versions,
840
- * and a shape we don't recognize should cost us a bookkeeping entry,
841
- * not the run. Anthropic's error results (`web_fetch_tool_result_error`)
842
- * are skipped: there's no page to record.
843
- */
844
- declare function captureNativeFetchResults(stepContent: unknown, target: WebFetchResult[]): void;
845
-
846
- export { API_KEY_ENV, type AiUsage, type AiUsageWithLegacy, type Conversation, type ConversationData, ConversationStorage, type CreateWebFetchOptions, type CreateWebSearchOptions, DEFAULT_MODELS, type FetchBackend, type FetchUrlOptions, GATEWAY_PATHS, type GetModelOptions, type HostLookup, type LLMConfig, type Logger, MAX_CONVERSATIONS, MODELS, type ModelName, type ModelOption, type ModelRef, OUTRO, PROVIDERS, type ParsedModelName, type Provider, type ProviderTool, type ResolveOptions, type ResolvedModel, SYSTEM, type SearchBackend, type ThinkingSpeed, type TokenPricing, type TokenUsageForCost, WEB_FETCH_TOOL_NAME, WEB_SEARCH_TOOL_NAME, type WebFetchHandle, type WebFetchOptions, type WebFetchResult, type WebSearchHandle, type WebSearchOptions, type WebSearchResult, addUsage, callGenerateObject, callGenerateText, callModel, canonicalModelName, captureNativeFetchResults, captureNativeResults, computeSessionCost, consoleLogger, createWebFetch, createWebSearch, emptyUsage, fetchUrl, gatewayEnvForProviders, gatewayUrlFor, gatewayUrlForModel, getApiKeyForProvider, getContextLimit, getGatewayBaseURL, getLightModelForProvider, getModel, getModelDetails, getProviderForModel, getProviderOptions, getProviderTool, getSearchApiKey, getTokenPricing, hasApiKeyForProvider, hasSearchApiKey, htmlToText, isPrivateAddress, linkifyCitations, listModels, loadModelPricing, maxOutputTokensFor, normalizeApiKey, normalizeCallerBaseURL, normalizeUsage, parseModelName, resolveFetchBackend, resolveLLMConfig, resolveModel, resolveSearchBackend, searchWeb, stripCitations, validateFetchUrl, withLegacyUsage, withProviderCacheUsage };