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

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,133 @@ 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
+ let errorCount = 0;
7171
+ for (const event of sorted) {
7172
+ const provider = getString3(event.payload.provider);
7173
+ if (provider) {
7174
+ providers.add(provider);
7175
+ }
7176
+ if (event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string")) {
7177
+ errorCount += 1;
7178
+ increment2(providerErrors, provider ?? "unknown");
7179
+ }
7180
+ const outcome = getString3(event.payload.outcome);
7181
+ if (outcome) {
7182
+ latestOutcome = outcome;
7183
+ }
7184
+ }
7185
+ const item = {
7186
+ endedAt: summary.endedAt,
7187
+ errorCount,
7188
+ eventCount: summary.eventCount,
7189
+ latestOutcome,
7190
+ providerErrors,
7191
+ providers: [...providers].sort(),
7192
+ sessionId,
7193
+ startedAt: summary.startedAt,
7194
+ status: errorCount > 0 ? "failed" : "healthy",
7195
+ transcriptCount: summary.transcriptCount,
7196
+ turnCount: summary.turnCount
7197
+ };
7198
+ const replayHref = options.replayHref === false ? "" : typeof options.replayHref === "function" ? options.replayHref(item) : `${options.replayHref ?? "/api/voice-sessions"}/${encodeURIComponent(sessionId)}/replay/htmx`;
7199
+ return {
7200
+ ...item,
7201
+ replayHref
7202
+ };
7203
+ });
7204
+ const search = options.q?.trim().toLowerCase();
7205
+ return sessions.filter((session) => {
7206
+ if (options.status && options.status !== "all" && session.status !== options.status) {
7207
+ return false;
7208
+ }
7209
+ if (options.provider && !session.providers.includes(options.provider)) {
7210
+ return false;
7211
+ }
7212
+ if (!search) {
7213
+ return true;
7214
+ }
7215
+ return [
7216
+ session.sessionId,
7217
+ session.latestOutcome,
7218
+ session.status,
7219
+ ...session.providers
7220
+ ].some((value) => value?.toLowerCase().includes(search));
7221
+ }).sort((left, right) => (right.endedAt ?? right.startedAt ?? 0) - (left.endedAt ?? left.startedAt ?? 0)).slice(0, options.limit ?? 50);
7222
+ };
7223
+ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
7224
+ '<div class="voice-sessions-list">',
7225
+ ...sessions.map((session) => [
7226
+ `<article class="voice-session-card ${escapeHtml6(session.status)}">`,
7227
+ '<div class="voice-session-card-header">',
7228
+ `<strong>${escapeHtml6(session.sessionId)}</strong>`,
7229
+ `<span>${escapeHtml6(session.status)}</span>`,
7230
+ "</div>",
7231
+ "<dl>",
7232
+ `<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
7233
+ `<div><dt>Turns</dt><dd>${String(session.turnCount)}</dd></div>`,
7234
+ `<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
7235
+ `<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
7236
+ "</dl>",
7237
+ session.latestOutcome ? `<p>Outcome: ${escapeHtml6(session.latestOutcome)}</p>` : "",
7238
+ session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml6).join(", ")}</p>` : "",
7239
+ session.replayHref ? `<p><a href="${escapeHtml6(session.replayHref)}">Open replay</a></p>` : "",
7240
+ "</article>"
7241
+ ].join("")),
7242
+ "</div>"
7243
+ ].join("");
7244
+ var createVoiceSessionsJSONHandler = (options = {}) => async ({ query }) => summarizeVoiceSessions({
7245
+ ...options,
7246
+ limit: typeof query?.limit === "string" ? Number(query.limit) : options.limit,
7247
+ provider: query?.provider ?? options.provider,
7248
+ q: query?.q ?? options.q,
7249
+ status: query?.status === "failed" || query?.status === "healthy" || query?.status === "all" ? query.status : options.status
7250
+ });
7251
+ var createVoiceSessionsHTMLHandler = (options = {}) => async ({ query }) => {
7252
+ const sessions = await summarizeVoiceSessions({
7253
+ ...options,
7254
+ limit: typeof query?.limit === "string" ? Number(query.limit) : options.limit,
7255
+ provider: query?.provider ?? options.provider,
7256
+ q: query?.q ?? options.q,
7257
+ status: query?.status === "failed" || query?.status === "healthy" || query?.status === "all" ? query.status : options.status
7258
+ });
7259
+ const body = await (options.render?.(sessions) ?? renderVoiceSessionsHTML(sessions));
7260
+ return new Response(body, {
7261
+ headers: {
7262
+ "Content-Type": "text/html; charset=utf-8",
7263
+ ...options.headers
7264
+ }
7265
+ });
7266
+ };
7267
+ var createVoiceSessionListRoutes = (options = {}) => {
7268
+ const path = options.path ?? "/api/voice-sessions";
7269
+ const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
7270
+ const routes = new Elysia4({
7271
+ name: options.name ?? "absolutejs-voice-session-list"
7272
+ }).get(path, createVoiceSessionsJSONHandler(options));
7273
+ if (htmlPath) {
7274
+ routes.get(htmlPath, createVoiceSessionsHTMLHandler(options));
7275
+ }
7276
+ return routes;
7277
+ };
7147
7278
  var createVoiceSessionReplayJSONHandler = (options) => async ({ params }) => summarizeVoiceSessionReplay({
7148
7279
  ...options,
7149
7280
  sessionId: params.sessionId ?? ""
@@ -10710,6 +10841,7 @@ export {
10710
10841
  transcodePCMToTwilioOutboundPayload,
10711
10842
  summarizeVoiceTraceSinkDeliveries,
10712
10843
  summarizeVoiceTrace,
10844
+ summarizeVoiceSessions,
10713
10845
  summarizeVoiceSessionReplay,
10714
10846
  summarizeVoiceProviderHealth,
10715
10847
  summarizeVoiceOpsTasks,
@@ -10736,6 +10868,7 @@ export {
10736
10868
  reopenVoiceOpsTask,
10737
10869
  renderVoiceTraceMarkdown,
10738
10870
  renderVoiceTraceHTML,
10871
+ renderVoiceSessionsHTML,
10739
10872
  renderVoiceProviderHealthHTML,
10740
10873
  renderVoiceCallReviewMarkdown,
10741
10874
  renderVoiceCallReviewHTML,
@@ -10777,10 +10910,13 @@ export {
10777
10910
  createVoiceTaskUpdatedEvent,
10778
10911
  createVoiceTaskSLABreachedEvent,
10779
10912
  createVoiceTaskCreatedEvent,
10913
+ createVoiceSessionsJSONHandler,
10914
+ createVoiceSessionsHTMLHandler,
10780
10915
  createVoiceSessionReplayRoutes,
10781
10916
  createVoiceSessionReplayJSONHandler,
10782
10917
  createVoiceSessionReplayHTMLHandler,
10783
10918
  createVoiceSessionRecord,
10919
+ createVoiceSessionListRoutes,
10784
10920
  createVoiceSession,
10785
10921
  createVoiceSTTRoutingCorrectionHandler,
10786
10922
  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.24",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",