@dudko.dev/agent-web 0.0.1

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,835 @@
1
+ import * as ai from 'ai';
2
+ import { LanguageModel, ToolSet, Tool, ModelMessage, generateText, streamText, tool } from 'ai';
3
+ import { z, ZodType } from 'zod';
4
+ import { IDBPDatabase } from 'idb';
5
+
6
+ interface StoredMessage {
7
+ role: 'system' | 'user' | 'assistant';
8
+ content: string;
9
+ ts?: number;
10
+ }
11
+ /** Persists an agent session's transcript. Swap in any backend. */
12
+ interface ContextStore {
13
+ load(sessionId: string): Promise<StoredMessage[]>;
14
+ append(sessionId: string, message: StoredMessage): Promise<void>;
15
+ replace(sessionId: string, messages: StoredMessage[]): Promise<void>;
16
+ clear(sessionId: string): Promise<void>;
17
+ }
18
+ /** Non-persistent fallback (tests, SSR, private mode). */
19
+ declare class MemoryStore implements ContextStore {
20
+ private readonly data;
21
+ load(sessionId: string): Promise<StoredMessage[]>;
22
+ append(sessionId: string, message: StoredMessage): Promise<void>;
23
+ replace(sessionId: string, messages: StoredMessage[]): Promise<void>;
24
+ clear(sessionId: string): Promise<void>;
25
+ }
26
+
27
+ /**
28
+ * Providers the browser registry can resolve from a spec. This is deliberately
29
+ * a browser-safe subset of the Node sibling's list: Amazon Bedrock (SigV4
30
+ * signing), Google Vertex (ADC via google-auth-library) and Cloudflare
31
+ * (Worker bindings / process.env) are Node-only and omitted; Azure folds into
32
+ * `openai-compatible`. `web-llm` is the local WebGPU pseudo-provider (no API
33
+ * key). Any other runtime — Chrome/Edge built-in AI, a custom LanguageModel —
34
+ * is passed to the agent directly and never touches this registry.
35
+ */
36
+ type ProviderType = 'openai' | 'anthropic' | 'google' | 'openai-compatible' | 'xai' | 'deepseek' | 'gateway' | 'web-llm';
37
+ /** A model described by provider + id, resolved through the registry (+ vault). */
38
+ interface ProviderModelSpec {
39
+ providerType: ProviderType;
40
+ /** Model id, e.g. "gpt-4o-mini", "claude-haiku-4-5", "Llama-3.2-3B-Instruct-q4f16_1-MLC". */
41
+ model: string;
42
+ /** Provider endpoint override. Required for `openai-compatible`; a proxy URL for the rest. */
43
+ baseURL?: string;
44
+ /** Id under which the API key is stored in the CredentialStore / vault. Preferred. */
45
+ credentialRef?: string;
46
+ /**
47
+ * Dev-only inline API key. Prefer `credentialRef` so the key lives encrypted
48
+ * in the vault, not on a long-lived config object. Using this logs a warning.
49
+ */
50
+ apiKey?: string;
51
+ /** Extra request headers merged into the provider factory (e.g. a proxy auth header). */
52
+ headers?: Record<string, string>;
53
+ /** Escape hatch spread into the SDK factory after baseURL / apiKey / headers. */
54
+ providerOptions?: Record<string, unknown>;
55
+ }
56
+ /**
57
+ * A per-stage override that inherits the base provider + credentials and
58
+ * changes only the fields it sets (typically just `model`). Distinguished from
59
+ * a full ProviderModelSpec by the absence of `providerType`.
60
+ */
61
+ interface StageOverride {
62
+ model?: string;
63
+ baseURL?: string;
64
+ credentialRef?: string;
65
+ apiKey?: string;
66
+ headers?: Record<string, string>;
67
+ providerOptions?: Record<string, unknown>;
68
+ }
69
+ /** What a caller hands us for a model: a ready AI SDK model, or a spec to resolve. */
70
+ type ModelInput = LanguageModel | ProviderModelSpec;
71
+ /** A model input, plus the partial-override form usable for per-stage layering. */
72
+ type StageInput = ModelInput | StageOverride;
73
+ /** Outcome of resolving one stage: either a ready model, or a fully-resolved spec. */
74
+ type ResolvedStage = {
75
+ type: 'direct';
76
+ model: LanguageModel;
77
+ } | {
78
+ type: 'spec';
79
+ spec: ProviderModelSpec;
80
+ };
81
+ /** True for a ready AI SDK model: a gateway model-id string or a LanguageModelV* object. */
82
+ declare const isDirectModel: (m: unknown) => m is LanguageModel;
83
+ /** True for a full provider spec (carries the `providerType` discriminator). */
84
+ declare const isProviderSpec: (m: unknown) => m is ProviderModelSpec;
85
+
86
+ /**
87
+ * Default phase prompts. Each builder returns `{ system, prompt }` strings.
88
+ * Because the agent runs in two tool-modes, the builders switch on `ctx.mode`:
89
+ *
90
+ * - `native` — the model calls tools / emits schema-constrained JSON via the
91
+ * AI SDK, so the prompts describe the task and let the SDK enforce structure.
92
+ * - `prompted` — weak local models can't do that reliably, so the prompts spell
93
+ * out an exact JSON shape and (for the executor) include a tool catalogue;
94
+ * the output is salvaged by parse.ts.
95
+ *
96
+ * Override any builder via `BrowserAgentConfig.prompts`.
97
+ */
98
+ type ToolCallMode = 'native' | 'prompted';
99
+ interface PromptParts {
100
+ system: string;
101
+ prompt: string;
102
+ }
103
+ interface PlannerPromptContext {
104
+ goal: string;
105
+ state?: string;
106
+ toolCatalog: string;
107
+ mode: ToolCallMode;
108
+ }
109
+ interface ExecutorPromptContext {
110
+ goal: string;
111
+ state?: string;
112
+ step: string;
113
+ index: number;
114
+ total: number;
115
+ toolCatalog: string;
116
+ done: string[];
117
+ mode: ToolCallMode;
118
+ }
119
+ interface ReplannerPromptContext {
120
+ goal: string;
121
+ state?: string;
122
+ done: string[];
123
+ remaining: string[];
124
+ mode: ToolCallMode;
125
+ }
126
+ interface SynthesizerPromptContext {
127
+ goal: string;
128
+ state?: string;
129
+ done: string[];
130
+ }
131
+ interface Prompts {
132
+ planner(ctx: PlannerPromptContext): PromptParts;
133
+ executor(ctx: ExecutorPromptContext): PromptParts;
134
+ replanner(ctx: ReplannerPromptContext): PromptParts;
135
+ synthesizer(ctx: SynthesizerPromptContext): PromptParts;
136
+ }
137
+ declare const defaultPrompts: Prompts;
138
+ /** Prepend a host-supplied systemPrompt to a phase system prompt, if present. */
139
+ declare const withSystem: (base: string, extra?: string) => string;
140
+
141
+ interface VaultOptions {
142
+ /** Database name, forwarded to the shared IDB owner (default 'agent-web'). */
143
+ dbName?: string;
144
+ }
145
+ /**
146
+ * Encrypted key/value secret store backed by IndexedDB. Values are encrypted at
147
+ * rest with the non-extractable AES-GCM vault key (see crypto.ts).
148
+ *
149
+ * ── THREAT MODEL (read before shipping any app-owned key to a browser) ──
150
+ * This vault PROTECTS secrets **at rest** — an IndexedDB / disk dump yields
151
+ * only ciphertext — and against **casual DevTools inspection** — no plaintext
152
+ * key is stored, and the AES key cannot be exported even by in-page script.
153
+ *
154
+ * It does **NOT** defend against **active XSS on your origin**: any script that
155
+ * already runs on the page can call `getSecret()`, or use the live `CryptoKey`
156
+ * to decrypt. Browser storage is not a secrets manager.
157
+ *
158
+ * Therefore this vault is for the **end user's OWN key on their OWN device**
159
+ * (bring-your-own-key). A shared, app-owned key must NEVER reach the client —
160
+ * put it behind a server proxy (`ProviderModelSpec.baseURL`) or the Vercel AI
161
+ * Gateway instead.
162
+ */
163
+ declare class IndexedDBVault {
164
+ private readonly dbName?;
165
+ constructor(opts?: VaultOptions);
166
+ private key;
167
+ /** Encrypt and store a secret string under `id`. */
168
+ setSecret(id: string, value: string): Promise<void>;
169
+ /** Fetch and decrypt a secret, or undefined if absent. */
170
+ getSecret(id: string): Promise<string | undefined>;
171
+ deleteSecret(id: string): Promise<void>;
172
+ listSecretIds(): Promise<string[]>;
173
+ }
174
+
175
+ /**
176
+ * The seam the provider registry uses to fetch API keys at model-build time.
177
+ * Swap in any backend: the encrypted vault (default), an in-memory map (tests /
178
+ * SSR), or a `fetch` to your own short-lived-token endpoint.
179
+ */
180
+ interface CredentialStore {
181
+ getApiKey(ref: string): Promise<string | undefined>;
182
+ setApiKey(ref: string, key: string): Promise<void>;
183
+ deleteApiKey(ref: string): Promise<void>;
184
+ }
185
+ /** Default CredentialStore: encrypted, IndexedDB-backed (see IndexedDBVault). */
186
+ declare class VaultCredentialStore implements CredentialStore {
187
+ private readonly vault;
188
+ constructor(opts?: VaultOptions);
189
+ getApiKey(ref: string): Promise<string | undefined>;
190
+ setApiKey(ref: string, key: string): Promise<void>;
191
+ deleteApiKey(ref: string): Promise<void>;
192
+ }
193
+ /** Non-persistent CredentialStore for tests / SSR / ephemeral sessions. */
194
+ declare class MemoryCredentialStore implements CredentialStore {
195
+ private readonly map;
196
+ constructor(initial?: Record<string, string>);
197
+ getApiKey(ref: string): Promise<string | undefined>;
198
+ setApiKey(ref: string, key: string): Promise<void>;
199
+ deleteApiKey(ref: string): Promise<void>;
200
+ }
201
+
202
+ /**
203
+ * The canonical tool shape is the AI SDK `Tool` — the same object the native
204
+ * tool-calling path passes straight to `streamText({ tools })`, and the same
205
+ * shape MCP tools take. We add one optional field, `promptHint`, a short
206
+ * human-readable parameter hint (e.g. "{ text: string, x?: number }") used only
207
+ * by the prompted/salvage path when it renders the tool catalogue into a text
208
+ * prompt for weak local models.
209
+ */
210
+ type AgentTool = Tool & {
211
+ promptHint?: string;
212
+ };
213
+ /** A named collection of tools — a plain AI SDK ToolSet, usable directly. */
214
+ type AgentToolSet = ToolSet;
215
+ /** Read the optional promptHint attached by defineTool, if any. */
216
+ declare const promptHintOf: (tool: Tool) => string | undefined;
217
+
218
+ /** Force a tool-calling strategy, or let the agent pick per model ('auto'). */
219
+ type ToolMode = 'auto' | 'native' | 'prompted';
220
+ /**
221
+ * 'all' — the executor sees the full ToolSet on every step.
222
+ * 'plan-narrowed' — the executor sees only a step's suggestedTools (the planner
223
+ * must populate them). Empty means a reasoning-only step.
224
+ */
225
+ type ToolSelectionStrategy = 'all' | 'plan-narrowed';
226
+ /** Per-phase generation budgets (max output tokens). */
227
+ interface PhaseBudgets {
228
+ planner?: number;
229
+ executor?: number;
230
+ replanner?: number;
231
+ synthesizer?: number;
232
+ }
233
+ interface BrowserAgentConfig {
234
+ /** The default (executor) model: a ready AI SDK model, or a ProviderModelSpec to resolve. */
235
+ model: ModelInput;
236
+ /** Optional per-stage model overrides. A partial override inherits base provider/creds. */
237
+ planner?: StageInput;
238
+ synthesizer?: StageInput;
239
+ /** Fetches API keys for ProviderModelSpec models. Default: none (inline keys only). */
240
+ credentials?: CredentialStore;
241
+ /** Propagated to the openai-compatible provider name and the MCP client. */
242
+ clientName?: string;
243
+ /** Host tools the executor may call (an AI SDK ToolSet; use defineTool). */
244
+ tools?: AgentToolSet;
245
+ availableTools?: string[];
246
+ excludedTools?: string[];
247
+ /** Force native/prompted tool-calling; 'auto' picks per model (default 'auto'). */
248
+ toolMode?: ToolMode;
249
+ toolSelectionStrategy?: ToolSelectionStrategy;
250
+ /** Prepended to every phase's system prompt. */
251
+ systemPrompt?: string;
252
+ /** Override any phase prompt builder. */
253
+ prompts?: Partial<Prompts>;
254
+ /** Serialize the host's current world state into prompt context (grounding). */
255
+ describeState?: () => string | Promise<string>;
256
+ /** Persist the transcript (e.g. new IndexedDBStore()). */
257
+ memory?: ContextStore;
258
+ sessionId?: string;
259
+ /** Hard cap on executed steps incl. replans (default 8). */
260
+ maxIterations?: number;
261
+ /** Cap on LLM steps inside a single native executor call (default 4). */
262
+ maxStepsPerTask?: number;
263
+ /** Cap on replanner "revise" decisions per run (default 2). */
264
+ maxRevisions?: number;
265
+ /** Per-call timeout so a hang can't freeze a run (default 120000). */
266
+ chatTimeoutMs?: number;
267
+ budgets?: PhaseBudgets;
268
+ temperature?: number;
269
+ /** Replan after a blocked/failed step (default true). */
270
+ replan?: boolean;
271
+ /** Write a final natural-language summary (default true). */
272
+ synthesize?: boolean;
273
+ /** Compress stored history past this many chars (0 = off, default 12000). */
274
+ compressAfterChars?: number;
275
+ }
276
+ interface ResolvedConfig {
277
+ clientName: string;
278
+ sessionId: string;
279
+ toolMode: ToolMode;
280
+ toolSelectionStrategy: ToolSelectionStrategy;
281
+ maxIterations: number;
282
+ maxStepsPerTask: number;
283
+ maxRevisions: number;
284
+ chatTimeoutMs: number;
285
+ budgets: Required<PhaseBudgets>;
286
+ temperature: number | undefined;
287
+ replan: boolean;
288
+ synthesize: boolean;
289
+ compressAfterChars: number;
290
+ }
291
+ declare const resolveConfig: (c: BrowserAgentConfig) => ResolvedConfig;
292
+
293
+ /** A single step of a plan. Mirrors the Node sibling's IPlanStep. */
294
+ interface IPlanStep {
295
+ id: string;
296
+ description: string;
297
+ expectedOutcome?: string;
298
+ /** Tools the planner thinks this step needs (drives 'plan-narrowed' selection). */
299
+ suggestedTools?: string[];
300
+ /** Set by the pipeline when suggestedTools were named but all were unknown. */
301
+ requiresTools?: boolean;
302
+ }
303
+ interface IPlan {
304
+ thought: string;
305
+ steps: IPlanStep[];
306
+ }
307
+ interface IUsage {
308
+ inputTokens: number;
309
+ outputTokens: number;
310
+ totalTokens: number;
311
+ }
312
+ interface IToolCall {
313
+ name: string;
314
+ input: unknown;
315
+ output: unknown;
316
+ ok: boolean;
317
+ }
318
+ interface IStepResult {
319
+ step: IPlanStep;
320
+ summary: string;
321
+ toolCalls: IToolCall[];
322
+ /** True when the executor signalled it could not complete the step ([BLOCKER]). */
323
+ blocked: boolean;
324
+ }
325
+
326
+ type ReplanMode = 'continue' | 'revise' | 'finish';
327
+ type Phase = 'plan' | 'execute' | 'replan' | 'synthesize';
328
+ /**
329
+ * Everything the agent does is streamed as a typed event so any UI (or none)
330
+ * can render progress. The final answer arrives as a `final` event; there is no
331
+ * built-in rendering. This is a browser-focused subset of the Node sibling's
332
+ * event taxonomy (no persistence/otel events), plus `model.load` for WebLLM
333
+ * weight-download progress.
334
+ */
335
+ type AgentEvent = {
336
+ type: 'run.start';
337
+ goal: string;
338
+ } | {
339
+ type: 'model.load';
340
+ progress: number;
341
+ text: string;
342
+ } | {
343
+ type: 'plan.thought-delta';
344
+ delta: string;
345
+ } | {
346
+ type: 'plan.step-added';
347
+ step: IPlanStep;
348
+ index: number;
349
+ } | {
350
+ type: 'plan.created';
351
+ plan: IPlan;
352
+ } | {
353
+ type: 'plan.revised';
354
+ plan: IPlan;
355
+ reason: string;
356
+ } | {
357
+ type: 'step.start';
358
+ step: IPlanStep;
359
+ index: number;
360
+ total: number;
361
+ } | {
362
+ type: 'step.text-delta';
363
+ step: IPlanStep;
364
+ delta: string;
365
+ } | {
366
+ type: 'step.tool-call';
367
+ step: IPlanStep;
368
+ name: string;
369
+ input: unknown;
370
+ } | {
371
+ type: 'step.tool-result';
372
+ step: IPlanStep;
373
+ name: string;
374
+ output: unknown;
375
+ ok: boolean;
376
+ } | {
377
+ type: 'step.complete';
378
+ step: IPlanStep;
379
+ result: IStepResult;
380
+ } | {
381
+ type: 'replan.decision';
382
+ mode: ReplanMode;
383
+ reason: string;
384
+ } | {
385
+ type: 'final.text-delta';
386
+ delta: string;
387
+ } | {
388
+ type: 'final';
389
+ text: string;
390
+ } | {
391
+ type: 'usage';
392
+ phase: Phase;
393
+ usage: IUsage;
394
+ } | {
395
+ type: 'retry';
396
+ phase: Phase;
397
+ attempt: number;
398
+ error: string;
399
+ } | {
400
+ type: 'stopped';
401
+ } | {
402
+ type: 'error';
403
+ phase: Phase | 'run';
404
+ error: string;
405
+ };
406
+ type AgentEventHandler = (event: AgentEvent) => void;
407
+
408
+ interface RunOptions {
409
+ onEvent?: AgentEventHandler;
410
+ signal?: AbortSignal;
411
+ /** Override the config's sessionId for this run. */
412
+ sessionId?: string;
413
+ }
414
+ interface RunResult {
415
+ goal: string;
416
+ final: string;
417
+ plan: IPlan;
418
+ trace: IStepResult[];
419
+ steps: number;
420
+ /** Total successful tool calls across the run. */
421
+ applied: number;
422
+ stopped: boolean;
423
+ usage: IUsage;
424
+ }
425
+ interface Agent {
426
+ run(goal: string, opts?: RunOptions): Promise<RunResult>;
427
+ /** The resolved models, for hosts that want to reuse them (e.g. warm-up). */
428
+ readonly models: {
429
+ planner: LanguageModel;
430
+ executor: LanguageModel;
431
+ synthesizer: LanguageModel;
432
+ };
433
+ }
434
+ /**
435
+ * Create a headless plan → execute → replan → synthesize agent. Models are
436
+ * resolved eagerly (dynamic provider imports + vault key fetch), so this is
437
+ * async. Each `run` streams typed AgentEvents for any UI to render.
438
+ */
439
+ declare const createAgent: (config: BrowserAgentConfig) => Promise<Agent>;
440
+
441
+ /**
442
+ * Structured-output schemas for the native path (generateObject). Kept free of
443
+ * `.max()`/`.min()` constraints — some providers reject or mis-handle them in
444
+ * JSON-schema mode; we cap/clean in code instead (see parse.ts normalizeSteps).
445
+ */
446
+ declare const PlanStepSchema: z.ZodObject<{
447
+ description: z.ZodString;
448
+ expectedOutcome: z.ZodOptional<z.ZodString>;
449
+ suggestedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
450
+ }, z.core.$strip>;
451
+ declare const PlanSchema: z.ZodObject<{
452
+ thought: z.ZodString;
453
+ steps: z.ZodArray<z.ZodObject<{
454
+ description: z.ZodString;
455
+ expectedOutcome: z.ZodOptional<z.ZodString>;
456
+ suggestedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
457
+ }, z.core.$strip>>;
458
+ }, z.core.$strip>;
459
+ declare const ReplanSchema: z.ZodObject<{
460
+ decision: z.ZodEnum<{
461
+ continue: "continue";
462
+ revise: "revise";
463
+ finish: "finish";
464
+ }>;
465
+ reason: z.ZodString;
466
+ plan: z.ZodOptional<z.ZodArray<z.ZodString>>;
467
+ }, z.core.$strip>;
468
+ type PlanShape = z.infer<typeof PlanSchema>;
469
+ type ReplanShape = z.infer<typeof ReplanSchema>;
470
+
471
+ /** Everything the phase functions (planner/executor/replanner/synthesizer) share. */
472
+ interface AgentContext {
473
+ config: ResolvedConfig;
474
+ raw: BrowserAgentConfig;
475
+ plannerModel: LanguageModel;
476
+ executorModel: LanguageModel;
477
+ synthesizerModel: LanguageModel;
478
+ /** Tool-mode for the planner/replanner (from the planner model). */
479
+ plannerMode: ToolCallMode;
480
+ /** Tool-mode for the executor (from the executor model). */
481
+ executorMode: ToolCallMode;
482
+ tools: AgentToolSet;
483
+ toolCatalog: string;
484
+ prompts: Prompts;
485
+ emit: (event: AgentEvent) => void;
486
+ signal?: AbortSignal;
487
+ /** Current world state for grounding, or undefined when no describeState is configured. */
488
+ state: () => Promise<string | undefined>;
489
+ }
490
+
491
+ /**
492
+ * Build the initial plan. Native mode uses generateObject(PlanSchema); prompted
493
+ * mode salvages `{ reply, plan }` from plain text. Native failures (a weak model
494
+ * that can't satisfy the schema) fall back to the prompted parse rather than
495
+ * throwing, so the run degrades gracefully. An empty `steps` list signals the
496
+ * runner to answer directly (greeting / question / unclear).
497
+ */
498
+ declare const createPlan: (ctx: AgentContext, goal: string) => Promise<{
499
+ plan: IPlan;
500
+ usage: IUsage;
501
+ }>;
502
+
503
+ /**
504
+ * Split the executor's reply into a clean summary and a structural `blocked`
505
+ * flag. The [BLOCKER] sentinel — emitted by the executor when it can't complete
506
+ * a step — is language-agnostic and drives the replanner; we strip every copy
507
+ * so it never leaks into downstream prompts.
508
+ */
509
+ declare const splitBlocker: (raw: string) => {
510
+ summary: string;
511
+ blocked: boolean;
512
+ };
513
+ /** Execute one plan step: run the tool loop (native or prompted) and emit events. */
514
+ declare const executeStep: (ctx: AgentContext, goal: string, step: IPlanStep, index: number, total: number, done: string[]) => Promise<{
515
+ result: IStepResult;
516
+ usage: IUsage;
517
+ }>;
518
+ /** The replanner runs when a step was blocked or any of its tool calls failed. */
519
+ declare const shouldReplan: (result: IStepResult) => boolean;
520
+
521
+ /**
522
+ * Robust parsing of model output. Small in-browser models emit JSON wrapped in
523
+ * prose or code fences, truncate it mid-token when they loop, or drift to plain
524
+ * text. Every parser here is defensive: it salvages what it can and NEVER
525
+ * throws, and it never surfaces raw JSON as user-facing text. This is the
526
+ * backbone of the "prompted" tool-mode (see tools/mode.ts).
527
+ */
528
+ /** A tool the executor asked to run. */
529
+ interface RawAction {
530
+ tool: string;
531
+ args: Record<string, unknown>;
532
+ }
533
+ type ReplanDecision = 'continue' | 'revise' | 'finish';
534
+ /** True when a string is (or is dominated by) raw JSON — kept out of the UI. */
535
+ declare const looksLikeJson: (s: string) => boolean;
536
+ /** Trim, drop empties, de-duplicate (case-insensitive), cap length. */
537
+ declare const normalizeSteps: (arr: unknown, cap?: number) => string[];
538
+ interface PlannerResult {
539
+ reply: string;
540
+ plan: string[];
541
+ }
542
+ declare const parsePlannerResponse: (raw: string) => PlannerResult;
543
+ interface ExecutorResult {
544
+ reply: string;
545
+ actions: RawAction[];
546
+ }
547
+ declare const parseExecutorResponse: (raw: string) => ExecutorResult;
548
+ interface ReplannerResult {
549
+ decision: ReplanDecision;
550
+ reason: string;
551
+ plan: string[];
552
+ }
553
+ declare const parseReplannerResponse: (raw: string) => ReplannerResult;
554
+ /** Extracts a clean human sentence, never raw JSON. */
555
+ declare const parsePlainText: (raw: string) => string;
556
+
557
+ interface ReplanOutcome {
558
+ decision: ReplanDecision;
559
+ reason: string;
560
+ /** Replacement remaining steps (descriptions) when decision is 'revise'. */
561
+ plan: string[];
562
+ usage: IUsage;
563
+ }
564
+ /**
565
+ * Decide whether to continue, revise the remaining plan, or finish, after a
566
+ * blocked/failed step. Native mode uses generateObject(ReplanSchema); prompted
567
+ * mode salvages the decision from text. Any failure degrades to 'continue' so a
568
+ * flaky replanner never aborts the run.
569
+ */
570
+ declare const decideReplan: (ctx: AgentContext, goal: string, done: string[], remaining: string[]) => Promise<ReplanOutcome>;
571
+
572
+ /**
573
+ * Write the final natural-language summary of what the run accomplished. Plain
574
+ * text only (parsePlainText strips any stray JSON). Falls back to a default
575
+ * sentence if the model returns nothing usable.
576
+ */
577
+ declare const synthesizeAnswer: (ctx: AgentContext, goal: string, done: string[]) => Promise<{
578
+ text: string;
579
+ usage: IUsage;
580
+ }>;
581
+
582
+ /**
583
+ * Resolve one stage to either a ready model or a fully-resolved spec, layering
584
+ * an optional override on the base:
585
+ * - a direct model (or full spec) override wins outright;
586
+ * - a partial StageOverride inherits the base provider + credentials, changing
587
+ * only the fields it sets (e.g. `{ model: 'gpt-4o-mini' }`);
588
+ * - a full-spec override that switches provider MUST carry its own key
589
+ * (credentialRef/apiKey) — inheriting a key across vendors is a footgun.
590
+ */
591
+ declare const resolveStage: (base: ModelInput, override: StageInput | undefined, stageName: string) => ResolvedStage;
592
+ /**
593
+ * Turn a resolved stage into an AI SDK `LanguageModel`. Direct models pass
594
+ * through untouched; specs are built by dynamically importing the provider's
595
+ * optional-peer package and fetching the API key from the CredentialStore.
596
+ */
597
+ declare const buildModelFromStage: (resolved: ResolvedStage, credentials?: CredentialStore, clientName?: string) => Promise<LanguageModel>;
598
+ /** Resolve a single ModelInput straight to a LanguageModel (no per-stage layering). */
599
+ declare const resolveModel: (input: ModelInput, credentials?: CredentialStore, clientName?: string) => Promise<LanguageModel>;
600
+
601
+ /** WebGPU feature-detect — WebLLM requires it. Call before offering local models. */
602
+ declare const isWebGPUAvailable: () => boolean;
603
+ interface WebLLMModelOptions {
604
+ /**
605
+ * Progress callback for weight download / engine init. `report.progress` is
606
+ * 0..1. Forwarded to WebLLM's `initProgressCallback`.
607
+ */
608
+ initProgressCallback?: (report: {
609
+ progress: number;
610
+ text: string;
611
+ }) => void;
612
+ /** Any other @browser-ai/web-llm setting (temperature, worker handler, …). */
613
+ [k: string]: unknown;
614
+ }
615
+ /**
616
+ * Build an AI SDK `LanguageModel` backed by WebLLM (WebGPU) via the community
617
+ * provider `@browser-ai/web-llm` (an optional peer, dynamically imported so it
618
+ * is code-split and never loads until a local model is actually used). Accepts
619
+ * any WebLLM prebuilt model id, e.g. "Llama-3.2-3B-Instruct-q4f16_1-MLC".
620
+ */
621
+ declare const createWebLLMModel: (model: string, options?: WebLLMModelOptions) => Promise<LanguageModel>;
622
+
623
+ /** Whether native tool-calling is reliable enough to be the default for a provider. */
624
+ declare const supportsNativeTools: (p: ProviderType) => boolean;
625
+ /** Whether native structured output (generateObject) is the default for a provider. */
626
+ declare const supportsStructuredOutput: (p: ProviderType) => boolean;
627
+ /**
628
+ * Whether calling this provider straight from a browser origin with a BYOK key
629
+ * is expected to work (CORS + browser-access policy). Hosts can use this to
630
+ * warn users before a direct call fails.
631
+ *
632
+ * - `google`: Gemini's endpoint is CORS-enabled — the most reliable direct BYOK path.
633
+ * - `gateway` / `openai-compatible`: the host controls the endpoint / CORS.
634
+ * - `anthropic`: works, but only with the direct-browser-access header, which
635
+ * the registry injects automatically.
636
+ * - `openai`: api.openai.com does NOT reliably send CORS for browser calls —
637
+ * route through a proxy `baseURL` or the gateway.
638
+ * - `xai` / `deepseek`: unreliable from the browser; prefer a proxy.
639
+ */
640
+ declare const directBrowserOk: (p: ProviderType) => boolean;
641
+
642
+ /**
643
+ * Fetch (or lazily create) the vault's AES-GCM key. The key is generated
644
+ * NON-EXTRACTABLE and stored as a live `CryptoKey` in IndexedDB — a CryptoKey
645
+ * is structured-cloneable, so it persists across reloads WITHOUT its raw bytes
646
+ * ever being exposed to JavaScript. Not even in-page script can read it out; it
647
+ * can only be used to encrypt / decrypt. That is what makes at-rest storage of
648
+ * the secrets meaningful (see IndexedDBVault's threat model).
649
+ */
650
+ declare const getOrCreateVaultKey: (dbName?: string) => Promise<CryptoKey>;
651
+ interface EncryptedBlob {
652
+ /** 96-bit random IV, fresh per write. */
653
+ iv: Uint8Array;
654
+ ciphertext: ArrayBuffer;
655
+ }
656
+ /** Encrypt a JSON-serialisable value with a fresh 96-bit IV (AES-GCM). */
657
+ declare const encryptJSON: (key: CryptoKey, value: unknown) => Promise<EncryptedBlob>;
658
+ /** Decrypt a blob produced by encryptJSON. Throws if the key or blob don't match. */
659
+ declare const decryptJSON: <T = unknown>(key: CryptoKey, blob: EncryptedBlob) => Promise<T>;
660
+
661
+ interface AgentWebDBOptions {
662
+ /** Database name (default 'agent-web'). Use a distinct name to isolate apps. */
663
+ dbName?: string;
664
+ }
665
+ declare const KEYS_STORE = "keys";
666
+ declare const SECRETS_STORE = "secrets";
667
+ declare const SESSIONS_STORE = "sessions";
668
+ /**
669
+ * The single owner of the IndexedDB database. Every persistent store — the
670
+ * non-extractable crypto key, encrypted secrets, and session transcripts —
671
+ * lives under ONE database name + version. Centralising `openDB` here prevents
672
+ * the classic bug where two modules open the same DB name with different store
673
+ * sets and the second call throws a VersionError.
674
+ */
675
+ declare const openAgentWebDB: (opts?: AgentWebDBOptions) => Promise<IDBPDatabase>;
676
+
677
+ /** Options shared by the low-level generation helpers. Provide `prompt` OR `messages`. */
678
+ interface GenerateOptions {
679
+ system?: string;
680
+ prompt?: string;
681
+ messages?: ModelMessage[];
682
+ tools?: ToolSet;
683
+ maxOutputTokens?: number;
684
+ temperature?: number;
685
+ abortSignal?: AbortSignal;
686
+ /** Time-box the call; combined with `abortSignal` (0/undefined = no timeout). */
687
+ timeoutMs?: number;
688
+ }
689
+ /**
690
+ * One-shot text generation. Thin, provider-agnostic wrapper over the AI SDK's
691
+ * `generateText` — the same call works against a cloud model, a local WebLLM
692
+ * model, or Chrome/Edge built-in AI. Returns the full AI SDK result (`.text`,
693
+ * `.usage`, `.toolCalls`, `.steps`, …).
694
+ */
695
+ declare const generate: (model: LanguageModel, opts?: GenerateOptions) => ReturnType<typeof generateText>;
696
+ /** Streaming text generation. Returns the AI SDK `streamText` result (`.textStream`, `.fullStream`). */
697
+ declare const stream: (model: LanguageModel, opts?: GenerateOptions) => ReturnType<typeof streamText>;
698
+ /**
699
+ * Structured output constrained to a Zod schema, via the AI SDK's
700
+ * `generateObject`. Cloud models and WebLLM both support this; tiny local
701
+ * models can be unreliable — the agent's prompted mode is the fallback.
702
+ * Returns the AI SDK result (`.object`, `.usage`).
703
+ */
704
+ declare const generateStructured: <OBJECT>(model: LanguageModel, schema: ZodType<OBJECT>, opts?: GenerateOptions) => Promise<ai.GenerateObjectResult<(OBJECT extends string ? "enum" : "object") extends infer T ? T extends (OBJECT extends string ? "enum" : "object") ? T extends "array" ? OBJECT[] : OBJECT : never : never>>;
705
+
706
+ interface ToolLoopCallbacks {
707
+ onTextDelta?: (delta: string) => void;
708
+ onToolCall?: (name: string, input: unknown) => void;
709
+ onToolResult?: (name: string, output: unknown, ok: boolean) => void;
710
+ }
711
+ interface ToolLoopOptions {
712
+ /** 'native' = SDK function-calling; 'prompted' = parse JSON out of plain text. */
713
+ mode: 'native' | 'prompted';
714
+ system?: string;
715
+ prompt?: string;
716
+ messages?: ModelMessage[];
717
+ /** In native mode: the callable tools. In prompted mode: used to dispatch salvaged calls. */
718
+ tools: ToolSet;
719
+ /** Native mode: restrict callable tools to these names (plan-narrowed). */
720
+ activeTools?: string[];
721
+ /** Native mode: cap on internal tool-calling steps (default 4). */
722
+ maxSteps?: number;
723
+ maxOutputTokens?: number;
724
+ temperature?: number;
725
+ abortSignal?: AbortSignal;
726
+ timeoutMs?: number;
727
+ callbacks?: ToolLoopCallbacks;
728
+ }
729
+ interface ToolLoopResult {
730
+ text: string;
731
+ toolCalls: IToolCall[];
732
+ usage: IUsage;
733
+ }
734
+ /**
735
+ * Run one round of tool-calling in whichever mode the model needs, returning a
736
+ * normalised result. Native mode streams the SDK's multi-step function-calling;
737
+ * prompted mode does a single generation, salvages a `{ reply, actions }` JSON
738
+ * from the text, and dispatches each action through the tool's own execute.
739
+ * Both call the exact same tool implementations.
740
+ */
741
+ declare const runToolLoop: (model: LanguageModel, opts: ToolLoopOptions) => Promise<ToolLoopResult>;
742
+
743
+ /** Normalise the AI SDK usage shape (fields can be undefined) into IUsage. */
744
+ declare const normalizeUsage: (u?: {
745
+ inputTokens?: number;
746
+ outputTokens?: number;
747
+ totalTokens?: number;
748
+ }) => IUsage;
749
+
750
+ type ToolConfig = Parameters<typeof tool>[0];
751
+ /**
752
+ * Define a host tool. Thin wrapper over the AI SDK `tool()`: pass a
753
+ * `description`, an `inputSchema` (a Zod schema or the result of `jsonSchema()`),
754
+ * and an `execute` function. The returned tool works unchanged on the native
755
+ * tool-calling path.
756
+ *
757
+ * Optionally add `promptHint` — a short parameter hint like
758
+ * "{ text: string, x?: number }" — which the prompted/salvage path shows to
759
+ * weak local models that can't do native function-calling.
760
+ *
761
+ * @example
762
+ * const tools = {
763
+ * add_text: defineTool({
764
+ * description: 'Add a text block to the page.',
765
+ * inputSchema: z.object({ text: z.string(), x: z.number().optional() }),
766
+ * promptHint: '{ text: string, x?: number }',
767
+ * execute: async ({ text, x }) => addTextBlock(text, x),
768
+ * }),
769
+ * }
770
+ */
771
+ declare const defineTool: (config: ToolConfig & {
772
+ promptHint?: string;
773
+ }) => AgentTool;
774
+
775
+ /**
776
+ * Render a ToolSet into a plain-text catalogue for the prompted/salvage path:
777
+ * `- name(hint): description`. The hint comes from `defineTool({ promptHint })`
778
+ * when present; weak local models lean on it to fill args correctly.
779
+ */
780
+ declare const renderCatalog: (tools: AgentToolSet) => string;
781
+ interface DispatchContext {
782
+ abortSignal?: AbortSignal;
783
+ messages?: ModelMessage[];
784
+ toolCallId?: string;
785
+ }
786
+ /**
787
+ * Validate and execute a single salvaged tool call against the ToolSet, using
788
+ * the tool's OWN `execute` and `inputSchema` — the identical implementation the
789
+ * native path calls. Unknown tools, schema-invalid args, and thrown errors all
790
+ * become a failed IToolCall rather than throwing.
791
+ */
792
+ declare const dispatch: (action: RawAction, tools: ToolSet, ctx?: DispatchContext) => Promise<IToolCall>;
793
+
794
+ /**
795
+ * Decide the tool-calling strategy for a resolved model. An explicit
796
+ * config.toolMode of 'native'/'prompted' always wins; 'auto' (default) inspects
797
+ * the model's provider id — local/on-device runtimes → 'prompted', cloud → 'native'.
798
+ */
799
+ declare const selectToolMode: (model: LanguageModel, mode?: ToolMode) => "native" | "prompted";
800
+
801
+ interface IndexedDBStoreOptions {
802
+ /** Database name, forwarded to the shared IDB owner (default 'agent-web'). */
803
+ dbName?: string;
804
+ }
805
+ /**
806
+ * IndexedDB-backed transcript store — one record (message array) per session,
807
+ * in the shared `sessions` object store (see storage/db.ts). Also exposes
808
+ * session utilities: list and delete.
809
+ */
810
+ declare class IndexedDBStore implements ContextStore {
811
+ private readonly dbName?;
812
+ constructor(opts?: IndexedDBStoreOptions);
813
+ load(sessionId: string): Promise<StoredMessage[]>;
814
+ replace(sessionId: string, messages: StoredMessage[]): Promise<void>;
815
+ append(sessionId: string, message: StoredMessage): Promise<void>;
816
+ clear(sessionId: string): Promise<void>;
817
+ /** List all stored session ids. */
818
+ listSessions(): Promise<string[]>;
819
+ }
820
+
821
+ interface CompressOptions {
822
+ /** Compress once the transcript exceeds this many characters (0 = never). */
823
+ maxChars?: number;
824
+ /** How many most-recent messages to keep verbatim. */
825
+ keepRecent?: number;
826
+ }
827
+ /**
828
+ * When the transcript grows too long, summarise everything except the last
829
+ * `keepRecent` messages into a single system message (via the model), so the
830
+ * session stays within the model's context window. Returns the same array
831
+ * unchanged when no compression is needed; never throws.
832
+ */
833
+ declare const compressHistory: (messages: StoredMessage[], model: LanguageModel, opts?: CompressOptions) => Promise<StoredMessage[]>;
834
+
835
+ export { type Agent, type AgentContext, type AgentEvent, type AgentEventHandler, type AgentTool, type AgentToolSet, type AgentWebDBOptions, type BrowserAgentConfig, type CompressOptions, type ContextStore, type CredentialStore, type EncryptedBlob, type ExecutorPromptContext, type ExecutorResult, type GenerateOptions, type IPlan, type IPlanStep, type IStepResult, type IToolCall, type IUsage, IndexedDBStore, type IndexedDBStoreOptions, IndexedDBVault, KEYS_STORE, MemoryCredentialStore, MemoryStore, type ModelInput, type Phase, type PhaseBudgets, PlanSchema, type PlanShape, PlanStepSchema, type PlannerPromptContext, type PlannerResult, type PromptParts, type Prompts, type ProviderModelSpec, type ProviderType, type RawAction, type ReplanDecision, type ReplanMode, ReplanSchema, type ReplanShape, type ReplannerPromptContext, type ReplannerResult, type ResolvedConfig, type ResolvedStage, type RunOptions, type RunResult, SECRETS_STORE, SESSIONS_STORE, type StageInput, type StageOverride, type StoredMessage, type SynthesizerPromptContext, type ToolCallMode, type ToolLoopCallbacks, type ToolLoopOptions, type ToolLoopResult, type ToolMode, type ToolSelectionStrategy, VaultCredentialStore, type VaultOptions, type WebLLMModelOptions, buildModelFromStage, compressHistory, createAgent, createPlan, createWebLLMModel, decideReplan, decryptJSON, defaultPrompts, defineTool, directBrowserOk, dispatch, encryptJSON, executeStep, generate, generateStructured, getOrCreateVaultKey, isDirectModel, isProviderSpec, isWebGPUAvailable, looksLikeJson, normalizeSteps, normalizeUsage, openAgentWebDB, parseExecutorResponse, parsePlainText, parsePlannerResponse, parseReplannerResponse, promptHintOf, renderCatalog, resolveConfig, resolveModel, resolveStage, runToolLoop, selectToolMode, shouldReplan, splitBlocker, stream, supportsNativeTools, supportsStructuredOutput, synthesizeAnswer, withSystem };