@alexkroman1/aai 1.6.1 → 1.7.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.
Files changed (77) hide show
  1. package/.turbo/turbo-build.log +22 -18
  2. package/CHANGELOG.md +10 -0
  3. package/dist/{_internal-types-DFL07G3f.js → _internal-types-CrnTi9Ew.js} +8 -2
  4. package/dist/host/memory-vector.d.ts +17 -0
  5. package/dist/host/pinecone-vector.d.ts +19 -0
  6. package/dist/host/providers/resolve-kv.d.ts +10 -0
  7. package/dist/host/providers/resolve-vector.d.ts +11 -0
  8. package/dist/host/providers/resolve.d.ts +6 -0
  9. package/dist/host/runtime-barrel.d.ts +5 -0
  10. package/dist/host/runtime-barrel.js +406 -93
  11. package/dist/host/runtime.d.ts +6 -0
  12. package/dist/host/server.d.ts +2 -0
  13. package/dist/host/tool-executor.d.ts +2 -0
  14. package/dist/pinecone-CeJ69aRs.js +19 -0
  15. package/dist/{cartesia-BfQPOQ7Y.js → rime-58p9mDR8.js} +19 -19
  16. package/dist/s3-BtCMvCod.js +37 -0
  17. package/dist/sdk/_internal-types.d.ts +11 -1
  18. package/dist/sdk/define.d.ts +5 -1
  19. package/dist/sdk/manifest-barrel.js +1 -1
  20. package/dist/sdk/manifest.d.ts +5 -1
  21. package/dist/sdk/protocol.d.ts +36 -0
  22. package/dist/sdk/protocol.js +26 -1
  23. package/dist/sdk/providers/kv/fs.d.ts +12 -0
  24. package/dist/sdk/providers/kv/memory.d.ts +9 -0
  25. package/dist/sdk/providers/kv/redis.d.ts +17 -0
  26. package/dist/sdk/providers/kv/s3.d.ts +25 -0
  27. package/dist/sdk/providers/kv-barrel.d.ts +13 -0
  28. package/dist/sdk/providers/kv-barrel.js +2 -0
  29. package/dist/sdk/providers/llm-barrel.js +1 -1
  30. package/dist/sdk/providers/stt-barrel.js +1 -1
  31. package/dist/sdk/providers/tts-barrel.js +1 -1
  32. package/dist/sdk/providers/vector/in-memory.d.ts +15 -0
  33. package/dist/sdk/providers/vector/pinecone.d.ts +19 -0
  34. package/dist/sdk/providers/vector-barrel.d.ts +11 -0
  35. package/dist/sdk/providers/vector-barrel.js +2 -0
  36. package/dist/sdk/providers.d.ts +4 -0
  37. package/dist/sdk/types.d.ts +8 -1
  38. package/dist/sdk/vector.d.ts +29 -0
  39. package/dist/{soniox-DCQ3GqJq.js → soniox-BQdL0mB5.js} +13 -13
  40. package/host/_runtime-conformance.ts +21 -0
  41. package/host/_test-utils.ts +1 -0
  42. package/host/memory-vector.test.ts +87 -0
  43. package/host/memory-vector.ts +108 -0
  44. package/host/pinecone-vector.test.ts +79 -0
  45. package/host/pinecone-vector.ts +79 -0
  46. package/host/providers/resolve-kv.test.ts +48 -0
  47. package/host/providers/resolve-kv.ts +128 -0
  48. package/host/providers/resolve-vector.test.ts +26 -0
  49. package/host/providers/resolve-vector.ts +42 -0
  50. package/host/providers/resolve.ts +26 -0
  51. package/host/runtime-barrel.ts +5 -0
  52. package/host/runtime-config.ts +1 -1
  53. package/host/runtime.ts +28 -2
  54. package/host/server.ts +57 -1
  55. package/host/tool-executor.ts +7 -1
  56. package/package.json +20 -1
  57. package/sdk/__snapshots__/exports.test.ts.snap +8 -0
  58. package/sdk/__snapshots__/schema-shapes.test.ts.snap +2 -0
  59. package/sdk/_internal-types.ts +8 -0
  60. package/sdk/define.ts +11 -1
  61. package/sdk/manifest.test.ts +20 -0
  62. package/sdk/manifest.ts +10 -0
  63. package/sdk/protocol-snapshot.test.ts +53 -0
  64. package/sdk/protocol.test.ts +54 -0
  65. package/sdk/protocol.ts +32 -0
  66. package/sdk/providers/kv/fs.ts +20 -0
  67. package/sdk/providers/kv/memory.ts +17 -0
  68. package/sdk/providers/kv/redis.ts +25 -0
  69. package/sdk/providers/kv/s3.ts +33 -0
  70. package/sdk/providers/kv-barrel.ts +19 -0
  71. package/sdk/providers/vector/in-memory.ts +23 -0
  72. package/sdk/providers/vector/pinecone.ts +27 -0
  73. package/sdk/providers/vector-barrel.ts +15 -0
  74. package/sdk/providers.ts +6 -0
  75. package/sdk/types.ts +14 -1
  76. package/sdk/vector.ts +32 -0
  77. /package/dist/{xai-jfQsxxPZ.js → xai-BDI61Y2M.js} +0 -0
@@ -1,16 +1,18 @@
1
1
  import { r as DEFAULT_SYSTEM_PROMPT } from "../types-KUgezM6u.js";
