@alexkroman1/aai 0.9.3 → 0.10.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.
Files changed (57) hide show
  1. package/dist/_internal-types.d.ts +49 -22
  2. package/dist/_internal-types.js +43 -1
  3. package/dist/_mock-ws.d.ts +1 -2
  4. package/dist/_run-code.d.ts +31 -0
  5. package/dist/_session-ctx.d.ts +73 -0
  6. package/dist/_session-otel.d.ts +43 -0
  7. package/dist/_session-persist.d.ts +30 -0
  8. package/dist/_ssrf.d.ts +30 -0
  9. package/dist/_ssrf.js +123 -0
  10. package/dist/_utils.d.ts +25 -0
  11. package/dist/_utils.js +54 -1
  12. package/dist/builtin-tools.d.ts +5 -34
  13. package/dist/direct-executor-Ca0wt5H0.js +572 -0
  14. package/dist/direct-executor.d.ts +34 -5
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +2 -2
  17. package/dist/kv.d.ts +30 -38
  18. package/dist/kv.js +19 -86
  19. package/dist/matchers.d.ts +20 -0
  20. package/dist/matchers.js +41 -0
  21. package/dist/memory-tools.d.ts +39 -0
  22. package/dist/middleware-core.d.ts +47 -0
  23. package/dist/middleware-core.js +107 -0
  24. package/dist/middleware.d.ts +37 -0
  25. package/dist/protocol.d.ts +44 -24
  26. package/dist/protocol.js +34 -14
  27. package/dist/runtime.d.ts +26 -2
  28. package/dist/runtime.js +44 -7
  29. package/dist/s2s.d.ts +19 -29
  30. package/dist/s2s.js +117 -87
  31. package/dist/server.d.ts +31 -3
  32. package/dist/server.js +102 -28
  33. package/dist/session-BkN9u0ni.js +683 -0
  34. package/dist/session.d.ts +55 -28
  35. package/dist/session.js +2 -312
  36. package/dist/sqlite-kv.d.ts +34 -0
  37. package/dist/sqlite-kv.js +133 -0
  38. package/dist/sqlite-vector.d.ts +58 -0
  39. package/dist/sqlite-vector.js +149 -0
  40. package/dist/system-prompt.d.ts +21 -0
  41. package/dist/telemetry.d.ts +49 -0
  42. package/dist/telemetry.js +95 -0
  43. package/dist/testing-MRl3SXsI.js +519 -0
  44. package/dist/testing.d.ts +299 -0
  45. package/dist/testing.js +2 -0
  46. package/dist/types.d.ts +324 -39
  47. package/dist/types.js +62 -9
  48. package/dist/vector.d.ts +18 -22
  49. package/dist/vector.js +41 -48
  50. package/dist/worker-entry.d.ts +11 -3
  51. package/dist/worker-entry.js +19 -8
  52. package/dist/ws-handler.d.ts +7 -3
  53. package/dist/ws-handler.js +64 -12
  54. package/package.json +55 -8
  55. package/dist/_mock-ws.js +0 -158
  56. package/dist/builtin-tools.js +0 -270
  57. package/dist/direct-executor.js +0 -125
package/dist/types.d.ts CHANGED
@@ -5,12 +5,152 @@ import { z } from "zod";
5
5
  import type { Kv } from "./kv.ts";
6
6
  import type { VectorStore } from "./vector.ts";
7
7
  /**
8
- * Result of the {@link AgentOptions.onBeforeStep} hook.
8
+ * Result returned by a `beforeTurn` middleware to block a turn.
9
9
  * @public
10
10
  */
