@absolutejs/voice 0.0.22-beta.22 → 0.0.22-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { voice } from './plugin';
2
2
  export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRuns } from './assistant';
3
3
  export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
4
- export { createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, summarizeVoiceSessionReplay } from './sessionReplay';
4
+ export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
5
5
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
6
6
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
7
7
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
@@ -30,7 +30,7 @@ export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewReco
30
30
  export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
31
31
  export type { VoiceAssistantHealthFailure, VoiceAssistantHealthHTMLHandlerOptions, VoiceAssistantHealthRoutesOptions, VoiceAssistantHealthSummary, VoiceAssistantHealthSummaryOptions } from './assistantHealth';
32
32
  export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
33
- export type { VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
33
+ export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
34
34
  export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
35
35
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
36
36
  export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
package/dist/index.js CHANGED
@@ -7064,6 +7064,10 @@ var buildVoiceTraceReplay = (events, options = {}) => ({
7064
7064
 
7065
7065
  // src/sessionReplay.ts
7066
7066
  var getString3 = (value) => typeof value === "string" ? value : undefined;
7067
+ var escapeHtml6 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
7068
+ var increment2 = (record, key) => {
7069
+ record[key] = (record[key] ?? 0) + 1;
7070
+ };
7067
7071
  var buildReplayTurns = (events) => {
7068
7072
  const turns = new Map;
7069
7073
  const getTurn = (turnId) => {
@@ -7144,6 +7148,131 @@ var summarizeVoiceSessionReplay = async (options) => {
7144
7148
  turns: buildReplayTurns(events)
7145
7149
  };
7146
7150
  };
7151
+ var summarizeVoiceSessions = async (options = {}) => {
7152
+ const events = options.events ?? await options.store?.list() ?? [];
7153
+ const grouped = new Map;
7154
+ for (const event of events) {
7155
+ grouped.set(event.sessionId, [...grouped.get(event.sessionId) ?? [], event]);
7156
+ }
7157
+ const sessions = [...grouped.entries()].map(([sessionId, sessionEvents]) => {
7158
+ const sorted = filterVoiceTraceEvents(sessionEvents);
7159
+ const summary = buildVoiceTraceReplay(sorted, {
7160
+ evaluation: {
7161
+ requireAssistantReply: false,
7162
+ requireCompletedCall: false,
7163
+ requireTranscript: false,
7164
+ requireTurn: false
7165
+ }
7166
+ }).summary;
7167
+ const providerErrors = {};
7168
+ const providers = new Set;
7169
+ let latestOutcome;
7170
+ for (const event of sorted) {
7171
+ const provider = getString3(event.payload.provider);
7172
+ if (provider) {
7173
+ providers.add(provider);
7174
+ }
7175
+ if (event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string")) {
7176
+ increment2(providerErrors, provider ?? "unknown");
7177
+ }
7178
+ const outcome = getString3(event.payload.outcome);
7179
+ if (outcome) {
7180
+ latestOutcome = outcome;
7181
+ }
7182
+ }
7183
+ const item = {
7184
+ endedAt: summary.endedAt,
7185
+ errorCount: summary.errorCount,
7186
+ eventCount: summary.eventCount,
7187
+ latestOutcome,
7188
+ providerErrors,
7189
+ providers: [...providers].sort(),
7190
+ sessionId,
7191
+ startedAt: summary.startedAt,
7192
+ status: summary.failed ? "failed" : "healthy",
7193
+ transcriptCount: summary.transcriptCount,
7194
+ turnCount: summary.turnCount
7195
+ };
7196
+ const replayHref = options.replayHref === false ? "" : typeof options.replayHref === "function" ? options.replayHref(item) : `${options.replayHref ?? "/api/voice-sessions"}/${encodeURIComponent(sessionId)}/replay/htmx`;
7197
+ return {
7198
+ ...item,
7199
+ replayHref
7200
+ };
7201
+ });
7202
+ const search = options.q?.trim().toLowerCase();
7203
+ return sessions.filter((session) => {
7204
+ if (options.status && options.status !== "all" && session.status !== options.status) {
7205
+ return false;
7206
+ }
7207
+ if (options.provider && !session.providers.includes(options.provider)) {
7208
+ return false;
7209
+ }
7210
+ if (!search) {
7211
+ return true;
7212
+ }
7213
+ return [
7214
+ session.sessionId,
7215
+ session.latestOutcome,
7216
+ session.status,
7217
+ ...session.providers
7218
+ ].some((value) => value?.toLowerCase().includes(search));
7219
+ }).sort((left, right) => (right.endedAt ?? right.startedAt ?? 0) - (left.endedAt ?? left.startedAt ?? 0)).slice(0, options.limit ?? 50);
7220
+ };
7221
+ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
7222
+ '<div class="voice-sessions-list">',
7223
+ ...sessions.map((session) => [
7224
+ `<article class="voice-session-card ${escapeHtml6(session.status)}">`,
7225
+ '<div class="voice-session-card-header">',
7226
+ `<strong>${escapeHtml6(session.sessionId)}</strong>`,
7227
+ `<span>${escapeHtml6(session.status)}</span>`,
7228
+ "</div>",
7229
+ "<dl>",
7230
+ `<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
7231
+ `<div><dt>Turns</dt><dd>${String(session.turnCount)}</dd></div>`,
7232
+ `<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
7233
+ `<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
7234
+ "</dl>",
7235
+ session.latestOutcome ? `<p>Outcome: ${escapeHtml6(session.latestOutcome)}</p>` : "",
7236
+ session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml6).join(", ")}</p>` : "",
7237
+ session.replayHref ? `<p><a href="${escapeHtml6(session.replayHref)}">Open replay</a></p>` : "",
7238
+ "</article>"
7239
+ ].join("")),
7240
+ "</div>"
7241
+ ].join("");
7242
+ var createVoiceSessionsJSONHandler = (options = {}) => async ({ query }) => summarizeVoiceSessions({
7243
+ ...options,
7244
+ limit: typeof query?.limit === "string" ? Number(query.limit) : options.limit,
7245
+ provider: query?.provider ?? options.provider,
7246
+ q: query?.q ?? options.q,
7247
+ status: query?.status === "failed" || query?.status === "healthy" || query?.status === "all" ? query.status : options.status
7248
+ });
7249
+ var createVoiceSessionsHTMLHandler = (options = {}) => async ({ query }) => {
7250
+ const sessions = await summarizeVoiceSessions({
7251
+ ...options,
7252
+ limit: typeof query?.limit === "string" ? Number(query.limit) : options.limit,
7253
+ provider: query?.provider ?? options.provider,
7254
+ q: query?.q ?? options.q,
7255
+ status: query?.status === "failed" || query?.status === "healthy" || query?.status === "all" ? query.status : options.status
7256
+ });
7257
+ const body = await (options.render?.(sessions) ?? renderVoiceSessionsHTML(sessions));
7258
+ return new Response(body, {
7259
+ headers: {
7260
+ "Content-Type": "text/html; charset=utf-8",
7261
+ ...options.headers
7262
+ }
7263
+ });
7264
+ };
7265
+ var createVoiceSessionListRoutes = (options = {}) => {
7266
+ const path = options.path ?? "/api/voice-sessions";
7267
+ const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
7268
+ const routes = new Elysia4({
7269
+ name: options.name ?? "absolutejs-voice-session-list"
7270
+ }).get(path, createVoiceSessionsJSONHandler(options));
7271
+ if (htmlPath) {
7272
+ routes.get(htmlPath, createVoiceSessionsHTMLHandler(options));
7273
+ }
7274
+ return routes;
7275
+ };
7147
7276
  var createVoiceSessionReplayJSONHandler = (options) => async ({ params }) => summarizeVoiceSessionReplay({
7148
7277
  ...options,
7149
7278
  sessionId: params.sessionId ?? ""
@@ -10710,6 +10839,7 @@ export {
10710
10839
  transcodePCMToTwilioOutboundPayload,
10711
10840
  summarizeVoiceTraceSinkDeliveries,
10712
10841
  summarizeVoiceTrace,
10842
+ summarizeVoiceSessions,
10713
10843
  summarizeVoiceSessionReplay,
10714
10844
  summarizeVoiceProviderHealth,
10715
10845
  summarizeVoiceOpsTasks,
@@ -10736,6 +10866,7 @@ export {
10736
10866
  reopenVoiceOpsTask,
10737
10867
  renderVoiceTraceMarkdown,
10738
10868
  renderVoiceTraceHTML,
10869
+ renderVoiceSessionsHTML,
10739
10870
  renderVoiceProviderHealthHTML,
10740
10871
  renderVoiceCallReviewMarkdown,
10741
10872
  renderVoiceCallReviewHTML,
@@ -10777,10 +10908,13 @@ export {
10777
10908
  createVoiceTaskUpdatedEvent,
10778
10909
  createVoiceTaskSLABreachedEvent,
10779
10910
  createVoiceTaskCreatedEvent,
10911
+ createVoiceSessionsJSONHandler,
10912
+ createVoiceSessionsHTMLHandler,
10780
10913
  createVoiceSessionReplayRoutes,
10781
10914
  createVoiceSessionReplayJSONHandler,
10782
10915
  createVoiceSessionReplayHTMLHandler,
10783
10916
  createVoiceSessionRecord,
10917
+ createVoiceSessionListRoutes,
10784
10918
  createVoiceSession,
10785
10919
  createVoiceSTTRoutingCorrectionHandler,
10786
10920
  createVoiceSQLiteTraceSinkDeliveryStore,
@@ -28,6 +28,39 @@ export type VoiceSessionReplay = {
28
28
  }>;
29
29
  turns: VoiceSessionReplayTurn[];
30
30
  };
31
+ export type VoiceSessionListStatus = 'failed' | 'healthy';
32
+ export type VoiceSessionListItem = {
33
+ endedAt?: number;
34
+ errorCount: number;
35
+ eventCount: number;
36
+ latestOutcome?: string;
37
+ providerErrors: Record<string, number>;
38
+ providers: string[];
39
+ replayHref: string;
40
+ sessionId: string;
41
+ startedAt?: number;
42
+ status: VoiceSessionListStatus;
43
+ transcriptCount: number;
44
+ turnCount: number;
45
+ };
46
+ export type VoiceSessionListOptions = {
47
+ events?: StoredVoiceTraceEvent[];
48
+ limit?: number;
49
+ provider?: string;
50
+ q?: string;
51
+ replayHref?: false | string | ((session: Omit<VoiceSessionListItem, 'replayHref'>) => string);
52
+ status?: VoiceSessionListStatus | 'all';
53
+ store?: VoiceTraceEventStore;
54
+ };
55
+ export type VoiceSessionListHTMLHandlerOptions = VoiceSessionListOptions & {
56
+ headers?: HeadersInit;
57
+ render?: (sessions: VoiceSessionListItem[]) => string | Promise<string>;
58
+ };
59
+ export type VoiceSessionListRoutesOptions = VoiceSessionListHTMLHandlerOptions & {
60
+ htmlPath?: false | string;
61
+ name?: string;
62
+ path?: string;
63
+ };
31
64
  export type VoiceSessionReplayOptions = {
32
65
  evaluation?: VoiceTraceEvaluationOptions;
33
66
  events?: StoredVoiceTraceEvent[];
@@ -46,6 +79,54 @@ export type VoiceSessionReplayRoutesOptions = VoiceSessionReplayHTMLHandlerOptio
46
79
  path?: string;
47
80
  };
48
81
  export declare const summarizeVoiceSessionReplay: (options: VoiceSessionReplayOptions) => Promise<VoiceSessionReplay>;
82
+ export declare const summarizeVoiceSessions: (options?: VoiceSessionListOptions) => Promise<VoiceSessionListItem[]>;
83
+ export declare const renderVoiceSessionsHTML: (sessions: VoiceSessionListItem[]) => string;
84
+ export declare const createVoiceSessionsJSONHandler: (options?: VoiceSessionListOptions) => ({ query }: {
85
+ query?: Record<string, string | undefined>;
86
+ }) => Promise<VoiceSessionListItem[]>;
87
+ export declare const createVoiceSessionsHTMLHandler: (options?: VoiceSessionListHTMLHandlerOptions) => ({ query }: {
88
+ query?: Record<string, string | undefined>;
89
+ }) => Promise<Response>;
90
+ export declare const createVoiceSessionListRoutes: (options?: VoiceSessionListRoutesOptions) => Elysia<"", {
91
+ decorator: {};
92
+ store: {};
93
+ derive: {};
94
+ resolve: {};
95
+ }, {
96
+ typebox: {};
97
+ error: {};
98
+ }, {
99
+ schema: {};
100
+ standaloneSchema: {};
101
+ macro: {};
102
+ macroFn: {};
103
+ parser: {};
104
+ response: {};
105
+ }, {
106
+ [x: string]: {
107
+ get: {
108
+ body: unknown;
109
+ params: {};
110
+ query: unknown;
111
+ headers: unknown;
112
+ response: {
113
+ 200: VoiceSessionListItem[];
114
+ };
115
+ };
116
+ };
117
+ }, {
118
+ derive: {};
119
+ resolve: {};
120
+ schema: {};
121
+ standaloneSchema: {};
122
+ response: {};
123
+ }, {
124
+ derive: {};
125
+ resolve: {};
126
+ schema: {};
127
+ standaloneSchema: {};
128
+ response: {};
129
+ }>;
49
130
  export declare const createVoiceSessionReplayJSONHandler: (options: Omit<VoiceSessionReplayOptions, "sessionId">) => ({ params }: {
50
131
  params: Record<string, string | undefined>;
51
132
  }) => Promise<VoiceSessionReplay>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.22",
3
+ "version": "0.0.22-beta.23",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",