2
2
  import { _ as TOOL_EXECUTION_TIMEOUT_MS, a as DEFAULT_SHUTDOWN_TIMEOUT_MS, c as FETCH_TIMEOUT_MS, d as MAX_PAGE_CHARS, g as RUN_CODE_TIMEOUT_MS, h as PIPELINE_FLUSH_TIMEOUT_MS, l as MAX_HTML_BYTES, m as MAX_WS_PAYLOAD_BYTES, o as DEFAULT_STT_SAMPLE_RATE, p as MAX_VALUE_SIZE, s as DEFAULT_TTS_SAMPLE_RATE, t as AGENT_CSP } from "../constants-C2nirZUI.js";
3
3
  import { i as toolError, n as errorDetail, r as errorMessage, t as parseWsUpgradeParams } from "../ws-upgrade-BeOQ7fXL.js";
4
- import { ClientMessageSchema, buildReadyConfig, lenientParse } from "../sdk/protocol.js";
5
- import { a as toAgentConfig, c as makeSttError, i as agentToolsToSchemas, l as makeTtsError, n as EMPTY_PARAMS, s as assertProviderTriple } from "../_internal-types-DFL07G3f.js";
6
- import { a as ASSEMBLYAI_KIND, r as ELEVENLABS_KIND, s as DEEPGRAM_KIND, t as SONIOX_KIND } from "../soniox-DCQ3GqJq.js";
7
- import { a as RIME_KIND, n as CARTESIA_KIND } from "../cartesia-BfQPOQ7Y.js";
8
- import { a as MISTRAL_KIND, d as ANTHROPIC_KIND, l as GOOGLE_KIND, r as OPENAI_KIND, s as GROQ_KIND } from "../xai-jfQsxxPZ.js";
4
+ import { ClientMessageSchema, VectorRequestSchema, buildReadyConfig, lenientParse } from "../sdk/protocol.js";
5
+ import { a as toAgentConfig, c as makeSttError, i as agentToolsToSchemas, l as makeTtsError, n as EMPTY_PARAMS, s as assertProviderTriple } from "../_internal-types-CrnTi9Ew.js";
6
+ import { a as MISTRAL_KIND, d as ANTHROPIC_KIND, l as GOOGLE_KIND, r as OPENAI_KIND, s as GROQ_KIND } from "../xai-BDI61Y2M.js";
7
+ import { a as DEEPGRAM_KIND, r as ELEVENLABS_KIND, s as ASSEMBLYAI_KIND, t as SONIOX_KIND } from "../soniox-BQdL0mB5.js";
8
+ import { a as CARTESIA_KIND, n as RIME_KIND } from "../rime-58p9mDR8.js";
9
+ import { a as MEMORY_KV_KIND, r as REDIS_KV_KIND } from "../s3-BtCMvCod.js";
10
+ import { r as IN_MEMORY_VECTOR_KIND, t as PINECONE_VECTOR_KIND } from "../pinecone-CeJ69aRs.js";
11
+ import { createRequire } from "node:module";
9
12
  import { z } from "zod";
10
13
  import { convert } from "html-to-text";
11
14
  import vm from "node:vm";
12
- import pTimeout from "p-timeout";
13
- import { createStorage, prefixStorage } from "unstorage";
15
+ import { createHash, randomUUID } from "node:crypto";
14
16
  import { createAnthropic } from "@ai-sdk/anthropic";
15
17
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
16
18
  import { createGroq } from "@ai-sdk/groq";
@@ -22,9 +24,10 @@ import { createNanoEvents } from "nanoevents";
22
24
  import { DeepgramClient } from "@deepgram/sdk";
23
25
  import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
24
26
  import { AudioFormat, CommitStrategy, RealtimeEvents } from "@elevenlabs/elevenlabs-js/wrapper/realtime/index.js";
25
- import WsWebSocket, { WebSocketServer } from "ws";
26
- import { randomUUID } from "node:crypto";
27
+ import WebSocket, { WebSocketServer } from "ws";
27
28
  import { Cartesia } from "@cartesia/cartesia-js";
29
+ import { createStorage, prefixStorage } from "unstorage";
30
+ import pTimeout from "p-timeout";
28
31
  import { jsonSchema, stepCountIs, streamText, tool } from "ai";
29
32
  import fs from "node:fs";
30
33
  import http from "node:http";
@@ -356,35 +359,84 @@ function resolveAllBuiltins(names, opts) {
356
359
  };
357
360
  }
358
361
  //#endregion
359
- //#region sdk/system-prompt.ts
360
- function getFormattedDate() {
361
- return (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
362
- weekday: "long",
363
- year: "numeric",
364
- month: "long",
365
- day: "numeric"
366
- });
367
- }
368
- const VOICE_RULES = "\n\nCRITICAL OUTPUT RULES — you MUST follow these for EVERY response:\nYour response will be spoken aloud by a TTS system and displayed as plain text.\n- NEVER use markdown: no **, no *, no _, no #, no `, no [](), no ---\n- NEVER use bullet points (-, *, •) or numbered lists (1., 2.)\n- NEVER use code blocks or inline code\n- NEVER mention tools, search, APIs, or technical failures to the user. If a tool returns no results, just answer naturally without explaining why.\n- Write exactly as you would say it out loud to a friend\n- Use short conversational sentences. To list things, say \"First,\" \"Next,\" \"Finally,\"\n- Keep responses concise — 1 to 3 sentences max";
362
+ //#region host/memory-vector.ts
369
363
  /**
370
- * Build the system prompt sent to the LLM from the agent configuration.
364
+ * In-memory Vector implementation.
371
365
  *
372
- * Assembles the default system prompt, today's date, agent-specific instructions,
373
- * and optional sections for tool usage preamble and voice output rules.
374
- *
375
- * @param config - The serializable agent configuration (name, systemPrompt, etc.).
376
- * @param opts.hasTools - When `true`, appends a preamble instructing the LLM to
377
- * speak a brief phrase before each tool call to fill silence.
378
- * @param opts.voice - When `true`, appends strict voice-specific output rules
379
- * (no markdown, no bullet points, conversational tone, concise responses).
380
- * @returns The assembled system prompt string.
366
+ * INTENTIONALLY BAD QUALITY. Pseudo-embedding hashes the text into a
367
+ * 64-dim Float32Array of values in [-1, ~0.99], then L2-normalizes
368
+ * the result. Because both stored and probe vectors are unit-length,
369
+ * cosine similarity reduces to a plain dot product that's what
370
+ * `cosine()` computes. Used only for `aai dev` and tests the goal
371
+ * is proving tool wiring, not retrieval ranking.
381
372
  */