11
- export type BeforeStepResult = {
12
- activeTools?: string[];
11
+ export type MiddlewareBlockResult = {
12
+ block: true;
13
+ reason: string;
14
+ };
15
+ /**
16
+ * Result returned by a `beforeToolCall` middleware hook to short-circuit tool execution.
17
+ *
18
+ * Return `{ result: string }` to skip execution and use a cached/synthetic result.
19
+ * Return `{ block: true; reason: string }` to deny the tool call.
20
+ * Return `{ args: Record<string, unknown> }` to transform the arguments.
21
+ * Return `undefined` to proceed normally.
22
+ *
23
+ * @public
24
+ */
25
+ export type ToolCallInterceptResult = {
26
+ result: string;
27
+ } | {
28
+ block: true;
29
+ reason: string;
30
+ } | {
31
+ args: Record<string, unknown>;
13
32
  } | undefined;
33
+ /**
34
+ * Composable middleware for the agent lifecycle.
35
+ *
36
+ * Middleware can intercept turns, tool calls, and output at well-defined
37
+ * points. Multiple middleware compose in array order: the first middleware
38
+ * in the array runs first for "before" hooks and last for "after" hooks.
39
+ *
40
+ * ## Middleware Composition Order
41
+ *
42
+ * **"Before" hooks** run in array order (first middleware → last):
43
+ * `beforeInput` → `beforeTurn` → `beforeToolCall` → `beforeOutput`
44
+ *
45
+ * **"After" hooks** run in reverse array order (last middleware → first):
46
+ * `afterToolCall` → `afterTurn`
47
+ *
48
+ * ### Execution flow for a user turn
49
+ *
50
+ * ```
51
+ * User text arrives
52
+ * │
53
+ * ▼
54
+ * beforeInput — transform/redact input text (piped through each middleware)
55
+ * │
56
+ * ▼
57
+ * beforeTurn — block or allow the turn (short-circuits on first block)
58
+ * │
59
+ * ▼ (if not blocked)
60
+ * LLM reasoning + tool calls
61
+ * │ ┌─ beforeToolCall (per call, short-circuits on block/result)
62
+ * │ └─ afterToolCall (per call, reverse order)
63
+ * │
64
+ * ▼
65
+ * beforeOutput — transform/redact LLM output text (piped through each middleware)
66
+ * │
67
+ * ▼
68
+ * afterTurn — cleanup/logging (reverse order)
69
+ * ```
70
+ *
71
+ * ### Short-circuit behavior
72
+ *
73
+ * - **`beforeTurn`**: If *any* middleware returns `{ block: true, reason }`,
74
+ * the turn is blocked and subsequent `beforeTurn` hooks do **not** run.
75
+ * When a turn is blocked, `afterTurn` hooks do **not** fire (the turn
76
+ * never started).
77
+ * - **`beforeToolCall`**: If *any* middleware blocks or returns a cached
78
+ * result, subsequent `beforeToolCall` hooks do **not** run. Arg transforms
79
+ * accumulate across middleware.
80
+ * - **`beforeInput`** / **`beforeOutput`**: These *always* run all middleware
81
+ * in sequence (no short-circuit). Each receives the output of the previous.
82
+ *
83
+ * ### Ordering example
84
+ *
85
+ * ```ts
86
+ * middleware: [auditTrail, hipaaGuardrails]
87
+ * ```
88
+ * - `beforeInput`: auditTrail runs first, then hipaaGuardrails
89
+ * - `beforeTurn`: auditTrail runs first; if it blocks, hipaaGuardrails is skipped
90
+ * - `afterTurn`: hipaaGuardrails runs first (reverse), then auditTrail
91
+ *
92
+ * If you want the guardrail to run *before* the audit trail logs, place it
93
+ * first in the array: `[hipaaGuardrails, auditTrail]`.
94
+ *
95
+ * @typeParam S - The shape of per-session state. Defaults to `any` so that
96
+ * reusable, state-agnostic middleware (e.g. loggers, rate-limiters) can be
97
+ * authored without threading a state generic. Use an explicit generic
98
+ * (`Middleware<MyState>`) when the middleware reads or writes session state.
99
+ *
100
+ * @public
101
+ */
102
+ export type Middleware<S = any> = {
103
+ /** Human-readable name for logging and debugging. */
104
+ name: string;
105
+ /**
106
+ * Filters user input text before it reaches the LLM. Return the
107
+ * (possibly modified) text. Runs on every user transcript, before
108
+ * `beforeTurn`.
109
+ *
110
+ * Use this to redact PII (SSNs, emails, patient IDs) from speech
111
+ * transcripts so the LLM never sees sensitive data.
112
+ *
113
+ * Middleware is piped in array order: the output of one filter becomes
114
+ * the input of the next.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * beforeInput: (text) =>
119
+ * text.replace(/\b\d{3}[-.]?\d{2}[-.]?\d{4}\b/g, "[SSN REDACTED]")
120
+ * ```
121
+ */
122
+ beforeInput?: (text: string, ctx: HookContext<S>) => string | Promise<string>;
123
+ /**
124
+ * Runs before each user turn. Can block the turn by returning
125
+ * `{ block: true, reason: "..." }`. Return `undefined` to proceed.
126
+ *
127
+ * Receives the text *after* `beforeInput` filtering has been applied.
128
+ *
129
+ * When a turn is blocked, subsequent `beforeTurn` middleware is skipped,
130
+ * and `afterTurn` hooks do **not** fire.
131
+ */
132
+ beforeTurn?: (text: string, ctx: HookContext<S>) => MiddlewareBlockResult | void | undefined | Promise<MiddlewareBlockResult | void | undefined>;
133
+ /**
134
+ * Runs after each user turn completes (after all steps finish).
135
+ * Runs in reverse array order.
136
+ */
137
+ afterTurn?: (text: string, ctx: HookContext<S>) => void | Promise<void>;
138
+ /**
139
+ * Runs before each tool call. Can approve, deny, transform args, or
140
+ * return a cached result.
141
+ */
142
+ beforeToolCall?: (toolName: string, args: Readonly<Record<string, unknown>>, ctx: HookContext<S>) => ToolCallInterceptResult | undefined | Promise<ToolCallInterceptResult | undefined>;
143
+ /**
144
+ * Runs after each tool call completes. Useful for caching results,
145
+ * logging, or analytics. Runs in reverse array order.
146
+ */
147
+ afterToolCall?: (toolName: string, args: Readonly<Record<string, unknown>>, result: string, ctx: HookContext<S>) => void | Promise<void>;
148
+ /**
149
+ * Filters agent text output before it is sent to TTS. Return the
150
+ * (possibly modified) text. Runs on every agent transcript chunk.
151
+ */
152
+ beforeOutput?: (text: string, ctx: HookContext<S>) => string | Promise<string>;
153
+ };
14
154
  /**
15
155
  * Identifier for a built-in server-side tool.
16
156
  *
@@ -18,7 +158,7 @@ export type BeforeStepResult = {
18
158
  * and provide capabilities like web search, code execution, and API access.
19
159
  *
20
160
  * - `"web_search"` — Search the web for current information, facts, or news.
21
- * - `"visit_webpage"` — Fetch a URL and return its content as Markdown.
161
+ * - `"visit_webpage"` — Fetch a URL and return its content as clean text.
22
162
  * - `"fetch_json"` — Call a REST API endpoint and return the JSON response.
23
163
  * - `"run_code"` — Execute JavaScript in a sandbox for calculations and data processing.
24
164
  * - `"vector_search"` — Search the agent's RAG knowledge base for relevant documents.
@@ -34,6 +174,8 @@ export type BuiltinTool = "web_search" | "visit_webpage" | "fetch_json" | "run_c
34
174
  * - `"required"` — The model must call at least one tool.
35
175
  * - `"none"` — Tool calling is disabled.
36
176
  * - `{ type: "tool"; toolName: string }` — Force a specific tool.
177
+ *
178
+ * @public
37
179
  */
38
180
  export type ToolChoice = "auto" | "required" | "none" | {
39
181
  type: "tool";
@@ -90,6 +232,30 @@ export type ToolContext<S = Record<string, unknown>> = {
90
232
  vector: VectorStore;
91
233
  /** Read-only snapshot of conversation messages so far. */
92
234
  messages: readonly Message[];
235
+ /**
236
+ * Push an intermediate update to the client UI before the tool finishes.
237
+ *
238
+ * Use this to send progressive data so the UI can render partial results
239
+ * immediately (e.g. a loading card, preview, or streaming data) instead
240
+ * of waiting for the full tool result.
241
+ *
242
+ * The data is serialized to JSON and delivered as a `tool_call_update`
243
+ * event on the client. Use `useToolCallUpdate` in the UI to consume it.
244
+ *
245
+ * No-op in sandbox (platform) mode.
246
+ */
247
+ sendUpdate(data: unknown): void;
248
+ /**
249
+ * SSRF-safe fetch function.
250
+ *
251
+ * In self-hosted mode this calls the network directly (with SSRF protection).
252
+ * In platform mode this is proxied through the sidecar so it works inside
253
+ * the sandbox. Always use `ctx.fetch` instead of the global `fetch` in tool
254
+ * code to ensure portability across both deployment modes.
255
+ */
256
+ fetch: typeof globalThis.fetch;
257
+ /** Unique identifier for the current session. Useful for correlating logs across concurrent sessions. */
258
+ sessionId: string;
93
259
  };
94
260
  /**
95
261
  * Context passed to lifecycle hooks (`onConnect`, `onTurn`, etc.).
@@ -102,7 +268,7 @@ export type ToolContext<S = Record<string, unknown>> = {
102
268
  *
103
269
  * @public
104
270
  */
105
- export type HookContext<S = Record<string, unknown>> = Omit<ToolContext<S>, "messages">;
271
+ export type HookContext<S = Record<string, unknown>> = Omit<ToolContext<S>, "messages" | "sendUpdate">;
106
272
  /**
107
273
  * Definition of a custom tool that the agent can invoke.
108
274
  *
@@ -139,7 +305,7 @@ export type ToolDef<P extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawS
139
305
  /** Human-readable description shown to the LLM. */
140
306
  description: string;
141
307
  /** Zod schema for the tool's parameters. */
142
- parameters?: P | undefined;
308
+ parameters?: P;
143
309
  /** Function that executes the tool and returns a result. */
144
310
  execute(args: z.infer<P>, ctx: ToolContext<S>): Promise<unknown> | unknown;
145
311
  };
@@ -148,18 +314,19 @@ export type ToolDef<P extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawS
148
314
  *
149
315
  * When tools are defined inline in `defineAgent({ tools: { ... } })`, the
150
316
  * generic `P` gets widened to the base `ZodObject` type, so `args` in
151
- * `execute` loses its specific shape. Wrapping a tool definition in `tool()`
152
- * lets TypeScript infer `P` from `parameters` and type `args` correctly.
317
+ * `execute` loses its specific shape. Wrapping a tool definition in
318
+ * `defineTool()` lets TypeScript infer `P` from `parameters` and type
319
+ * `args` correctly.
153
320
  *
154
321
  * @example
155
322
  * ```ts
156
- * import { defineAgent, tool } from "aai";
323
+ * import { defineAgent, defineTool } from "aai";
157
324
  * import { z } from "zod";
158
325
  *
159
326
  * export default defineAgent({
160
327
  * name: "my-agent",
161
328
  * tools: {
162
- * greet: tool({
329
+ * greet: defineTool({
163
330
  * description: "Greet the user",
164
331
  * parameters: z.object({ name: z.string() }),
165
332
  * execute: ({ name }) => `Hello, ${name}!`, // name is string
@@ -170,7 +337,82 @@ export type ToolDef<P extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawS
170
337
  *
171
338
  * @public
172
339
  */
173
- export declare function tool<P extends z.ZodObject<z.ZodRawShape>, S = Record<string, unknown>>(def: ToolDef<P, S>): ToolDef<P, S>;
340
+ export declare function defineTool<P extends z.ZodObject<z.ZodRawShape>, S = Record<string, unknown>>(def: ToolDef<P, S>): ToolDef<P, S>;
341
+ /** Alias for {@link defineTool}. Prefer `defineTool` for clarity. */
342
+ export { defineTool as tool };
343
+ /**
344
+ * Create a typed `defineTool` helper with the session state type baked in.
345
+ *
346
+ * When tools need access to typed session state, you'd normally have to write
347
+ * verbose generics on every `defineTool` call. `createToolFactory` eliminates
348
+ * that boilerplate by returning a `defineTool` variant that already knows `S`.
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * import { createToolFactory, defineAgent } from "aai";
353
+ * import { z } from "zod";
354
+ *
355
+ * interface PortfolioState { holdings: Map<string, number> }
356
+ *
357
+ * const tool = createToolFactory<PortfolioState>();
358
+ *
359
+ * export default defineAgent<PortfolioState>({
360
+ * name: "portfolio",
361
+ * state: () => ({ holdings: new Map() }),
362
+ * tools: {
363
+ * buy: tool({
364
+ * description: "Buy shares",
365
+ * parameters: z.object({ symbol: z.string(), qty: z.number() }),
366
+ * execute: (args, ctx) => {
367
+ * // args.symbol is string, ctx.state is PortfolioState
368
+ * ctx.state.holdings.set(args.symbol, args.qty);
369
+ * },
370
+ * }),
371
+ * },
372
+ * });
373
+ * ```
374
+ *
375
+ * @public
376
+ */
377
+ export declare function createToolFactory<S = Record<string, unknown>>(): <P extends z.ZodObject<z.ZodRawShape>>(def: ToolDef<P, S>) => ToolDef<P, S>;
378
+ /**
379
+ * A mapping of tool names to their result types.
380
+ *
381
+ * Define this in a shared file (e.g. `shared.ts`) that both `agent.ts` and
382
+ * `client.tsx` can import, so tool result types stay in sync without
383
+ * duplication.
384
+ *
385
+ * @example
386
+ * ```ts
387
+ * // shared.ts
388
+ * import type { ToolResultMap } from "@alexkroman1/aai";
389
+ *
390
+ * export interface Pizza {
391
+ * id: number;
392
+ * size: "small" | "medium" | "large";
393
+ * toppings: string[];
394
+ * }
395
+ *
396
+ * export type MyToolResults = ToolResultMap<{
397
+ * add_pizza: { added: Pizza; orderTotal: string };
398
+ * place_order: { orderNumber: number; total: string };
399
+ * }>;
400
+ * ```
401
+ *
402
+ * Then use with {@link @alexkroman1/aai-ui#useToolResult | useToolResult}:
403
+ *
404
+ * ```tsx
405
+ * // client.tsx
406
+ * import type { MyToolResults } from "./shared.ts";
407
+ *
408
+ * useToolResult<MyToolResults["add_pizza"]>("add_pizza", (result) => {
409
+ * console.log(result.added); // fully typed
410
+ * });
411
+ * ```
412
+ *
413
+ * @public
414
+ */
415
+ export type ToolResultMap<T extends Record<string, unknown> = Record<string, unknown>> = T;
174
416
  /**
175
417
  * Information about a completed agentic step, passed to the `onStep` hook.
176
418
  *
@@ -206,7 +448,6 @@ export type StepInfo = {
206
448
  * export default defineAgent({
207
449
  * name: "research-bot",
208
450
  * instructions: "You help users research topics.",
209
- * voice: "orion",
210
451
  * builtinTools: ["web_search"],
211
452
  * tools: {
212
453
  * summarize: {
@@ -240,17 +481,25 @@ export type AgentOptions<S = Record<string, unknown>> = {
240
481
  toolChoice?: ToolChoice;
241
482
  /** Built-in tools to enable (e.g. `"web_search"`, `"run_code"`). */
242
483
  builtinTools?: readonly BuiltinTool[];
243
- /**
244
- * Default set of active tools per turn.
245
- *
246
- * When set, only these tools are available to the LLM each turn.
247
- * Can be overridden dynamically per-turn via `onBeforeStep`.
248
- */
249
- activeTools?: readonly string[];
250
484
  /** Custom tools the agent can invoke. */
251
485
  tools?: Readonly<Record<string, ToolDef<z.ZodObject<z.ZodRawShape>, NoInfer<S>>>>;
252
486
  /** Factory that creates fresh per-session state. Called once per connection. */
253
487
  state?: () => S;
488
+ /**
489
+ * Enable automatic session state persistence across reconnects.
490
+ *
491
+ * When enabled, session state, conversation history, and the S2S session ID
492
+ * are saved to KV on disconnect and restored when the client reconnects with
493
+ * `?sessionId=<old-session-id>` in the WebSocket URL.
494
+ *
495
+ * - `true` — enable with default TTL (1 hour)
496
+ * - `{ ttl: number }` — enable with custom TTL in milliseconds
497
+ *
498
+ * Requires the agent's `state` return value to be JSON-serializable.
499
+ */
500
+ persistence?: boolean | {
501
+ ttl?: number;
502
+ };
254
503
  /** Called when a new session connects. */
255
504
  onConnect?: (ctx: HookContext<S>) => void | Promise<void>;
256
505
  /** Called when a session disconnects. */
@@ -262,12 +511,23 @@ export type AgentOptions<S = Record<string, unknown>> = {
262
511
  /** Called after each agentic step completes. */
263
512
  onStep?: (step: StepInfo, ctx: HookContext<S>) => void | Promise<void>;
264
513
  /**
265
- * Called before each step; can restrict which tools are active.
514
+ * Composable middleware that intercepts turns, tool calls, and output.
266
515
  *
267
- * Return `{ activeTools: [...] }` to limit available tools for the
268
- * upcoming step, or `void` to keep all tools active.
516
+ * Middleware runs in array order for "before" hooks (first to last)
517
+ * and reverse order for "after" hooks (last to first).
269
518
  */
270
- onBeforeStep?: (stepNumber: number, ctx: HookContext<S>) => BeforeStepResult | Promise<BeforeStepResult>;
519
+ middleware?: readonly Middleware<S>[];
520
+ /**
521
+ * Close the S2S connection after this many milliseconds of inactivity.
522
+ * Inactivity means no audio, transcripts, or tool calls in either direction.
523
+ * When the timeout fires the session is stopped and the client receives an
524
+ * `idle_timeout` event.
525
+ *
526
+ * Set to `0` or `Infinity` to disable.
527
+ *
528
+ * @defaultValue 300_000 (5 minutes)
529
+ */
530
+ idleTimeoutMs?: number;
271
531
  };
272
532
  /**
273
533
  * Default system prompt used when `instructions` is not provided.
@@ -279,30 +539,55 @@ export declare const DEFAULT_INSTRUCTIONS: string;
279
539
  /** Default greeting spoken when a session starts. */
280
540
  export declare const DEFAULT_GREETING: string;
281
541
  /**
282
- * Agent definition with all defaults applied, returned by
283
- * {@link defineAgent}.
542
+ * Agent definition returned by {@link defineAgent}.
543
+ *
544
+ * Core fields (`name`, `instructions`, `greeting`, `maxSteps`, `tools`)
545
+ * are resolved to their final values with defaults applied. Optional
546
+ * behavioral fields (hooks, middleware, `sttPrompt`, etc.) remain
547
+ * optional — `undefined` means "not configured."
284
548
  *
285
- * Unlike {@link AgentOptions}, every field here is resolved to its
286
- * final value — no optional fields with implicit defaults remain.
549
+ * @public
287
550
  */
288
- export type AgentDef = {
551
+ export type AgentDef<S = Record<string, unknown>> = {
289
552
  name: string;
290
553
  instructions: string;
291
554
  greeting: string;
292
555
  sttPrompt?: string;
293
- maxSteps: number | ((ctx: HookContext) => number);
556
+ maxSteps: number | ((ctx: HookContext<S>) => number);
294
557
  toolChoice?: ToolChoice;
295
558
  builtinTools?: readonly BuiltinTool[];
296
- activeTools?: readonly string[];
297
- tools: Readonly<Record<string, ToolDef>>;
298
- state?: () => Record<string, unknown>;
299
- onConnect?: AgentOptions["onConnect"];
300
- onDisconnect?: AgentOptions["onDisconnect"];
301
- onError?: AgentOptions["onError"];
302
- onTurn?: AgentOptions["onTurn"];
303
- onStep?: AgentOptions["onStep"];
304
- onBeforeStep?: AgentOptions["onBeforeStep"];
559
+ tools: Readonly<Record<string, ToolDef<z.ZodObject<z.ZodRawShape>, S>>>;
560
+ state?: () => S;
561
+ /** Resolved persistence config, or `undefined` if disabled. */
562
+ persistence?: {
563
+ ttl: number;
564
+ };
565
+ onConnect?: (ctx: HookContext<S>) => void | Promise<void>;
566
+ onDisconnect?: (ctx: HookContext<S>) => void | Promise<void>;
567
+ onError?: (error: Error, ctx?: HookContext<S>) => void;
568
+ onTurn?: (text: string, ctx: HookContext<S>) => void | Promise<void>;
569
+ onStep?: (step: StepInfo, ctx: HookContext<S>) => void | Promise<void>;
570
+ middleware?: readonly Middleware<S>[];
571
+ idleTimeoutMs?: number;
305
572
  };
573
+ /** @internal Zod schema for {@link BuiltinTool}. Exported for reuse in internal schemas. */
574
+ export declare const BuiltinToolSchema: z.ZodEnum<{
575
+ web_search: "web_search";
576
+ visit_webpage: "visit_webpage";
577
+ fetch_json: "fetch_json";
578
+ run_code: "run_code";
579
+ vector_search: "vector_search";
580
+ memory: "memory";
581
+ }>;
582
+ /** @internal Zod schema for {@link ToolChoice}. Exported for reuse in internal schemas. */
583
+ export declare const ToolChoiceSchema: z.ZodUnion<readonly [z.ZodEnum<{
584
+ auto: "auto";
585
+ required: "required";
586
+ none: "none";
587
+ }>, z.ZodObject<{
588
+ type: z.ZodLiteral<"tool">;
589
+ toolName: z.ZodString;
590
+ }, z.core.$strip>]>;
306
591
  /**
307
592
  * Create an agent definition from the given options, applying sensible defaults.
308
593
  *
@@ -333,4 +618,4 @@ export type AgentDef = {
333
618
  * });
334
619
  * ```
335
620
  */
336
- export declare function defineAgent<S>(options: AgentOptions<S>): AgentDef;
621
+ export declare function defineAgent<S = Record<string, unknown>>(options: AgentOptions<S>): AgentDef<S>;
package/dist/types.js CHANGED
@@ -8,18 +8,19 @@ import { z } from "zod";
8
8
  *
9
9
  * When tools are defined inline in `defineAgent({ tools: { ... } })`, the
10
10
  * generic `P` gets widened to the base `ZodObject` type, so `args` in
11
- * `execute` loses its specific shape. Wrapping a tool definition in `tool()`
12
- * lets TypeScript infer `P` from `parameters` and type `args` correctly.
11
+ * `execute` loses its specific shape. Wrapping a tool definition in
12
+ * `defineTool()` lets TypeScript infer `P` from `parameters` and type
13
+ * `args` correctly.
13
14
  *
14
15
  * @example
15
16
  * ```ts
16
- * import { defineAgent, tool } from "aai";
17
+ * import { defineAgent, defineTool } from "aai";
17
18
  * import { z } from "zod";
18
19
  *
19
20
  * export default defineAgent({
20
21
  * name: "my-agent",
21
22
  * tools: {
22
- * greet: tool({
23
+ * greet: defineTool({
23
24
  * description: "Greet the user",
24
25
  * parameters: z.object({ name: z.string() }),
25
26
  * execute: ({ name }) => `Hello, ${name}!`, // name is string
@@ -30,10 +31,47 @@ import { z } from "zod";
30
31
  *
31
32
  * @public
32
33
  */
33
- function tool(def) {
34
+ function defineTool(def) {
34
35
  return def;
35
36
  }
36
37
  /**
38
+ * Create a typed `defineTool` helper with the session state type baked in.
39
+ *
40
+ * When tools need access to typed session state, you'd normally have to write
41
+ * verbose generics on every `defineTool` call. `createToolFactory` eliminates
42
+ * that boilerplate by returning a `defineTool` variant that already knows `S`.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { createToolFactory, defineAgent } from "aai";
47
+ * import { z } from "zod";
48
+ *
49
+ * interface PortfolioState { holdings: Map<string, number> }
50
+ *
51
+ * const tool = createToolFactory<PortfolioState>();
52
+ *
53
+ * export default defineAgent<PortfolioState>({
54
+ * name: "portfolio",
55
+ * state: () => ({ holdings: new Map() }),
56
+ * tools: {
57
+ * buy: tool({
58
+ * description: "Buy shares",
59
+ * parameters: z.object({ symbol: z.string(), qty: z.number() }),
60
+ * execute: (args, ctx) => {
61
+ * // args.symbol is string, ctx.state is PortfolioState
62
+ * ctx.state.holdings.set(args.symbol, args.qty);
63
+ * },
64
+ * }),
65
+ * },
66
+ * });
67
+ * ```
68
+ *
69
+ * @public
70
+ */
71
+ function createToolFactory() {
72
+ return (def) => def;
73
+ }
74
+ /**
37
75
  * Default system prompt used when `instructions` is not provided.
38
76
  *
39
77
  * Optimized for voice-first interactions: short sentences, no visual
@@ -56,6 +94,7 @@ If you need to list items, say "First," "Next," and "Finally."
56
94
  - Never use exclamation points. Keep your tone calm and conversational.`;
57
95
  /** Default greeting spoken when a session starts. */
58
96
  const DEFAULT_GREETING = "Hey there. I'm a voice assistant. What can I help you with?";
97
+ /** @internal Zod schema for {@link BuiltinTool}. Exported for reuse in internal schemas. */
59
98
  const BuiltinToolSchema = z.enum([
60
99
  "web_search",
61
100
  "visit_webpage",
@@ -64,6 +103,7 @@ const BuiltinToolSchema = z.enum([
64
103
  "vector_search",
65
104
  "memory"
66
105
  ]);
106
+ /** @internal Zod schema for {@link ToolChoice}. Exported for reuse in internal schemas. */
67
107
  const ToolChoiceSchema = z.union([z.enum([
68
108
  "auto",
69
109
  "required",
@@ -77,6 +117,8 @@ const ToolDefSchema = z.object({
77
117
  parameters: z.custom((val) => val === void 0 || val instanceof z.ZodType, "Expected a Zod schema").optional(),
78
118
  execute: z.function()
79
119
  });
120
+ /** Default TTL for persisted session data: 1 hour. */
121
+ const DEFAULT_PERSIST_TTL = 36e5;
80
122
  const AgentOptionsSchema = z.object({
81
123
  name: z.string().min(1, "Agent name must be non-empty"),
82
124
  instructions: z.string().optional(),
@@ -85,15 +127,24 @@ const AgentOptionsSchema = z.object({
85
127
  maxSteps: z.union([z.number().int().positive(), z.function()]).optional(),
86
128
  toolChoice: ToolChoiceSchema.optional(),
87
129
  builtinTools: z.array(BuiltinToolSchema).optional(),
88
- activeTools: z.array(z.string().min(1)).optional(),
89
130
  tools: z.record(z.string(), ToolDefSchema).optional(),
90
131
  state: z.function().optional(),
132
+ persistence: z.union([z.literal(true), z.object({ ttl: z.number().int().positive().optional() })]).optional(),
91
133
  onConnect: z.function().optional(),
92
134
  onDisconnect: z.function().optional(),
93
135
  onError: z.function().optional(),
94
136
  onTurn: z.function().optional(),
95
137
  onStep: z.function().optional(),
96
- onBeforeStep: z.function().optional()
138
+ middleware: z.array(z.object({
139
+ name: z.string().min(1, "Middleware name must be non-empty"),
140
+ beforeInput: z.function().optional(),
141
+ beforeTurn: z.function().optional(),
142
+ afterTurn: z.function().optional(),
143
+ beforeToolCall: z.function().optional(),
144
+ afterToolCall: z.function().optional(),
145
+ beforeOutput: z.function().optional()
146
+ })).optional(),
147
+ idleTimeoutMs: z.number().nonnegative().optional()
97
148
  });
98
149
  /**
99
150
  * Create an agent definition from the given options, applying sensible defaults.
@@ -127,13 +178,15 @@ const AgentOptionsSchema = z.object({
127
178
  */
128
179
  function defineAgent(options) {
129
180
  AgentOptionsSchema.parse(options);
181
+ const persistence = options.persistence ? { ttl: typeof options.persistence === "object" ? options.persistence.ttl ?? DEFAULT_PERSIST_TTL : DEFAULT_PERSIST_TTL } : void 0;
130
182
  return {
131
183
  ...options,
132
184
  instructions: options.instructions ?? DEFAULT_INSTRUCTIONS,
133
185
  greeting: options.greeting ?? "Hey there. I'm a voice assistant. What can I help you with?",
134
186
  maxSteps: options.maxSteps ?? 5,
135
- tools: options.tools ?? {}
187
+ tools: options.tools ?? {},
188
+ persistence
136
189
  };
137
190
  }
138
191
  //#endregion
139
- export { DEFAULT_GREETING, DEFAULT_INSTRUCTIONS, defineAgent, tool };
192
+ export { BuiltinToolSchema, DEFAULT_GREETING, DEFAULT_INSTRUCTIONS, ToolChoiceSchema, createToolFactory, defineAgent, defineTool, defineTool as tool };
package/dist/vector.d.ts CHANGED
@@ -1,6 +1,20 @@
1
1
  /**
2
- * Vector store interface and in-memory implementation.
2
+ * Vector store interface.
3
3
  */
4
+ /**
5
+ * Validate a vector filter expression to prevent injection attacks.
6
+ *
7
+ * Rejects filters containing SQL keywords (SELECT, DROP, etc.),
8
+ * statement terminators, comments, and null bytes. Enforces a
9
+ * maximum length of 1000 characters.
10
+ *
11
+ * @param filter - The raw filter string from user input.
12
+ * @returns The validated filter string (trimmed).
13
+ * @throws Error if the filter contains dangerous patterns.
14
+ *
15
+ * @public
16
+ */
17
+ export declare function validateVectorFilter(filter: string): string;
4
18
  /**
5
19
  * A single vector search result entry.
6
20
  *
@@ -63,27 +77,9 @@ export type VectorStore = {
63
77
  filter?: string;
64
78
  }): Promise<VectorEntry[]>;
65
79
  /**
66
- * Remove entries by ID.
80
+ * Delete entries by ID.
67
81
  *
68
- * @param ids - A single ID or array of IDs to remove.
82
+ * @param ids - A single ID or array of IDs to delete.
69
83
  */
70
- remove(ids: string | string[]): Promise<void>;
84
+ delete(ids: string | string[]): Promise<void>;
71
85
  };
72
- /**
73
- * Create an in-memory vector store for testing and local development.
74
- *
75
- * Uses brute-force substring matching instead of real vector similarity.
76
- * Good enough for testing the plumbing but not for production use.
77
- *
78
- * @returns A {@link VectorStore} instance backed by in-memory storage.
79
- *
80
- * @example
81
- * ```ts
82
- * import { createMemoryVectorStore } from "aai";
83
- *
84
- * const vector = createMemoryVectorStore();
85
- * await vector.upsert("doc-1", "The capital of France is Paris.");
86
- * const results = await vector.query("France capital");
87
- * ```
88
- */
89
- export declare function createMemoryVectorStore(): VectorStore;