@alexkroman1/aai 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_embeddings.d.ts +31 -0
- package/dist/_internal-types-IfPcaJd5.js +61 -0
- package/dist/_internal-types.js +1 -60
- package/dist/_ssrf-DCp_27V4.js +123 -0
- package/dist/_ssrf.js +1 -122
- package/dist/_utils-DgzpOMSV.js +61 -0
- package/dist/_utils.js +1 -60
- package/dist/{direct-executor-Ca0wt5H0.js → direct-executor-B-5mq3cu.js} +15 -17
- package/dist/index.js +1 -1
- package/dist/kv-iXtikQmR.js +32 -0
- package/dist/kv.js +1 -31
- package/dist/matchers.js +1 -1
- package/dist/middleware-core-BwyBIPed.js +107 -0
- package/dist/middleware-core.js +1 -106
- package/dist/protocol-B-H2Q4ox.js +162 -0
- package/dist/protocol.js +1 -161
- package/dist/runtime-CxcwaK68.js +58 -0
- package/dist/runtime.js +1 -52
- package/dist/s2s-M7JqtgFw.js +272 -0
- package/dist/s2s.js +1 -271
- package/dist/server.d.ts +6 -6
- package/dist/server.js +47 -43
- package/dist/{session-BkN9u0ni.js → session-BYlwcrya.js} +6 -6
- package/dist/session.js +1 -1
- package/dist/telemetry-CJlaDFNc.js +95 -0
- package/dist/telemetry.js +1 -94
- package/dist/{testing-MRl3SXsI.js → testing-BbitshLb.js} +7 -9
- package/dist/testing.js +1 -1
- package/dist/types-D8ZBxTL_.js +192 -0
- package/dist/types.js +1 -191
- package/dist/unstorage-kv-CDgP-frt.js +64 -0
- package/dist/unstorage-kv.d.ts +33 -0
- package/dist/unstorage-kv.js +2 -0
- package/dist/unstorage-vector-Cj5llNhg.js +172 -0
- package/dist/unstorage-vector.d.ts +47 -0
- package/dist/unstorage-vector.js +2 -0
- package/dist/vector.d.ts +3 -2
- package/dist/worker-entry-2jaiqIj0.js +70 -0
- package/dist/worker-entry.js +1 -69
- package/dist/ws-handler-C0Q6eSay.js +207 -0
- package/dist/ws-handler.js +1 -206
- package/package.json +14 -9
- package/dist/sqlite-kv.d.ts +0 -34
- package/dist/sqlite-kv.js +0 -133
- package/dist/sqlite-vector.d.ts +0 -58
- package/dist/sqlite-vector.js +0 -149
package/dist/server.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
1
|
+
import { t as createUnstorageVectorStore } from "./unstorage-vector-Cj5llNhg.js";
|
|
2
|
+
import { i as filterEnv } from "./_utils-DgzpOMSV.js";
|
|
3
|
+
import { t as createDirectExecutor } from "./direct-executor-B-5mq3cu.js";
|
|
4
|
+
import { m as buildReadyConfig } from "./protocol-B-H2Q4ox.js";
|
|
5
|
+
import { n as consoleLogger, t as DEFAULT_S2S_CONFIG } from "./runtime-CxcwaK68.js";
|
|
6
|
+
import { t as createUnstorageKv } from "./unstorage-kv-CDgP-frt.js";
|
|
7
|
+
import { t as wireSessionSocket } from "./ws-handler-C0Q6eSay.js";
|
|
8
|
+
import { createStorage } from "unstorage";
|
|
9
9
|
import { serve } from "@hono/node-server";
|
|
10
10
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
11
|
+
import { createNodeWebSocket } from "@hono/node-ws";
|
|
11
12
|
import { Hono } from "hono";
|
|
13
|
+
import { html } from "hono/html";
|
|
14
|
+
import { secureHeaders } from "hono/secure-headers";
|
|
12
15
|
//#region server.ts
|
|
13
16
|
/**
|
|
14
17
|
* Self-hostable agent server.
|
|
@@ -17,16 +20,6 @@ import { Hono } from "hono";
|
|
|
17
20
|
* Calls `createDirectExecutor` + `wireSessionSocket` directly — no
|
|
18
21
|
* intermediary needed.
|
|
19
22
|
*/
|
|
20
|
-
/** Escape HTML special characters to prevent XSS. */
|
|
21
|
-
function escapeHtml(s) {
|
|
22
|
-
return s.replace(/[&<>"']/g, (ch) => ({
|
|
23
|
-
"&": "&",
|
|
24
|
-
"<": "<",
|
|
25
|
-
">": ">",
|
|
26
|
-
"\"": """,
|
|
27
|
-
"'": "'"
|
|
28
|
-
})[ch]);
|
|
29
|
-
}
|
|
30
23
|
/**
|
|
31
24
|
* Create an HTTP + WebSocket server for self-hosted agent deployments.
|
|
32
25
|
*
|
|
@@ -66,23 +59,20 @@ async function drainSessions(sessions, shutdownTimeoutMs, logger) {
|
|
|
66
59
|
}
|
|
67
60
|
function createServer(options) {
|
|
68
61
|
if (options.clientHtml && options.clientDir) throw new Error("ServerOptions: clientHtml and clientDir are mutually exclusive — provide one or the other, not both.");
|
|
69
|
-
const { agent,
|
|
62
|
+
const { agent, clientHtml, clientDir, logger = consoleLogger, s2sConfig = DEFAULT_S2S_CONFIG, shutdownTimeoutMs = 3e4 } = options;
|
|
70
63
|
const env = filterEnv(options.env ?? (typeof process !== "undefined" ? process.env : {}));
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
return createSqliteKv();
|
|
74
|
-
})();
|
|
64
|
+
const storage = options.storage ?? createStorage();
|
|
65
|
+
const kv = createUnstorageKv({ storage });
|
|
75
66
|
const executor = createDirectExecutor({
|
|
76
67
|
agent,
|
|
77
68
|
env,
|
|
78
|
-
kv
|
|
79
|
-
|
|
69
|
+
kv,
|
|
70
|
+
vector: createUnstorageVectorStore({ storage }),
|
|
80
71
|
logger,
|
|
81
72
|
s2sConfig
|
|
82
73
|
});
|
|
83
74
|
const sessions = /* @__PURE__ */ new Map();
|
|
84
75
|
const readyConfig = buildReadyConfig(s2sConfig);
|
|
85
|
-
const safeAgentName = escapeHtml(agent.name);
|
|
86
76
|
function handleWs(ws, skipGreeting, resumeFrom) {
|
|
87
77
|
wireSessionSocket(ws, {
|
|
88
78
|
sessions,
|
|
@@ -108,6 +98,7 @@ function createServer(options) {
|
|
|
108
98
|
async listen(port = 3e3) {
|
|
109
99
|
if (serverHandle) throw new Error("Server is already listening");
|
|
110
100
|
const app = new Hono();
|
|
101
|
+
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
|
|
111
102
|
app.onError((err, c) => {
|
|
112
103
|
logger.error(`${c.req.method} ${c.req.path} error: ${err.message}`);
|
|
113
104
|
return c.json({ error: "Internal Server Error" }, 500);
|
|
@@ -122,6 +113,24 @@ function createServer(options) {
|
|
|
122
113
|
if (status >= 400) logger.error(`${method} ${path} ${status} ${ms}ms`);
|
|
123
114
|
else logger.info(`${method} ${path} ${status} ${ms}ms`);
|
|
124
115
|
});
|
|
116
|
+
app.use("*", secureHeaders({ contentSecurityPolicy: {
|
|
117
|
+
defaultSrc: ["'self'"],
|
|
118
|
+
scriptSrc: ["'self'", "blob:"],
|
|
119
|
+
styleSrc: [
|
|
120
|
+
"'self'",
|
|
121
|
+
"'unsafe-inline'",
|
|
122
|
+
"https://fonts.googleapis.com"
|
|
123
|
+
],
|
|
124
|
+
connectSrc: [
|
|
125
|
+
"'self'",
|
|
126
|
+
"wss:",
|
|
127
|
+
"ws:"
|
|
128
|
+
],
|
|
129
|
+
imgSrc: ["'self'", "data:"],
|
|
130
|
+
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
|
131
|
+
objectSrc: ["'none'"],
|
|
132
|
+
baseUri: ["'self'"]
|
|
133
|
+
} }));
|
|
125
134
|
app.get("/health", (c) => c.json({
|
|
126
135
|
status: "ok",
|
|
127
136
|
name: agent.name
|
|
@@ -129,40 +138,35 @@ function createServer(options) {
|
|
|
129
138
|
app.get("/kv", async (c) => {
|
|
130
139
|
const key = c.req.query("key");
|
|
131
140
|
if (!key) return c.json({ error: "Missing key query parameter" }, 400);
|
|
132
|
-
const value = await
|
|
141
|
+
const value = await kv.get(key);
|
|
133
142
|
if (value === null) return c.json(null, 404);
|
|
134
143
|
return c.json(value);
|
|
135
144
|
});
|
|
136
145
|
if (clientDir) app.use("/*", serveStatic({ root: clientDir }));
|
|
137
|
-
const csp = "default-src 'self'; script-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src 'self' wss: ws:; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com; object-src 'none'; base-uri 'self'";
|
|
138
146
|
app.get("/", (c) => {
|
|
139
|
-
if (clientHtml) return c.html(clientHtml
|
|
140
|
-
return c.html(`<!DOCTYPE html><html><body><h1>${
|
|
147
|
+
if (clientHtml) return c.html(clientHtml);
|
|
148
|
+
return c.html(html`<!DOCTYPE html><html><body><h1>${agent.name}</h1><p>Agent server running.</p></body></html>`);
|
|
141
149
|
});
|
|
150
|
+
app.get("/websocket", upgradeWebSocket((c) => {
|
|
151
|
+
const resumeFrom = c.req.query("sessionId") ?? void 0;
|
|
152
|
+
const skipGreeting = c.req.query("resume") !== void 0 || resumeFrom !== void 0;
|
|
153
|
+
logger.info(`WS upgrade ${c.req.path}${skipGreeting ? " (resume)" : ""}`);
|
|
154
|
+
return { onOpen(_evt, ws) {
|
|
155
|
+
if (ws.raw) handleWs(ws.raw, skipGreeting, resumeFrom);
|
|
156
|
+
} };
|
|
157
|
+
}));
|
|
142
158
|
const nodeServer = serve({
|
|
143
159
|
fetch: app.fetch,
|
|
144
160
|
port
|
|
145
161
|
});
|
|
162
|
+
injectWebSocket(nodeServer);
|
|
146
163
|
await new Promise((resolve, reject) => {
|
|
147
164
|
nodeServer.on("listening", resolve);
|
|
148
165
|
nodeServer.on("error", reject);
|
|
149
166
|
});
|
|
150
167
|
const addr = nodeServer.address();
|
|
151
168
|
listenPort = typeof addr === "object" && addr ? addr.port : port;
|
|
152
|
-
const wss = new WebSocketServer({ noServer: true });
|
|
153
|
-
nodeServer.on("upgrade", (req, socket, head) => {
|
|
154
|
-
const reqUrl = new URL(req.url ?? "/", `http://localhost:${listenPort}`);
|
|
155
|
-
const resumeFrom = reqUrl.searchParams.get("sessionId") ?? void 0;
|
|
156
|
-
const skipGreeting = reqUrl.searchParams.has("resume") || resumeFrom !== void 0;
|
|
157
|
-
logger.info(`WS upgrade ${reqUrl.pathname}${skipGreeting ? " (resume)" : ""}`);
|
|
158
|
-
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
159
|
-
handleWs(ws, skipGreeting, resumeFrom);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
169
|
serverHandle = { async shutdown() {
|
|
163
|
-
await new Promise((resolve, reject) => {
|
|
164
|
-
wss.close((err) => err ? reject(err) : resolve());
|
|
165
|
-
});
|
|
166
170
|
await new Promise((resolve, reject) => {
|
|
167
171
|
nodeServer.close((err) => err ? reject(err) : resolve());
|
|
168
172
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DEFAULT_INSTRUCTIONS } from "./types.js";
|
|
2
|
-
import { errorDetail, errorMessage, toolError } from "./_utils.js";
|
|
3
|
-
import {
|
|
4
|
-
import { consoleLogger } from "./runtime.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { r as DEFAULT_INSTRUCTIONS } from "./types-D8ZBxTL_.js";
|
|
2
|
+
import { n as errorDetail, r as errorMessage, s as toolError } from "./_utils-DgzpOMSV.js";
|
|
3
|
+
import { c as MAX_TOOL_RESULT_CHARS, o as HOOK_TIMEOUT_MS } from "./protocol-B-H2Q4ox.js";
|
|
4
|
+
import { n as consoleLogger } from "./runtime-CxcwaK68.js";
|
|
5
|
+
import { _ as turnStepsHistogram, a as idleTimeoutCounter, d as toolCallCounter, f as toolCallDuration, g as turnCounter, h as tracer, n as activeSessionsUpDown, p as toolCallErrorCounter, r as bargeInCounter, u as sessionCounter } from "./telemetry-CJlaDFNc.js";
|
|
6
|
+
import { n as defaultCreateS2sWebSocket, t as connectS2s } from "./s2s-M7JqtgFw.js";
|
|
7
7
|
//#region _session-ctx.ts
|
|
8
8
|
const DEFAULT_MAX_HISTORY = 200;
|
|
9
9
|
function buildCtx(opts) {
|
package/dist/session.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as persistKey, n as createS2sSession, r as buildSystemPrompt, t as _internals } from "./session-
|
|
1
|
+
import { i as persistKey, n as createS2sSession, r as buildSystemPrompt, t as _internals } from "./session-BYlwcrya.js";
|
|
2
2
|
export { _internals, buildSystemPrompt, createS2sSession, persistKey };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { SpanStatusCode, SpanStatusCode as SpanStatusCode$1, context, metrics, metrics as metrics$1, trace, trace as trace$1 } from "@opentelemetry/api";
|
|
3
|
+
//#region telemetry.ts
|
|
4
|
+
/**
|
|
5
|
+
* OpenTelemetry instrumentation helpers for the AAI SDK.
|
|
6
|
+
*
|
|
7
|
+
* Uses `@opentelemetry/api` only — consumers bring their own SDK and
|
|
8
|
+
* exporters. When no SDK is configured the API returns no-op instances,
|
|
9
|
+
* so the overhead in uninstrumented environments is negligible.
|
|
10
|
+
*
|
|
11
|
+
* Provides:
|
|
12
|
+
* - `tracer` — a pre-scoped `Tracer` for creating spans
|
|
13
|
+
* - `meter` — a pre-scoped `Meter` for recording metrics
|
|
14
|
+
* - Pre-built counters, histograms, and up/down counters covering the
|
|
15
|
+
* STT → LLM → TTS pipeline
|
|
16
|
+
*/
|
|
17
|
+
const SCOPE = "aai";
|
|
18
|
+
const _require = createRequire(import.meta.url);
|
|
19
|
+
let VERSION = "0.0.0";
|
|
20
|
+
try {
|
|
21
|
+
VERSION = _require("./package.json").version;
|
|
22
|
+
} catch {
|
|
23
|
+
VERSION = _require("../package.json").version;
|
|
24
|
+
}
|
|
25
|
+
/** Tracer scoped to the AAI SDK. */
|
|
26
|
+
const tracer = trace.getTracer(SCOPE, VERSION);
|
|
27
|
+
/** Meter scoped to the AAI SDK. */
|
|
28
|
+
const meter = metrics.getMeter(SCOPE, VERSION);
|
|
29
|
+
/** Total sessions opened. */
|
|
30
|
+
const sessionCounter = meter.createCounter("aai.session.count", { description: "Total voice sessions opened" });
|
|
31
|
+
/** Currently active sessions. */
|
|
32
|
+
const activeSessionsUpDown = meter.createUpDownCounter("aai.session.active", { description: "Currently active voice sessions" });
|
|
33
|
+
/** Total user turns (speech → transcript). */
|
|
34
|
+
const turnCounter = meter.createCounter("aai.turn.count", { description: "Total user turns" });
|
|
35
|
+
/** Total tool calls executed. */
|
|
36
|
+
const toolCallCounter = meter.createCounter("aai.tool.call.count", { description: "Total tool calls executed" });
|
|
37
|
+
/** Tool call execution duration in seconds. */
|
|
38
|
+
const toolCallDuration = meter.createHistogram("aai.tool.call.duration", {
|
|
39
|
+
description: "Tool call execution duration in seconds",
|
|
40
|
+
unit: "s"
|
|
41
|
+
});
|
|
42
|
+
/** Total tool call errors. */
|
|
43
|
+
const toolCallErrorCounter = meter.createCounter("aai.tool.call.error.count", { description: "Total tool call errors" });
|
|
44
|
+
/** S2S WebSocket connection duration in seconds. */
|
|
45
|
+
const s2sConnectionDuration = meter.createHistogram("aai.s2s.connection.duration", {
|
|
46
|
+
description: "S2S WebSocket connection duration in seconds",
|
|
47
|
+
unit: "s"
|
|
48
|
+
});
|
|
49
|
+
/** Total S2S errors. */
|
|
50
|
+
const s2sErrorCounter = meter.createCounter("aai.s2s.error.count", { description: "Total S2S errors" });
|
|
51
|
+
/** Number of agentic loop steps (tool calls) per completed turn. */
|
|
52
|
+
const turnStepsHistogram = meter.createHistogram("aai.turn.steps", { description: "Number of agentic loop steps per completed turn" });
|
|
53
|
+
/** Total barge-in (reply interrupted) events. */
|
|
54
|
+
const bargeInCounter = meter.createCounter("aai.turn.bargein.count", { description: "Total barge-in (reply interrupted) events" });
|
|
55
|
+
/** Messages silently dropped because the WebSocket was closed. */
|
|
56
|
+
const wsSendDroppedCounter = meter.createCounter("aai.ws.send_dropped", { description: "Messages silently dropped because the WebSocket was closed" });
|
|
57
|
+
/** Sessions closed due to S2S idle timeout. */
|
|
58
|
+
const idleTimeoutCounter = meter.createCounter("aai.session.idle.timeout.count", { description: "Sessions closed due to S2S idle timeout" });
|
|
59
|
+
/**
|
|
60
|
+
* Run `fn` inside a new span. The span is automatically ended and its
|
|
61
|
+
* status set based on whether `fn` throws.
|
|
62
|
+
*/
|
|
63
|
+
function withSpan(name, fn) {
|
|
64
|
+
return tracer.startActiveSpan(name, (span) => {
|
|
65
|
+
try {
|
|
66
|
+
const result = fn(span);
|
|
67
|
+
if (result instanceof Promise) return result.then((v) => {
|
|
68
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
69
|
+
span.end();
|
|
70
|
+
return v;
|
|
71
|
+
}).catch((err) => {
|
|
72
|
+
span.setStatus({
|
|
73
|
+
code: SpanStatusCode.ERROR,
|
|
74
|
+
message: err instanceof Error ? err.message : String(err)
|
|
75
|
+
});
|
|
76
|
+
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
+
span.end();
|
|
78
|
+
throw err;
|
|
79
|
+
});
|
|
80
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
81
|
+
span.end();
|
|
82
|
+
return result;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
span.setStatus({
|
|
85
|
+
code: SpanStatusCode.ERROR,
|
|
86
|
+
message: err instanceof Error ? err.message : String(err)
|
|
87
|
+
});
|
|
88
|
+
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
89
|
+
span.end();
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { turnStepsHistogram as _, idleTimeoutCounter as a, s2sConnectionDuration as c, toolCallCounter as d, toolCallDuration as f, turnCounter as g, tracer as h, context as i, s2sErrorCounter as l, trace$1 as m, activeSessionsUpDown as n, meter as o, toolCallErrorCounter as p, bargeInCounter as r, metrics$1 as s, SpanStatusCode$1 as t, sessionCounter as u, withSpan as v, wsSendDroppedCounter as y };
|
package/dist/telemetry.js
CHANGED
|
@@ -1,95 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SpanStatusCode, SpanStatusCode as SpanStatusCode$1, context, metrics, metrics as metrics$1, trace, trace as trace$1 } from "@opentelemetry/api";
|
|
3
|
-
//#region telemetry.ts
|
|
4
|
-
/**
|
|
5
|
-
* OpenTelemetry instrumentation helpers for the AAI SDK.
|
|
6
|
-
*
|
|
7
|
-
* Uses `@opentelemetry/api` only — consumers bring their own SDK and
|
|
8
|
-
* exporters. When no SDK is configured the API returns no-op instances,
|
|
9
|
-
* so the overhead in uninstrumented environments is negligible.
|
|
10
|
-
*
|
|
11
|
-
* Provides:
|
|
12
|
-
* - `tracer` — a pre-scoped `Tracer` for creating spans
|
|
13
|
-
* - `meter` — a pre-scoped `Meter` for recording metrics
|
|
14
|
-
* - Pre-built counters, histograms, and up/down counters covering the
|
|
15
|
-
* STT → LLM → TTS pipeline
|
|
16
|
-
*/
|
|
17
|
-
const SCOPE = "aai";
|
|
18
|
-
const _require = createRequire(import.meta.url);
|
|
19
|
-
let VERSION = "0.0.0";
|
|
20
|
-
try {
|
|
21
|
-
VERSION = _require("./package.json").version;
|
|
22
|
-
} catch {
|
|
23
|
-
VERSION = _require("../package.json").version;
|
|
24
|
-
}
|
|
25
|
-
/** Tracer scoped to the AAI SDK. */
|
|
26
|
-
const tracer = trace$1.getTracer(SCOPE, VERSION);
|
|
27
|
-
/** Meter scoped to the AAI SDK. */
|
|
28
|
-
const meter = metrics$1.getMeter(SCOPE, VERSION);
|
|
29
|
-
/** Total sessions opened. */
|
|
30
|
-
const sessionCounter = meter.createCounter("aai.session.count", { description: "Total voice sessions opened" });
|
|
31
|
-
/** Currently active sessions. */
|
|
32
|
-
const activeSessionsUpDown = meter.createUpDownCounter("aai.session.active", { description: "Currently active voice sessions" });
|
|
33
|
-
/** Total user turns (speech → transcript). */
|
|
34
|
-
const turnCounter = meter.createCounter("aai.turn.count", { description: "Total user turns" });
|
|
35
|
-
/** Total tool calls executed. */
|
|
36
|
-
const toolCallCounter = meter.createCounter("aai.tool.call.count", { description: "Total tool calls executed" });
|
|
37
|
-
/** Tool call execution duration in seconds. */
|
|
38
|
-
const toolCallDuration = meter.createHistogram("aai.tool.call.duration", {
|
|
39
|
-
description: "Tool call execution duration in seconds",
|
|
40
|
-
unit: "s"
|
|
41
|
-
});
|
|
42
|
-
/** Total tool call errors. */
|
|
43
|
-
const toolCallErrorCounter = meter.createCounter("aai.tool.call.error.count", { description: "Total tool call errors" });
|
|
44
|
-
/** S2S WebSocket connection duration in seconds. */
|
|
45
|
-
const s2sConnectionDuration = meter.createHistogram("aai.s2s.connection.duration", {
|
|
46
|
-
description: "S2S WebSocket connection duration in seconds",
|
|
47
|
-
unit: "s"
|
|
48
|
-
});
|
|
49
|
-
/** Total S2S errors. */
|
|
50
|
-
const s2sErrorCounter = meter.createCounter("aai.s2s.error.count", { description: "Total S2S errors" });
|
|
51
|
-
/** Number of agentic loop steps (tool calls) per completed turn. */
|
|
52
|
-
const turnStepsHistogram = meter.createHistogram("aai.turn.steps", { description: "Number of agentic loop steps per completed turn" });
|
|
53
|
-
/** Total barge-in (reply interrupted) events. */
|
|
54
|
-
const bargeInCounter = meter.createCounter("aai.turn.bargein.count", { description: "Total barge-in (reply interrupted) events" });
|
|
55
|
-
/** Messages silently dropped because the WebSocket was closed. */
|
|
56
|
-
const wsSendDroppedCounter = meter.createCounter("aai.ws.send_dropped", { description: "Messages silently dropped because the WebSocket was closed" });
|
|
57
|
-
/** Sessions closed due to S2S idle timeout. */
|
|
58
|
-
const idleTimeoutCounter = meter.createCounter("aai.session.idle.timeout.count", { description: "Sessions closed due to S2S idle timeout" });
|
|
59
|
-
/**
|
|
60
|
-
* Run `fn` inside a new span. The span is automatically ended and its
|
|
61
|
-
* status set based on whether `fn` throws.
|
|
62
|
-
*/
|
|
63
|
-
function withSpan(name, fn) {
|
|
64
|
-
return tracer.startActiveSpan(name, (span) => {
|
|
65
|
-
try {
|
|
66
|
-
const result = fn(span);
|
|
67
|
-
if (result instanceof Promise) return result.then((v) => {
|
|
68
|
-
span.setStatus({ code: SpanStatusCode$1.OK });
|
|
69
|
-
span.end();
|
|
70
|
-
return v;
|
|
71
|
-
}).catch((err) => {
|
|
72
|
-
span.setStatus({
|
|
73
|
-
code: SpanStatusCode$1.ERROR,
|
|
74
|
-
message: err instanceof Error ? err.message : String(err)
|
|
75
|
-
});
|
|
76
|
-
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
77
|
-
span.end();
|
|
78
|
-
throw err;
|
|
79
|
-
});
|
|
80
|
-
span.setStatus({ code: SpanStatusCode$1.OK });
|
|
81
|
-
span.end();
|
|
82
|
-
return result;
|
|
83
|
-
} catch (err) {
|
|
84
|
-
span.setStatus({
|
|
85
|
-
code: SpanStatusCode$1.ERROR,
|
|
86
|
-
message: err instanceof Error ? err.message : String(err)
|
|
87
|
-
});
|
|
88
|
-
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
89
|
-
span.end();
|
|
90
|
-
throw err;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
//#endregion
|
|
1
|
+
import { _ as turnStepsHistogram, a as idleTimeoutCounter, c as s2sConnectionDuration, d as toolCallCounter, f as toolCallDuration, g as turnCounter, h as tracer, i as context, l as s2sErrorCounter, m as trace, n as activeSessionsUpDown, o as meter, p as toolCallErrorCounter, r as bargeInCounter, s as metrics, t as SpanStatusCode, u as sessionCounter, v as withSpan, y as wsSendDroppedCounter } from "./telemetry-CJlaDFNc.js";
|
|
95
2
|
export { SpanStatusCode, activeSessionsUpDown, bargeInCounter, context, idleTimeoutCounter, meter, metrics, s2sConnectionDuration, s2sErrorCounter, sessionCounter, toolCallCounter, toolCallDuration, toolCallErrorCounter, trace, tracer, turnCounter, turnStepsHistogram, withSpan, wsSendDroppedCounter };
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { join } from "node:path";
|
|
1
|
+
import { n as createTestEmbedFn, t as createUnstorageVectorStore } from "./unstorage-vector-Cj5llNhg.js";
|
|
2
|
+
import { t as createDirectExecutor } from "./direct-executor-B-5mq3cu.js";
|
|
3
|
+
import { t as createUnstorageKv } from "./unstorage-kv-CDgP-frt.js";
|
|
4
|
+
import { createStorage } from "unstorage";
|
|
7
5
|
//#region _mock-ws.ts
|
|
8
6
|
/**
|
|
9
7
|
* A mock WebSocket implementation for testing.
|
|
@@ -472,8 +470,8 @@ var TestHarness = class {
|
|
|
472
470
|
* Uses a temp directory that is unique per call for test isolation.
|
|
473
471
|
*/
|
|
474
472
|
function createTestVectorStore() {
|
|
475
|
-
return
|
|
476
|
-
|
|
473
|
+
return createUnstorageVectorStore({
|
|
474
|
+
storage: createStorage(),
|
|
477
475
|
embedFn: createTestEmbedFn()
|
|
478
476
|
});
|
|
479
477
|
}
|
|
@@ -507,7 +505,7 @@ function createTestVectorStore() {
|
|
|
507
505
|
* @public
|
|
508
506
|
*/
|
|
509
507
|
function createTestHarness(agent, options = {}) {
|
|
510
|
-
const { env = {}, kv =
|
|
508
|
+
const { env = {}, kv = createUnstorageKv({ storage: createStorage() }), vector = createTestVectorStore() } = options;
|
|
511
509
|
return new TestHarness(createDirectExecutor({
|
|
512
510
|
agent,
|
|
513
511
|
env,
|
package/dist/testing.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as installMockWebSocket, i as MockWebSocket, n as TurnResult, r as createTestHarness, t as TestHarness } from "./testing-
|
|
1
|
+
import { a as installMockWebSocket, i as MockWebSocket, n as TurnResult, r as createTestHarness, t as TestHarness } from "./testing-BbitshLb.js";
|
|
2
2
|
export { MockWebSocket, TestHarness, TurnResult, createTestHarness, installMockWebSocket };
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region types.ts
|
|
3
|
+
/**
|
|
4
|
+
* Core type definitions for the AAI agent SDK.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Identity helper that preserves the Zod schema generic for type inference.
|
|
8
|
+
*
|
|
9
|
+
* When tools are defined inline in `defineAgent({ tools: { ... } })`, the
|
|
10
|
+
* generic `P` gets widened to the base `ZodObject` type, so `args` in
|
|
11
|
+
* `execute` loses its specific shape. Wrapping a tool definition in
|
|
12
|
+
* `defineTool()` lets TypeScript infer `P` from `parameters` and type
|
|
13
|
+
* `args` correctly.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { defineAgent, defineTool } from "aai";
|
|
18
|
+
* import { z } from "zod";
|
|
19
|
+
*
|
|
20
|
+
* export default defineAgent({
|
|
21
|
+
* name: "my-agent",
|
|
22
|
+
* tools: {
|
|
23
|
+
* greet: defineTool({
|
|
24
|
+
* description: "Greet the user",
|
|
25
|
+
* parameters: z.object({ name: z.string() }),
|
|
26
|
+
* execute: ({ name }) => `Hello, ${name}!`, // name is string
|
|
27
|
+
* }),
|
|
28
|
+
* },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
function defineTool(def) {
|
|
35
|
+
return def;
|
|
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
|
+
/**
|
|
75
|
+
* Default system prompt used when `instructions` is not provided.
|
|
76
|
+
*
|
|
77
|
+
* Optimized for voice-first interactions: short sentences, no visual
|
|
78
|
+
* formatting, confident tone, and concise answers.
|
|
79
|
+
*/
|
|
80
|
+
const DEFAULT_INSTRUCTIONS = `\
|
|
81
|
+
You are AAI, a helpful AI assistant.
|
|
82
|
+
|
|
83
|
+
Voice-First Rules:
|
|
84
|
+
- Optimize for natural speech. Avoid jargon unless central to the answer. \
|
|
85
|
+
Use short, punchy sentences.
|
|
86
|
+
- Never mention "search results," "sources," or "the provided text." \
|
|
87
|
+
Speak as if the knowledge is your own.
|
|
88
|
+
- No visual formatting. Do not say "bullet point," "bold," or "bracketed one." \
|
|
89
|
+
If you need to list items, say "First," "Next," and "Finally."
|
|
90
|
+
- Start with the most important information. No introductory filler.
|
|
91
|
+
- Be concise. Keep answers to 1-3 sentences. For complex topics, provide a high-level summary.
|
|
92
|
+
- Be confident. Avoid hedging phrases like "It seems that" or "I believe."
|
|
93
|
+
- If you don't have enough information, say so directly rather than guessing.
|
|
94
|
+
- Never use exclamation points. Keep your tone calm and conversational.`;
|
|
95
|
+
/** Default greeting spoken when a session starts. */
|
|
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. */
|
|
98
|
+
const BuiltinToolSchema = z.enum([
|
|
99
|
+
"web_search",
|
|
100
|
+
"visit_webpage",
|
|
101
|
+
"fetch_json",
|
|
102
|
+
"run_code",
|
|
103
|
+
"vector_search",
|
|
104
|
+
"memory"
|
|
105
|
+
]);
|
|
106
|
+
/** @internal Zod schema for {@link ToolChoice}. Exported for reuse in internal schemas. */
|
|
107
|
+
const ToolChoiceSchema = z.union([z.enum([
|
|
108
|
+
"auto",
|
|
109
|
+
"required",
|
|
110
|
+
"none"
|
|
111
|
+
]), z.object({
|
|
112
|
+
type: z.literal("tool"),
|
|
113
|
+
toolName: z.string().min(1)
|
|
114
|
+
})]);
|
|
115
|
+
const ToolDefSchema = z.object({
|
|
116
|
+
description: z.string().min(1, "Tool description must be non-empty"),
|
|
117
|
+
parameters: z.custom((val) => val === void 0 || val instanceof z.ZodType, "Expected a Zod schema").optional(),
|
|
118
|
+
execute: z.function()
|
|
119
|
+
});
|
|
120
|
+
/** Default TTL for persisted session data: 1 hour. */
|
|
121
|
+
const DEFAULT_PERSIST_TTL = 36e5;
|
|
122
|
+
const AgentOptionsSchema = z.object({
|
|
123
|
+
name: z.string().min(1, "Agent name must be non-empty"),
|
|
124
|
+
instructions: z.string().optional(),
|
|
125
|
+
greeting: z.string().optional(),
|
|
126
|
+
sttPrompt: z.string().optional(),
|
|
127
|
+
maxSteps: z.union([z.number().int().positive(), z.function()]).optional(),
|
|
128
|
+
toolChoice: ToolChoiceSchema.optional(),
|
|
129
|
+
builtinTools: z.array(BuiltinToolSchema).optional(),
|
|
130
|
+
tools: z.record(z.string(), ToolDefSchema).optional(),
|
|
131
|
+
state: z.function().optional(),
|
|
132
|
+
persistence: z.union([z.literal(true), z.object({ ttl: z.number().int().positive().optional() })]).optional(),
|
|
133
|
+
onConnect: z.function().optional(),
|
|
134
|
+
onDisconnect: z.function().optional(),
|
|
135
|
+
onError: z.function().optional(),
|
|
136
|
+
onTurn: z.function().optional(),
|
|
137
|
+
onStep: 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()
|
|
148
|
+
});
|
|
149
|
+
/**
|
|
150
|
+
* Create an agent definition from the given options, applying sensible defaults.
|
|
151
|
+
*
|
|
152
|
+
* This is the main entry point for defining a voice agent. The returned
|
|
153
|
+
* `AgentDef` is consumed by the AAI server at deploy time.
|
|
154
|
+
*
|
|
155
|
+
* @param options - Configuration for the agent including name, instructions,
|
|
156
|
+
* tools, hooks, and other settings.
|
|
157
|
+
* @returns A fully resolved agent definition with all defaults applied.
|
|
158
|
+
*
|
|
159
|
+
* @public
|
|
160
|
+
*
|
|
161
|
+
* @example Basic agent with a custom tool
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { defineAgent } from "aai";
|
|
164
|
+
* import { z } from "zod";
|
|
165
|
+
*
|
|
166
|
+
* export default defineAgent({
|
|
167
|
+
* name: "greeter",
|
|
168
|
+
* instructions: "You greet people warmly.",
|
|
169
|
+
* tools: {
|
|
170
|
+
* greet: {
|
|
171
|
+
* description: "Greet a user by name",
|
|
172
|
+
* parameters: z.object({ name: z.string() }),
|
|
173
|
+
* execute: ({ name }) => `Hello, ${name}!`,
|
|
174
|
+
* },
|
|
175
|
+
* },
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
function defineAgent(options) {
|
|
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;
|
|
182
|
+
return {
|
|
183
|
+
...options,
|
|
184
|
+
instructions: options.instructions ?? DEFAULT_INSTRUCTIONS,
|
|
185
|
+
greeting: options.greeting ?? "Hey there. I'm a voice assistant. What can I help you with?",
|
|
186
|
+
maxSteps: options.maxSteps ?? 5,
|
|
187
|
+
tools: options.tools ?? {},
|
|
188
|
+
persistence
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
export { createToolFactory as a, ToolChoiceSchema as i, DEFAULT_GREETING as n, defineAgent as o, DEFAULT_INSTRUCTIONS as r, defineTool as s, BuiltinToolSchema as t };
|