382
- function buildSystemPrompt(config, opts) {
383
- const { hasTools } = opts;
384
- const agentInstructions = config.systemPrompt && config.systemPrompt !== DEFAULT_SYSTEM_PROMPT ? `\n\nAgent-Specific Instructions:\n${config.systemPrompt}` : "";
385
- const toolPreamble = hasTools ? "\n\nWhen you decide to use a tool, ALWAYS say a brief natural phrase BEFORE the tool call (e.g. \"Let me look that up\" or \"One moment while I check\"). This fills silence while the tool executes. Keep preambles to one short sentence." : "";
386
- const guidance = opts.toolGuidance && opts.toolGuidance.length > 0 ? `\n\nBuilt-in Tool Usage:\n${opts.toolGuidance.join("\n")}` : "";
387
- return DEFAULT_SYSTEM_PROMPT + `\n\nToday's date is ${getFormattedDate()}.` + agentInstructions + toolPreamble + guidance + (opts.voice ? VOICE_RULES : "");
373
+ const stores = /* @__PURE__ */ new Map();
374
+ function getStore(ns) {
375
+ let store = stores.get(ns);
376
+ if (!store) {
377
+ store = /* @__PURE__ */ new Map();
378
+ stores.set(ns, store);
379
+ }
380
+ return store;
381
+ }
382
+ const DIM = 64;
383
+ function pseudoEmbed(text) {
384
+ const out = new Float32Array(DIM);
385
+ const h1 = createHash("sha256").update(text).digest();
386
+ const h2 = createHash("sha256").update(h1).digest();
387
+ for (let i = 0; i < 32; i++) out[i] = (h1[i] - 128) / 128;
388
+ for (let i = 0; i < 32; i++) out[i + 32] = (h2[i] - 128) / 128;
389
+ let norm = 0;
390
+ for (let i = 0; i < DIM; i++) norm += out[i] * out[i];
391
+ norm = Math.sqrt(norm) || 1;
392
+ for (let i = 0; i < DIM; i++) out[i] = out[i] / norm;
393
+ return out;
394
+ }
395
+ function cosine(a, b) {
396
+ let dot = 0;
397
+ for (let i = 0; i < DIM; i++) dot += a[i] * b[i];
398
+ return dot;
399
+ }
400
+ function matches(metadata, filter) {
401
+ for (const [key, want] of Object.entries(filter)) {
402
+ if (want !== null && typeof want === "object") throw new Error(`In-memory Vector: filter operator unsupported (${key}). Only top-level exact-match is supported.`);
403
+ if (metadata?.[key] !== want) return false;
404
+ }
405
+ return true;
406
+ }
407
+ function createMemoryVector(opts) {
408
+ const ns = opts.namespace;
409
+ return {
410
+ async upsert(id, text, metadata) {
411
+ getStore(ns).set(id, {
412
+ text,
413
+ metadata,
414
+ vec: pseudoEmbed(text)
415
+ });
416
+ },
417
+ async query(text, queryOpts) {
418
+ const topK = queryOpts?.topK ?? 5;
419
+ const filter = queryOpts?.filter;
420
+ const probe = pseudoEmbed(text);
421
+ const scored = [];
422
+ for (const [id, rec] of getStore(ns)) {
423
+ if (filter && !matches(rec.metadata, filter)) continue;
424
+ scored.push({
425
+ id,
426
+ score: cosine(probe, rec.vec),
427
+ text: rec.text,
428
+ ...rec.metadata !== void 0 ? { metadata: rec.metadata } : {}
429
+ });
430
+ }
431
+ scored.sort((a, b) => b.score - a.score);
432
+ return scored.slice(0, topK);
433
+ },
434
+ async delete(ids) {
435
+ const store = getStore(ns);
436
+ const list = Array.isArray(ids) ? ids : [ids];
437
+ for (const id of list) store.delete(id);
438
+ }
439
+ };
388
440
  }
389
441
  //#endregion
390
442
  //#region host/providers/stt/assemblyai.ts
@@ -762,7 +814,7 @@ function openSoniox(opts = {}) {
762
814
  async open(openOpts) {
763
815
  const apiKey = openOpts.apiKey || process.env.SONIOX_API_KEY;
764
816
  if (!apiKey) throw makeSttError("stt_auth_failed", "Soniox STT: missing API key. Set SONIOX_API_KEY in the agent env.");
765
- const ws = new WsWebSocket(SONIOX_WS_URL);
817
+ const ws = new WebSocket(SONIOX_WS_URL);
766
818
  const emitter = createNanoEvents();
767
819
  let closed = false;
768
820
  const finalBuf = { value: "" };
@@ -800,7 +852,7 @@ function openSoniox(opts = {}) {
800
852
  else openOpts.signal.addEventListener("abort", () => void close(), { once: true });
801
853
  return {
802
854
  sendAudio(pcm) {
803
- if (closed || ws.readyState !== WsWebSocket.OPEN) return;
855
+ if (closed || ws.readyState !== WebSocket.OPEN) return;
804
856
  ws.send(new Uint8Array(pcm.buffer, pcm.byteOffset, pcm.byteLength), { binary: true });
805
857
  },
806
858
  on(event, fn) {
@@ -1112,7 +1164,7 @@ function openRime(opts) {
1112
1164
  const url = `wss://users-ws.rime.ai/ws2?speaker=${encodeURIComponent(voice)}&modelId=${encodeURIComponent(model)}&audioFormat=pcm&samplingRate=${sampleRate}&lang=${encodeURIComponent(lang)}`;
1113
1165
  let ws;
1114
1166
  try {
1115
- ws = new WsWebSocket(url, { headers: { Authorization: `Bearer ${apiKey}` } });
1167
+ ws = new WebSocket(url, { headers: { Authorization: `Bearer ${apiKey}` } });
1116
1168
  } catch (cause) {
1117
1169
  throw makeTtsError("tts_connect_failed", `Rime TTS: failed to create WebSocket: ${cause instanceof Error ? cause.message : String(cause)}`);
1118
1170
  }
@@ -1172,19 +1224,19 @@ function openRime(opts) {
1172
1224
  return {
1173
1225
  sendText(text) {
1174
1226
  if (closed || text.length === 0) return;
1175
- if (ws.readyState !== WsWebSocket.OPEN) return;
1227
+ if (ws.readyState !== WebSocket.OPEN) return;
1176
1228
  doneEmitted = false;
1177
1229
  ws.send(JSON.stringify({ text }));
1178
1230
  },
1179
1231
  flush() {
1180
1232
  if (closed) return;
1181
- if (ws.readyState !== WsWebSocket.OPEN) return;
1233
+ if (ws.readyState !== WebSocket.OPEN) return;
1182
1234
  ws.send(JSON.stringify({ text: "." }));
1183
1235
  armFirstAudioTimer();
1184
1236
  },
1185
1237
  cancel() {
1186
1238
  if (closed) return;
1187
- if (ws.readyState === WsWebSocket.OPEN) ws.send(JSON.stringify({ operation: "clear" }));
1239
+ if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ operation: "clear" }));
1188
1240
  emitDoneOnce();
1189
1241
  },
1190
1242
  on(event, fn) {
@@ -1259,12 +1311,259 @@ function resolveLlm(descriptor, env) {
1259
1311
  default: throw new Error(`Unknown LLM provider kind: "${descriptor.kind}". Supported: ${ANTHROPIC_KIND}, ${OPENAI_KIND}, ${GOOGLE_KIND}, ${MISTRAL_KIND}, xai, ${GROQ_KIND}.`);
1260
1312
  }
1261
1313
  }
1314
+ const requireFromHere = createRequire(import.meta.url);
1315
+ /**
1316
+ * Lazy-load a package via createRequire so the package is a true optional
1317
+ * peer dependency — if it's not installed the error surfaces only when the
1318
+ * provider is actually used, not at module load time.
1319
+ */
1320
+ function loadProviderPackage(name, label) {
1321
+ try {
1322
+ return requireFromHere(name);
1323
+ } catch (err) {
1324
+ if (err instanceof Error && (err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND") && err.message.includes(name)) throw new Error(`${label}: package \`${name}\` is not installed. Run \`pnpm add ${name}\`.`, { cause: err });
1325
+ throw err;
1326
+ }
1327
+ }
1262
1328
  function requireKey(env, name, label) {
1263
1329
  const key = resolveApiKey(name, env);
1264
1330
  if (!key) throw new Error(`${label} LLM: missing API key. Set ${name} in the agent env.`);
1265
1331
  return key;
1266
1332
  }
1267
1333
  //#endregion
1334
+ //#region host/pinecone-vector.ts
1335
+ function createPineconeVector(opts) {
1336
+ const { Pinecone } = loadProviderPackage("@pinecone-database/pinecone", "Pinecone Vector");
1337
+ const client = new Pinecone({ apiKey: opts.apiKey });
1338
+ const ns = () => client.index(opts.index).namespace(opts.namespace);
1339
+ return {
1340
+ async upsert(id, text, metadata) {
1341
+ const record = {
1342
+ _id: id,
1343
+ text,
1344
+ ...metadata ?? {}
1345
+ };
1346
+ await ns().upsertRecords([record]);
1347
+ },
1348
+ async query(text, queryOpts) {
1349
+ const topK = queryOpts?.topK ?? 5;
1350
+ const req = {
1351
+ query: {
1352
+ inputs: { text },
1353
+ topK,
1354
+ ...queryOpts?.filter !== void 0 ? { filter: queryOpts.filter } : {}
1355
+ },
1356
+ fields: ["*"]
1357
+ };
1358
+ return (await ns().searchRecords(req)).result.hits.map((hit) => {
1359
+ const { text: hitText, ...rest } = hit.fields;
1360
+ const metadata = Object.keys(rest).length > 0 ? rest : void 0;
1361
+ return {
1362
+ id: hit._id,
1363
+ score: hit._score,
1364
+ text: typeof hitText === "string" ? hitText : "",
1365
+ ...metadata !== void 0 ? { metadata } : {}
1366
+ };
1367
+ });
1368
+ },
1369
+ async delete(ids) {
1370
+ const list = Array.isArray(ids) ? ids : [ids];
1371
+ await ns().deleteMany(list);
1372
+ }
1373
+ };
1374
+ }
1375
+ //#endregion
1376
+ //#region host/unstorage-kv.ts
1377
+ /**
1378
+ * Key-value store backed by unstorage.
1379
+ *
1380
+ * Works with any unstorage driver (memory, fs, S3/R2, etc.).
1381
+ */
1382
+ /**
1383
+ * Create a KV store backed by any unstorage driver.
1384
+ *
1385
+ * @param options - See {@link UnstorageKvOptions}.
1386
+ * @returns A {@link Kv} instance.
1387
+ *
1388
+ * @example
1389
+ * ```ts
1390
+ * import { createStorage } from "unstorage";
1391
+ * import { createUnstorageKv } from "@alexkroman1/aai/unstorage-kv";
1392
+ *
1393
+ * const kv = createUnstorageKv({ storage: createStorage() });
1394
+ * await kv.set("greeting", "hello");
1395
+ * const value = await kv.get<string>("greeting"); // "hello"
1396
+ * ```
1397
+ */
1398
+ function createUnstorageKv(options) {
1399
+ const store = options.prefix ? prefixStorage(options.storage, options.prefix) : options.storage;
1400
+ return {
1401
+ async get(key) {
1402
+ return await store.getItem(key) ?? null;
1403
+ },
1404
+ async set(key, value, setOptions) {
1405
+ if (JSON.stringify(value).length > 65536) throw new Error(`Value exceeds max size of ${MAX_VALUE_SIZE} bytes`);
1406
+ const storable = value;
1407
+ if (setOptions?.expireIn && setOptions.expireIn > 0) await store.setItem(key, storable, { ttl: Math.ceil(setOptions.expireIn / 1e3) });
1408
+ else await store.setItem(key, storable);
1409
+ },
1410
+ async delete(keys) {
1411
+ const keyArray = Array.isArray(keys) ? keys : [keys];
1412
+ await Promise.all(keyArray.map((k) => store.removeItem(k)));
1413
+ },
1414
+ close() {
1415
+ store.dispose();
1416
+ }
1417
+ };
1418
+ }
1419
+ //#endregion
1420
+ //#region host/providers/resolve-kv.ts
1421
+ /**
1422
+ * Descriptor → concrete `Kv` resolver. Mirror of `resolveLlm` /
1423
+ * `resolveVector`. Always wraps the produced unstorage Storage in
1424
+ * `createUnstorageKv` with the provided per-tenant prefix so namespace
1425
+ * isolation is enforced regardless of backend choice.
1426
+ */
1427
+ /**
1428
+ * Load a CJS unstorage driver factory. The CJS variants use
1429
+ * `module.exports = defineDriver(...)` so the require result is the
1430
+ * factory itself (not an object with `.default`).
1431
+ *
1432
+ * Delegates to loadProviderPackage (lazy-load via createRequire so the
1433
+ * driver is a true optional peer dep).
1434
+ */
1435
+ function loadDriver(modulePath, label) {
1436
+ return loadProviderPackage(modulePath, `${label} KV: driver`);
1437
+ }
1438
+ /**
1439
+ * Build a lazy unstorage Driver that defers loading the real driver
1440
+ * factory until the first I/O operation. This is necessary for drivers
1441
+ * whose peer dependencies (e.g. `ioredis`) may not be installed on the
1442
+ * host at startup — the missing package will only surface when the agent
1443
+ * actually performs KV operations, not at session creation time.
1444
+ */
1445
+ function makeLazyDriver(modulePath, label, opts) {
1446
+ let resolved = null;
1447
+ const get = () => {
1448
+ if (!resolved) resolved = loadDriver(modulePath, label)(opts);
1449
+ return resolved;
1450
+ };
1451
+ return {
1452
+ name: label.toLowerCase(),
1453
+ hasItem: (key, txOpts) => get().hasItem(key, txOpts),
1454
+ getItem: (key, txOpts) => get().getItem(key, txOpts),
1455
+ getItemRaw: (key, txOpts) => get().getItemRaw?.(key, txOpts) ?? null,
1456
+ setItem: (key, value, txOpts) => get().setItem?.(key, value, txOpts),
1457
+ setItemRaw: (key, value, txOpts) => get().setItemRaw?.(key, value, txOpts),
1458
+ removeItem: (key, txOpts) => get().removeItem?.(key, txOpts),
1459
+ getKeys: (base, txOpts) => get().getKeys(base, txOpts),
1460
+ clear: (base, txOpts) => get().clear?.(base, txOpts),
1461
+ dispose: () => resolved ? resolved.dispose?.() : void 0
1462
+ };
1463
+ }
1464
+ /** Resolve a {@link KvProvider} descriptor into a {@link Kv}. */
1465
+ function resolveKv(descriptor, env, prefix) {
1466
+ switch (descriptor.kind) {
1467
+ case MEMORY_KV_KIND: return createUnstorageKv({
1468
+ storage: createStorage(),
1469
+ prefix
1470
+ });
1471
+ case "fs": {
1472
+ const opts = descriptor.options;
1473
+ return createUnstorageKv({
1474
+ storage: createStorage({ driver: loadDriver("unstorage/drivers/fs", "fs")({ base: opts.base }) }),
1475
+ prefix
1476
+ });
1477
+ }
1478
+ case "s3": {
1479
+ const opts = descriptor.options;
1480
+ const accessKeyId = resolveApiKey("AWS_ACCESS_KEY_ID", env);
1481
+ const secretAccessKey = resolveApiKey("AWS_SECRET_ACCESS_KEY", env);
1482
+ if (!(accessKeyId && secretAccessKey)) throw new Error("S3 KV: missing AWS credentials. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in the agent env.");
1483
+ return createUnstorageKv({
1484
+ storage: createStorage({ driver: loadDriver("unstorage/drivers/s3", "S3")({
1485
+ bucket: opts.bucket,
1486
+ ...opts.endpoint !== void 0 ? { endpoint: opts.endpoint } : {},
1487
+ region: opts.region ?? "auto",
1488
+ accessKeyId,
1489
+ secretAccessKey
1490
+ }) }),
1491
+ prefix
1492
+ });
1493
+ }
1494
+ case REDIS_KV_KIND: {
1495
+ const opts = descriptor.options;
1496
+ const url = resolveApiKey("REDIS_URL", env);
1497
+ if (!url) throw new Error("Redis KV: missing connection URL. Set REDIS_URL in the agent env.");
1498
+ return createUnstorageKv({
1499
+ storage: createStorage({ driver: makeLazyDriver("unstorage/drivers/redis", "Redis", {
1500
+ url,
1501
+ ...opts.tls !== void 0 ? { tls: opts.tls } : {}
1502
+ }) }),
1503
+ prefix
1504
+ });
1505
+ }
1506
+ default: throw new Error(`Unknown KV provider kind: "${descriptor.kind}". Supported: ${MEMORY_KV_KIND}, fs, s3, ${REDIS_KV_KIND}.`);
1507
+ }
1508
+ }
1509
+ //#endregion
1510
+ //#region host/providers/resolve-vector.ts
1511
+ /**
1512
+ * Descriptor → concrete `Vector` resolver. Mirror of `resolveLlm`.
1513
+ *
1514
+ * Pulls API keys from the agent env so descriptors stay
1515
+ * secret-free. Lazy-loads provider SDKs via `createRequire` so
1516
+ * unused providers never enter the bundle.
1517
+ */
1518
+ /** Resolve a {@link VectorProvider} descriptor into a {@link Vector}. */
1519
+ function resolveVector(descriptor, env, namespace) {
1520
+ switch (descriptor.kind) {
1521
+ case IN_MEMORY_VECTOR_KIND: return createMemoryVector({ namespace });
1522
+ case PINECONE_VECTOR_KIND: {
1523
+ const apiKey = resolveApiKey("PINECONE_API_KEY", env);
1524
+ if (!apiKey) throw new Error("Pinecone Vector: missing API key. Set PINECONE_API_KEY in the agent env.");
1525
+ const opts = descriptor.options;
1526
+ return createPineconeVector({
1527
+ apiKey,
1528
+ index: opts.index,
1529
+ namespace
1530
+ });
1531
+ }
1532
+ default: throw new Error(`Unknown Vector provider kind: "${descriptor.kind}". Supported: ${IN_MEMORY_VECTOR_KIND}, ${PINECONE_VECTOR_KIND}.`);
1533
+ }
1534
+ }
1535
+ //#endregion
1536
+ //#region sdk/system-prompt.ts
1537
+ function getFormattedDate() {
1538
+ return (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
1539
+ weekday: "long",
1540
+ year: "numeric",
1541
+ month: "long",
1542
+ day: "numeric"
1543
+ });
1544
+ }
1545
+ const VOICE_RULES = "\n\nCRITICAL OUTPUT RULES — you MUST follow these for EVERY response:\nYour response will be spoken aloud by a TTS system and displayed as plain text.\n- NEVER use markdown: no **, no *, no _, no #, no `, no [](), no ---\n- NEVER use bullet points (-, *, •) or numbered lists (1., 2.)\n- NEVER use code blocks or inline code\n- NEVER mention tools, search, APIs, or technical failures to the user. If a tool returns no results, just answer naturally without explaining why.\n- Write exactly as you would say it out loud to a friend\n- Use short conversational sentences. To list things, say \"First,\" \"Next,\" \"Finally,\"\n- Keep responses concise — 1 to 3 sentences max";
1546
+ /**
1547
+ * Build the system prompt sent to the LLM from the agent configuration.
1548
+ *
1549
+ * Assembles the default system prompt, today's date, agent-specific instructions,
1550
+ * and optional sections for tool usage preamble and voice output rules.
1551
+ *
1552
+ * @param config - The serializable agent configuration (name, systemPrompt, etc.).
1553
+ * @param opts.hasTools - When `true`, appends a preamble instructing the LLM to
1554
+ * speak a brief phrase before each tool call to fill silence.
1555
+ * @param opts.voice - When `true`, appends strict voice-specific output rules
1556
+ * (no markdown, no bullet points, conversational tone, concise responses).
1557
+ * @returns The assembled system prompt string.
1558
+ */
1559
+ function buildSystemPrompt(config, opts) {
1560
+ const { hasTools } = opts;
1561
+ const agentInstructions = config.systemPrompt && config.systemPrompt !== DEFAULT_SYSTEM_PROMPT ? `\n\nAgent-Specific Instructions:\n${config.systemPrompt}` : "";
1562
+ const toolPreamble = hasTools ? "\n\nWhen you decide to use a tool, ALWAYS say a brief natural phrase BEFORE the tool call (e.g. \"Let me look that up\" or \"One moment while I check\"). This fills silence while the tool executes. Keep preambles to one short sentence." : "";
1563
+ const guidance = opts.toolGuidance && opts.toolGuidance.length > 0 ? `\n\nBuilt-in Tool Usage:\n${opts.toolGuidance.join("\n")}` : "";
1564
+ return DEFAULT_SYSTEM_PROMPT + `\n\nToday's date is ${getFormattedDate()}.` + agentInstructions + toolPreamble + guidance + (opts.voice ? VOICE_RULES : "");
1565
+ }
1566
+ //#endregion
1268
1567
  //#region host/runtime-config.ts
1269
1568
  /**
1270
1569
  * Runtime dependencies injected into the session pipeline.
@@ -1306,7 +1605,7 @@ const jsonLogger = {
1306
1605
  };
1307
1606
  /** Default S2S endpoint configuration. */
1308
1607
  const DEFAULT_S2S_CONFIG = {
1309
- wssUrl: "wss://agents.assemblyai.com/v1/voice",
1608
+ wssUrl: "wss://agents.assemblyai.com/v1/ws",
1310
1609
  inputSampleRate: DEFAULT_STT_SAMPLE_RATE,
1311
1610
  outputSampleRate: DEFAULT_TTS_SAMPLE_RATE
1312
1611
  };
@@ -1547,7 +1846,7 @@ function createSessionCore(opts) {
1547
1846
  */
1548
1847
  const yieldTick = () => new Promise((r) => setTimeout(r, 0));
1549
1848
  function buildToolContext(opts) {
1550
- const { env, state, kv, messages, sessionId } = opts;
1849
+ const { env, state, kv, vector, messages, sessionId } = opts;
1551
1850
  return {
1552
1851
  env,
1553
1852
  state: state ?? {},
@@ -1555,6 +1854,10 @@ function buildToolContext(opts) {
1555
1854
  if (!kv) throw new Error("KV not available");
1556
1855
  return kv;
1557
1856
  },
1857
+ get vector() {
1858
+ if (!vector) throw new Error("Vector not available");
1859
+ return vector;
1860
+ },
1558
1861
  messages: messages ?? [],
1559
1862
  sessionId: sessionId ?? "",
1560
1863
  send(event, data) {
@@ -2022,7 +2325,7 @@ function createPipelineTransport(opts) {
2022
2325
  //#region host/s2s.ts
2023
2326
  const uint8ToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
2024
2327
  const base64ToUint8 = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
2025
- const defaultCreateS2sWebSocket = (url, opts) => new WsWebSocket(url, { headers: opts.headers });
2328
+ const defaultCreateS2sWebSocket = (url, opts) => new WebSocket(url, { headers: opts.headers });
2026
2329
  const S2sMessageSchema = z.discriminatedUnion("type", [
2027
2330
  z.object({
2028
2331
  type: z.literal("session.ready"),
@@ -2462,50 +2765,6 @@ function createS2sTransport(opts) {
2462
2765
  };
2463
2766
  }
2464
2767
  //#endregion
2465
- //#region host/unstorage-kv.ts
2466
- /**
2467
- * Key-value store backed by unstorage.
2468
- *
2469
- * Works with any unstorage driver (memory, fs, S3/R2, etc.).
2470
- */
2471
- /**
2472
- * Create a KV store backed by any unstorage driver.
2473
- *
2474
- * @param options - See {@link UnstorageKvOptions}.
2475
- * @returns A {@link Kv} instance.
2476
- *
2477
- * @example
2478
- * ```ts
2479
- * import { createStorage } from "unstorage";
2480
- * import { createUnstorageKv } from "@alexkroman1/aai/unstorage-kv";
2481
- *
2482
- * const kv = createUnstorageKv({ storage: createStorage() });
2483
- * await kv.set("greeting", "hello");
2484
- * const value = await kv.get<string>("greeting"); // "hello"
2485
- * ```
2486
- */
2487
- function createUnstorageKv(options) {
2488
- const store = options.prefix ? prefixStorage(options.storage, options.prefix) : options.storage;
2489
- return {
2490
- async get(key) {
2491
- return await store.getItem(key) ?? null;
2492
- },
2493
- async set(key, value, setOptions) {
2494
- if (JSON.stringify(value).length > 65536) throw new Error(`Value exceeds max size of ${MAX_VALUE_SIZE} bytes`);
2495
- const storable = value;
2496
- if (setOptions?.expireIn && setOptions.expireIn > 0) await store.setItem(key, storable, { ttl: Math.ceil(setOptions.expireIn / 1e3) });
2497
- else await store.setItem(key, storable);
2498
- },
2499
- async delete(keys) {
2500
- const keyArray = Array.isArray(keys) ? keys : [keys];
2501
- await Promise.all(keyArray.map((k) => store.removeItem(k)));
2502
- },
2503
- close() {
2504
- store.dispose();
2505
- }
2506
- };
2507
- }
2508
- //#endregion
2509
2768
  //#region host/ws-handler.ts
2510
2769
  /**
2511
2770
  * WebSocket session lifecycle handler.
@@ -2741,6 +3000,10 @@ function resolveLlmIfDescriptor(value, env) {
2741
3000
  function createLocalKv() {
2742
3001
  return createUnstorageKv({ storage: createStorage() });
2743
3002
  }
3003
+ /** Create an in-memory Vector store (default for self-hosted). */
3004
+ function createLocalVector(slug) {
3005
+ return createMemoryVector({ namespace: slug });
3006
+ }
2744
3007
  /**
2745
3008
  * Create an agent runtime — the execution engine for a voice agent.
2746
3009
  *
@@ -2754,8 +3017,11 @@ function createLocalKv() {
2754
3017
  * @public
2755
3018
  */
2756
3019
  function createRuntime(opts) {
2757
- const { agent, env, kv = createLocalKv(), createWebSocket, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG, sessionStartTimeoutMs, shutdownTimeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS } = opts;
3020
+ const { agent, env, kv = createLocalKv(), vector, createWebSocket, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG, sessionStartTimeoutMs, shutdownTimeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS } = opts;
2758
3021
  const mode = assertProviderTriple(opts.stt, opts.llm, opts.tts);
3022
+ const slug = agent.name ?? "local";
3023
+ const resolvedKv = agent.kv ? resolveKv(agent.kv, env, "") : kv;
3024
+ const resolvedVector = agent.vector ? resolveVector(agent.vector, env, slug) : vector ?? createLocalVector(slug);
2759
3025
  const agentConfig = toAgentConfig(agent);
2760
3026
  const sessions = /* @__PURE__ */ new Map();
2761
3027
  const sinkMap = /* @__PURE__ */ new Map();
@@ -2775,7 +3041,8 @@ function createRuntime(opts) {
2775
3041
  tool,
2776
3042
  env: frozenEnv,
2777
3043
  sessionId: sessionId ?? "",
2778
- kv,
3044
+ kv: resolvedKv,
3045
+ vector: resolvedVector,
2779
3046
  messages,
2780
3047
  logger
2781
3048
  });
@@ -2807,7 +3074,8 @@ function createRuntime(opts) {
2807
3074
  env: frozenEnv,
2808
3075
  state: getState(sessionId ?? ""),
2809
3076
  sessionId: sessionId ?? "",
2810
- kv,
3077
+ kv: resolvedKv,
3078
+ vector: resolvedVector,
2811
3079
  messages,
2812
3080
  logger,
2813
3081
  send: sink ? (event, data) => sink.event({
@@ -2982,6 +3250,47 @@ async function serveStatic(dir, req, res) {
2982
3250
  return false;
2983
3251
  }
2984
3252
  }
3253
+ function handleVectorPost(vector, req, res) {
3254
+ let body = "";
3255
+ req.on("data", (chunk) => {
3256
+ body += chunk;
3257
+ });
3258
+ req.on("end", async () => {
3259
+ try {
3260
+ const json = JSON.parse(body);
3261
+ const parsed = VectorRequestSchema.safeParse(json);
3262
+ if (!parsed.success) {
3263
+ res.statusCode = 400;
3264
+ res.end(JSON.stringify({ error: parsed.error.message }));
3265
+ return;
3266
+ }
3267
+ const op = parsed.data;
3268
+ let result;
3269
+ switch (op.op) {
3270
+ case "upsert":
3271
+ await vector.upsert(op.id, op.text, op.metadata);
3272
+ result = "OK";
3273
+ break;
3274
+ case "query":
3275
+ result = await vector.query(op.text, {
3276
+ ...op.topK !== void 0 ? { topK: op.topK } : {},
3277
+ ...op.filter !== void 0 ? { filter: op.filter } : {}
3278
+ });
3279
+ break;
3280
+ case "delete":
3281
+ await vector.delete(op.ids);
3282
+ result = "OK";
3283
+ break;
3284
+ default: break;
3285
+ }
3286
+ res.statusCode = 200;
3287
+ res.end(JSON.stringify({ result }));
3288
+ } catch (err) {
3289
+ res.statusCode = 500;
3290
+ res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
3291
+ }
3292
+ });
3293
+ }
2985
3294
  function handleKvGet(kv, req, res) {
2986
3295
  const key = new URL(req.url ?? "/", "http://localhost").searchParams.get("key");
2987
3296
  if (!key) {
@@ -3008,7 +3317,7 @@ function handleKvGet(kv, req, res) {
3008
3317
  * @internal Used by aai-cli dev server.
3009
3318
  */
3010
3319
  function createServer(options) {
3011
- const { runtime, clientHtml, clientDir, logger = consoleLogger, kv } = options;
3320
+ const { runtime, clientHtml, clientDir, logger = consoleLogger, kv, vector } = options;
3012
3321
  const name = options.name ?? "agent";
3013
3322
  if (clientHtml && clientDir) throw new Error("clientHtml and clientDir are mutually exclusive");
3014
3323
  const escapedName = escapeHtml(name);
@@ -3031,6 +3340,10 @@ function createServer(options) {
3031
3340
  handleKvGet(kv, req, res);
3032
3341
  return;
3033
3342
  }
3343
+ if (vector && method === "POST" && url === "/vector") {
3344
+ handleVectorPost(vector, req, res);
3345
+ return;
3346
+ }
3034
3347
  handleRequest(req, res, url, method);
3035
3348
  });
3036
3349
  async function handleRequest(req, res, url, method) {
@@ -3089,4 +3402,4 @@ function createServer(options) {
3089
3402
  };
3090
3403
  }
3091
3404
  //#endregion
3092
- export { DEFAULT_S2S_CONFIG, _internals, consoleLogger, createPipelineTransport, createRuntime, createS2sTransport, createServer, createSessionCore, createUnstorageKv, executeInIsolate, executeToolCall, jsonLogger, resolveAllBuiltins, wireSessionSocket };
3405
+ export { DEFAULT_S2S_CONFIG, _internals, consoleLogger, createMemoryVector, createPineconeVector, createPipelineTransport, createRuntime, createS2sTransport, createServer, createSessionCore, createUnstorageKv, executeInIsolate, executeToolCall, jsonLogger, resolveAllBuiltins, resolveKv, resolveVector, wireSessionSocket };
@@ -12,6 +12,7 @@ import type { ClientSink } from "../sdk/protocol.ts";
12
12
  import { type ReadyConfig } from "../sdk/protocol.ts";
13
13
  import { type LlmProvider, type SttOpener, type SttProvider, type TtsOpener, type TtsProvider } from "../sdk/providers.ts";
14
14
  import type { AgentDef } from "../sdk/types.ts";
15
+ import type { Vector } from "../sdk/vector.ts";
15
16
  import type { Logger, S2SConfig } from "./runtime-config.ts";
16
17
  import type { CreateS2sWebSocket } from "./s2s.ts";
17
18
  import { type SessionCore } from "./session-core.ts";
@@ -50,6 +51,11 @@ export type RuntimeOptions = {
50
51
  agent: AgentDef<any>;
51
52
  env: Record<string, string>;
52
53
  kv?: Kv | undefined;
54
+ /**
55
+ * Vector store. If omitted, an in-memory store is created. The
56
+ * runtime overrides this with `agent.vector` if set.
57
+ */
58
+ vector?: Vector | undefined;
53
59
  /** Custom WebSocket factory for the S2S connection (useful for testing). */
54
60
  createWebSocket?: CreateS2sWebSocket | undefined;
55
61
  logger?: Logger | undefined;