nexus-ai-pro 0.9.0 → 1.0.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.
- package/API_STABILITY.md +23 -0
- package/CHANGELOG.md +24 -0
- package/README.md +215 -1
- package/ROADMAP.md +35 -2
- package/SECURITY.md +31 -0
- package/dist/realtime/conversation.d.ts +19 -0
- package/dist/realtime/conversation.js +406 -0
- package/dist/realtime/errors.d.ts +25 -0
- package/dist/realtime/errors.js +74 -0
- package/dist/realtime/events.d.ts +10 -0
- package/dist/realtime/events.js +39 -0
- package/dist/realtime/id.d.ts +2 -0
- package/dist/realtime/id.js +8 -0
- package/dist/realtime/index.d.ts +22 -0
- package/dist/realtime/index.js +12 -0
- package/dist/realtime/mock.transport.d.ts +55 -0
- package/dist/realtime/mock.transport.js +169 -0
- package/dist/realtime/openai-events.d.ts +5 -0
- package/dist/realtime/openai-events.js +404 -0
- package/dist/realtime/openai-server.d.ts +33 -0
- package/dist/realtime/openai-server.js +138 -0
- package/dist/realtime/openai-webrtc.transport.d.ts +153 -0
- package/dist/realtime/openai-webrtc.transport.js +871 -0
- package/dist/realtime/openai-websocket.transport.d.ts +65 -0
- package/dist/realtime/openai-websocket.transport.js +420 -0
- package/dist/realtime/provider.d.ts +37 -0
- package/dist/realtime/provider.js +59 -0
- package/dist/realtime/session.d.ts +110 -0
- package/dist/realtime/session.js +962 -0
- package/dist/realtime/tools.d.ts +47 -0
- package/dist/realtime/tools.js +358 -0
- package/dist/realtime/transport-utils.d.ts +55 -0
- package/dist/realtime/transport-utils.js +240 -0
- package/dist/realtime/types.d.ts +544 -0
- package/dist/realtime/types.js +1 -0
- package/package.json +36 -2
package/API_STABILITY.md
CHANGED
|
@@ -25,6 +25,29 @@ Source files, `dist` file paths, examples, test helpers that are not exported, a
|
|
|
25
25
|
|
|
26
26
|
The package is ESM-only and supports Node.js 22 or newer. CommonJS `require()` and deep imports into `dist` or `src` are not supported.
|
|
27
27
|
|
|
28
|
+
## Realtime API stage
|
|
29
|
+
|
|
30
|
+
The opt-in `nexus-ai-pro/realtime` family is public in the current development line. This includes the
|
|
31
|
+
explicit `realtime/session`, `realtime/tools`, `realtime/conversation`, `realtime/openai-webrtc`,
|
|
32
|
+
`realtime/openai-websocket`, `realtime/openai-server`, and `realtime/mock` subpaths. Its exported symbols,
|
|
33
|
+
normalized event names, configuration fields, and conversation/export shapes follow the same patch
|
|
34
|
+
compatibility rules as the rest of the package.
|
|
35
|
+
|
|
36
|
+
The realtime surface is still pre-1.0. Provider-specific wire events returned by `openai-events`, values
|
|
37
|
+
under `raw`, SDP/ICE behavior, and upstream model or voice availability are controlled by the provider
|
|
38
|
+
and are not normalized compatibility guarantees. New normalized event variants or optional metrics may
|
|
39
|
+
be added in a minor release. A necessary incompatible change during 0.x will be documented in a minor
|
|
40
|
+
release with migration guidance.
|
|
41
|
+
|
|
42
|
+
Platform interfaces for WebRTC and WebSocket are structural so applications can inject browser, server,
|
|
43
|
+
mobile, or test adapters without a framework dependency. A documented structural member is public;
|
|
44
|
+
private transport internals and unexported protocol helpers are not.
|
|
45
|
+
|
|
46
|
+
`VoiceSession` and `RealtimeSession` are independent public APIs. `VoiceSession` remains a batch,
|
|
47
|
+
turn-oriented transcription/completion/speech workflow. `RealtimeSession` owns a persistent transport,
|
|
48
|
+
live events, interruption, and normalized realtime conversation state. Neither is a compatibility alias
|
|
49
|
+
for the other.
|
|
50
|
+
|
|
28
51
|
## Deprecation process
|
|
29
52
|
|
|
30
53
|
Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. During the 0.x series, removal will not normally occur earlier than the next minor release. After 1.0, removals will be reserved for major releases, except when an urgent security issue requires otherwise.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- An opt-in realtime package family with `realtime`, `realtime/session`, `realtime/tools`,
|
|
10
|
+
`realtime/conversation`, provider transport, server-helper, and deterministic mock subpaths. Realtime
|
|
11
|
+
code is not added to the root or batch voice import graph.
|
|
12
|
+
- Framework-independent OpenAI WebRTC and WebSocket transports with injectable platform interfaces,
|
|
13
|
+
secure server-owned WebRTC negotiation, microphone and remote-audio handling, cancellation, and
|
|
14
|
+
reconnect support.
|
|
15
|
+
- A persistent `RealtimeSession` and higher-level `createRealtimeAgent()` API with normalized and raw
|
|
16
|
+
events, barge-in, response cancellation, unheard-audio truncation, typed tools, confirmations,
|
|
17
|
+
idempotency keys, bounded execution, and transcript/usage state.
|
|
18
|
+
- Immutable normalized conversations with JSON, OpenAI-event, text, and analytics exports, plus
|
|
19
|
+
connection, first-audio, turn, tool, interruption, reconnect, token, audio-duration, and cost metrics.
|
|
20
|
+
- Realtime security controls for tool allowlists, transcript retention, PII hooks, and maximum session
|
|
21
|
+
and audio duration, with structural OpenTelemetry-compatible tracing and metrics hooks.
|
|
22
|
+
- Opt-in safe-tool result caching, active-connection/audio-duration metrics, and an application-owned
|
|
23
|
+
realtime cost estimator hook without bundled pricing tables.
|
|
24
|
+
- A network-free realtime scheduling example using the deterministic mock transport.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Realtime connections are documented as a separate opt-in operation family. Existing `VoiceSession`
|
|
29
|
+
behavior remains the batch transcription -> completion/tools -> speech workflow.
|
|
30
|
+
|
|
7
31
|
## [0.9.0] - 2026-07-15
|
|
8
32
|
|
|
9
33
|
### Added
|
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ npm install nexus-ai-pro
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Requires Node.js 22 or newer. `nexus-ai-pro` is ESM-only: use `import` from an ES module rather than CommonJS `require()`.
|
|
19
|
+
The main Nexus pipeline is Node-oriented; the isolated realtime subpaths use portable structural
|
|
20
|
+
interfaces and can be bundled for modern browsers.
|
|
19
21
|
|
|
20
22
|
Install only the provider SDKs your app uses. The OpenAI SDK also powers OpenAI-compatible adapters such as OpenRouter, Groq, Mistral, DeepSeek, Azure OpenAI, LM Studio, and llama.cpp.
|
|
21
23
|
|
|
@@ -24,6 +26,8 @@ npm install openai @anthropic-ai/sdk ollama
|
|
|
24
26
|
```
|
|
25
27
|
|
|
26
28
|
The Google Gemini provider uses Node.js `fetch`, so it does not need an extra SDK.
|
|
29
|
+
Realtime transports use injected or platform WebRTC, WebSocket, and `fetch` interfaces and do not
|
|
30
|
+
require the OpenAI SDK.
|
|
27
31
|
|
|
28
32
|
## Quick Start
|
|
29
33
|
|
|
@@ -77,6 +81,7 @@ You can still pass a plain `NexusAIConfig` to `new NexusAI(...)` when you want f
|
|
|
77
81
|
- estimate tokens and cost before provider calls
|
|
78
82
|
- cache exact or semantically similar prompts
|
|
79
83
|
- add tools, agents, RAG context, evals, batch jobs, and queues
|
|
84
|
+
- build persistent realtime voice agents with interruption, live tools, and normalized conversation state
|
|
80
85
|
- keep TypeScript types around every request and response
|
|
81
86
|
|
|
82
87
|
## CLI
|
|
@@ -469,7 +474,210 @@ ai.registerVoiceProvider('private-voice', {
|
|
|
469
474
|
});
|
|
470
475
|
```
|
|
471
476
|
|
|
472
|
-
|
|
477
|
+
`VoiceSession` remains the batch-oriented path: each turn is transcription -> completion/tools -> optional
|
|
478
|
+
speech. For a persistent connection with live microphone audio, streamed remote audio, barge-in, and
|
|
479
|
+
provider events, use the separate realtime entry points below. Neither API replaces the other.
|
|
480
|
+
|
|
481
|
+
## Realtime Voice Agents
|
|
482
|
+
|
|
483
|
+
Realtime is opt-in and is not re-exported from the package root or from `nexus-ai-pro/voice`:
|
|
484
|
+
|
|
485
|
+
```ts
|
|
486
|
+
import {
|
|
487
|
+
createRealtimeAgent,
|
|
488
|
+
defineTool,
|
|
489
|
+
OpenAIRealtimeProvider,
|
|
490
|
+
} from 'nexus-ai-pro/realtime';
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
The framework-independent session normalizes connection, speech, transcript, response, tool,
|
|
494
|
+
interruption, error, conversation, and metrics events while retaining provider events when configured.
|
|
495
|
+
The browser and server transports use structural platform interfaces, so the realtime core does not
|
|
496
|
+
require React, a DOM shim, or an OpenAI SDK.
|
|
497
|
+
|
|
498
|
+
### Browser WebRTC
|
|
499
|
+
|
|
500
|
+
Give the browser a same-origin `sessionEndpoint`; never put a permanent provider API key in browser
|
|
501
|
+
code. The endpoint receives the SDP offer and returns the SDP answer while your server owns the
|
|
502
|
+
provider key and authoritative session configuration.
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
type BookingInput = { startTime: string; endTime: string; timezone: string };
|
|
506
|
+
|
|
507
|
+
const createBooking = defineTool({
|
|
508
|
+
name: 'create_calendar_booking',
|
|
509
|
+
description: 'Create a calendar booking after the user confirms it.',
|
|
510
|
+
parameters: {
|
|
511
|
+
type: 'object',
|
|
512
|
+
properties: {
|
|
513
|
+
startTime: { type: 'string', format: 'date-time' },
|
|
514
|
+
endTime: { type: 'string', format: 'date-time' },
|
|
515
|
+
timezone: { type: 'string' },
|
|
516
|
+
},
|
|
517
|
+
required: ['startTime', 'endTime', 'timezone'],
|
|
518
|
+
additionalProperties: false,
|
|
519
|
+
},
|
|
520
|
+
requiresConfirmation: true,
|
|
521
|
+
execute: async (input: BookingInput, context) =>
|
|
522
|
+
calendar.create(input, { idempotencyKey: context.idempotencyKey, signal: context.signal }),
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
const provider = new OpenAIRealtimeProvider({
|
|
526
|
+
sessionEndpoint: '/api/realtime/session',
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const agent = createRealtimeAgent({
|
|
530
|
+
provider,
|
|
531
|
+
model: 'gpt-realtime',
|
|
532
|
+
modalities: ['audio', 'text'],
|
|
533
|
+
instructions: 'Check availability before booking. Never write without confirmation.',
|
|
534
|
+
tools: [createBooking],
|
|
535
|
+
voice: {
|
|
536
|
+
transport: 'webrtc',
|
|
537
|
+
voice: 'alloy',
|
|
538
|
+
turnDetection: { type: 'server_vad', silenceDurationMs: 450 },
|
|
539
|
+
interruption: {
|
|
540
|
+
enabled: true,
|
|
541
|
+
cancelResponse: true,
|
|
542
|
+
truncateUnheardAudio: true,
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
toolExecution: { mode: 'automatic', timeoutMs: 8_000, maxParallelCalls: 3 },
|
|
546
|
+
reconnect: { enabled: true, maxAttempts: 3, initialDelayMs: 500 },
|
|
547
|
+
conversation: { captureTranscripts: true, captureToolCalls: true, maxRawEvents: 2_000 },
|
|
548
|
+
security: {
|
|
549
|
+
maxSessionDurationMs: 30 * 60_000,
|
|
550
|
+
maxAudioDurationMs: 20 * 60_000,
|
|
551
|
+
toolAllowlist: ['create_calendar_booking'],
|
|
552
|
+
},
|
|
553
|
+
telemetry: {
|
|
554
|
+
includeTranscripts: false,
|
|
555
|
+
estimateCost: (usage) => pricing.estimateRealtime(usage),
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
agent.on('tool.confirmation.required', ({ call }) => {
|
|
560
|
+
const approved = window.confirm(`Create booking requested by ${call.name}?`);
|
|
561
|
+
agent.confirmTool(call.callId, approved);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
agent.on('metrics', (metrics) => {
|
|
565
|
+
console.log('speech end -> first audio', metrics.speechEndToFirstAudioMs);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
const audioElement = document.querySelector<HTMLAudioElement>('#assistant-audio')!;
|
|
569
|
+
await agent.connect({ microphone: true, audioElement });
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
WebRTC attaches the selected microphone track directly to the peer connection and assigns the remote
|
|
573
|
+
media stream to `audioElement`. Browsers still require HTTPS (or localhost), user permission, and often
|
|
574
|
+
a user gesture before audio playback. `sendAudio()` is intended for transports that accept binary
|
|
575
|
+
chunks; normal WebRTC microphone use should stay on the media track.
|
|
576
|
+
|
|
577
|
+
WebRTC first-audio timing uses the provider's `output_audio_buffer.started` event. WebSocket playback
|
|
578
|
+
remains application-owned, so its player must call `markAudioPlayed()` as described below.
|
|
579
|
+
|
|
580
|
+
A minimal server endpoint can use the focused server helper:
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
import { createOpenAIRealtimeCall } from 'nexus-ai-pro/realtime/openai-server';
|
|
584
|
+
|
|
585
|
+
export async function POST(request: Request) {
|
|
586
|
+
const answerSdp = await createOpenAIRealtimeCall(await request.text(), {
|
|
587
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
588
|
+
model: 'gpt-realtime',
|
|
589
|
+
session: {
|
|
590
|
+
instructions: 'You are a concise scheduling assistant.',
|
|
591
|
+
max_output_tokens: 1_024,
|
|
592
|
+
},
|
|
593
|
+
}, request.signal);
|
|
594
|
+
|
|
595
|
+
return new Response(answerSdp, { headers: { 'content-type': 'application/sdp' } });
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Authenticate that endpoint, authorize the tenant and requested tools, apply origin/CSRF and rate-limit
|
|
600
|
+
controls, and keep its accepted session options allowlisted. The client must not choose arbitrary tools,
|
|
601
|
+
instructions, limits, or credentials.
|
|
602
|
+
|
|
603
|
+
### Server WebSocket
|
|
604
|
+
|
|
605
|
+
Server-to-server agents can inject their WebSocket implementation and keep provider authorization in
|
|
606
|
+
request headers. The package deliberately does not force a WebSocket dependency:
|
|
607
|
+
|
|
608
|
+
```ts
|
|
609
|
+
import {
|
|
610
|
+
OpenAIWebSocketTransport,
|
|
611
|
+
type OpenAIWebSocketFactory,
|
|
612
|
+
} from 'nexus-ai-pro/realtime/openai-websocket';
|
|
613
|
+
import { createRealtimeSession } from 'nexus-ai-pro/realtime/session';
|
|
614
|
+
|
|
615
|
+
const webSocketFactory: OpenAIWebSocketFactory = (url, { headers, protocols }) =>
|
|
616
|
+
serverWebSocketClient.connect(url, { headers, protocols });
|
|
617
|
+
|
|
618
|
+
const session = createRealtimeSession({
|
|
619
|
+
provider: 'openai',
|
|
620
|
+
model: 'gpt-realtime',
|
|
621
|
+
transport: new OpenAIWebSocketTransport({
|
|
622
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
623
|
+
webSocketFactory,
|
|
624
|
+
}),
|
|
625
|
+
modalities: ['audio', 'text'],
|
|
626
|
+
reconnect: { enabled: true, maxAttempts: 3 },
|
|
627
|
+
connection: {
|
|
628
|
+
remoteAudioSink: (audio) => audioPlayer.enqueue(audio.data),
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
await session.connect();
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
When an application plays WebSocket audio itself, call `session.markAudioPlayed()` when the first bytes
|
|
636
|
+
actually reach playback. That makes the primary latency metric—user speech ended to first assistant
|
|
637
|
+
audio played—represent user experience rather than network receipt.
|
|
638
|
+
|
|
639
|
+
The global browser WebSocket path accepts only an ephemeral token. Supplying a permanent API key to a
|
|
640
|
+
browser adapter is rejected; use an injected server factory or an ephemeral-token provider instead.
|
|
641
|
+
|
|
642
|
+
### Interruption, tools, state, and exports
|
|
643
|
+
|
|
644
|
+
With interruption enabled, a `speech.started` event can stop local playback, cancel the active response,
|
|
645
|
+
and, for WebSocket playback with a known position, truncate unheard assistant audio. Applications can
|
|
646
|
+
also call `session.interrupt('manual')`.
|
|
647
|
+
|
|
648
|
+
Realtime tools support structural `parse`, `safeParse`, or `validate` schemas, execution timeouts,
|
|
649
|
+
bounded parallelism, safe-tool retries, duplicate call suppression, idempotency keys, allowlists, and
|
|
650
|
+
write confirmation. A tool marked `safe: true` can also opt into cross-call caching with `cache: true`
|
|
651
|
+
or `{ ttlMs, key }` when `toolExecution.cache` supplies a structural cache adapter; cache failures are
|
|
652
|
+
ignored unless `cacheFailureMode: 'fail'` is configured. `requiresConfirmation` does not replace server
|
|
653
|
+
authorization: re-check identity, permissions, business invariants, and idempotency in the tool
|
|
654
|
+
implementation.
|
|
655
|
+
|
|
656
|
+
Session state is available as immutable snapshots and four export formats:
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
const json = session.export('json');
|
|
660
|
+
const providerEvents = session.export('openai-events');
|
|
661
|
+
const transcript = session.export('text');
|
|
662
|
+
const analytics = session.export('analytics');
|
|
663
|
+
|
|
664
|
+
session.on('conversation.updated', (snapshot) => conversationStore.save(snapshot));
|
|
665
|
+
session.on('error', (error) => telemetry.recordException(error));
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
`json` contains normalized user/assistant speech, text messages, tool calls/results, interruptions,
|
|
669
|
+
errors, and latency/usage metrics. `openai-events` returns retained raw provider events; it is useful for
|
|
670
|
+
diagnostics and replay but follows the upstream provider schema. Disable transcript/raw-event retention
|
|
671
|
+
for sensitive workloads. `piiHook` redacts normalized conversation/event text, but deliberately does not
|
|
672
|
+
rewrite raw provider exports. Telemetry transcript fields are redacted by default; `includeTranscripts`
|
|
673
|
+
must be explicitly enabled. `estimateCost` accepts an application-owned pricing callback so model price
|
|
674
|
+
changes are not hard-coded into the package. The built-in limits and telemetry hooks are local controls,
|
|
675
|
+
not a durable session store, distributed rate limiter, or authorization system.
|
|
676
|
+
|
|
677
|
+
Focused public imports are available for `realtime`, `realtime/session`, `realtime/tools`,
|
|
678
|
+
`realtime/conversation`, `realtime/openai-webrtc`, `realtime/openai-websocket`,
|
|
679
|
+
`realtime/openai-server`, and `realtime/mock`. The deterministic mock is suitable for tests and demos;
|
|
680
|
+
see [`examples/realtime-scheduling.ts`](./examples/realtime-scheduling.ts).
|
|
473
681
|
|
|
474
682
|
## Phone Number Telephony
|
|
475
683
|
|
|
@@ -598,6 +806,7 @@ Available runtime helpers:
|
|
|
598
806
|
- Redis and BullMQ queue adapters
|
|
599
807
|
- eval runner with quality, operations, RAG, safety metrics, and optional LLM-as-judge scoring
|
|
600
808
|
- voice sessions for multi-turn calls with conditional task prompts and app tools
|
|
809
|
+
- realtime sessions with WebRTC/WebSocket transports, interruption, tools, metrics, and conversation exports
|
|
601
810
|
- workflow templates for RAG answers, extraction, classification, comparison, support, sales, legal review, and code review
|
|
602
811
|
|
|
603
812
|
Use a plain assertion for small evals, or add an LLM judge when a rubric is more useful than exact matching:
|
|
@@ -683,6 +892,8 @@ import { ContextWindowManager } from 'nexus-ai-pro/context';
|
|
|
683
892
|
import { guardrailPolicy } from 'nexus-ai-pro/security';
|
|
684
893
|
import { RedisCacheAdapter } from 'nexus-ai-pro/cache';
|
|
685
894
|
import { DeepSeekProvider } from 'nexus-ai-pro/providers/deepseek';
|
|
895
|
+
import { createRealtimeSession } from 'nexus-ai-pro/realtime/session';
|
|
896
|
+
import { OpenAIWebRTCTransport } from 'nexus-ai-pro/realtime/openai-webrtc';
|
|
686
897
|
```
|
|
687
898
|
|
|
688
899
|
Provider SDKs are optional peer dependencies. The package is ESM-only, supports Node.js 22+, and is marked with `sideEffects: false`. Only the entry points listed in the package export map are public; deep imports into `dist` or `src` are unsupported.
|
|
@@ -699,6 +910,8 @@ npm run example:basic
|
|
|
699
910
|
npm run example:security
|
|
700
911
|
npm run example:optimizer
|
|
701
912
|
npm run example:agent
|
|
913
|
+
npm run build && npx tsx examples/realtime-scheduling.ts
|
|
914
|
+
npx tsc -p tsconfig.examples.browser.json
|
|
702
915
|
```
|
|
703
916
|
|
|
704
917
|
Repository example files cover:
|
|
@@ -710,6 +923,7 @@ Repository example files cover:
|
|
|
710
923
|
- classifier calibration
|
|
711
924
|
- domain workflows
|
|
712
925
|
- custom providers
|
|
926
|
+
- deterministic realtime scheduling with tools, confirmation, metrics, and conversation exports
|
|
713
927
|
- import examples under `examples/exports`
|
|
714
928
|
|
|
715
929
|
## Test Commands
|
package/ROADMAP.md
CHANGED
|
@@ -3,6 +3,39 @@
|
|
|
3
3
|
This roadmap is a design proposal, not a compatibility promise. Stable and experimental
|
|
4
4
|
surfaces are defined in [API_STABILITY.md](./API_STABILITY.md).
|
|
5
5
|
|
|
6
|
+
## Delivered in the current development line: realtime voice foundation
|
|
7
|
+
|
|
8
|
+
The first native realtime milestone is implemented as an opt-in package family rather than as another
|
|
9
|
+
`complete()` mode or an eager dependency of `NexusAI`:
|
|
10
|
+
|
|
11
|
+
- persistent provider-neutral `RealtimeSession` and higher-level `createRealtimeAgent()` APIs;
|
|
12
|
+
- OpenAI WebRTC for browser microphone/remote-media connections and WebSocket for server agents;
|
|
13
|
+
- structural, injectable browser/server interfaces plus a deterministic mock transport;
|
|
14
|
+
- normalized speech, transcript, text, response, tool, interruption, error, conversation, metrics, and
|
|
15
|
+
retained raw-provider events;
|
|
16
|
+
- barge-in, response cancellation, optional unheard-audio truncation, reconnect backoff, and cancellation;
|
|
17
|
+
- typed/validated tools with automatic or manual execution, confirmation, timeouts, bounded parallelism,
|
|
18
|
+
safe retries, opt-in read caching, duplicate suppression, allowlists, and idempotency keys;
|
|
19
|
+
- normalized JSON conversation state and OpenAI-event, text, and analytics exports;
|
|
20
|
+
- server-owned SDP/client-secret helpers, retention controls, PII hooks, session/audio limits, and
|
|
21
|
+
OpenTelemetry-compatible hooks.
|
|
22
|
+
|
|
23
|
+
Current limitations are intentional and should remain visible:
|
|
24
|
+
|
|
25
|
+
- OpenAI is the only native realtime provider; provider-neutral contracts have not yet been validated
|
|
26
|
+
against a second wire protocol.
|
|
27
|
+
- The package supplies framework-independent transports, not React hooks, UI/audio-player components,
|
|
28
|
+
mobile SDK wrappers, SIP, or a telephone gateway.
|
|
29
|
+
- WebRTC requires application-owned HTTPS session negotiation and browser permission/autoplay handling.
|
|
30
|
+
WebSocket consumers are responsible for audio capture/playback and for injecting a server socket
|
|
31
|
+
implementation when authenticated headers are required.
|
|
32
|
+
- Conversation snapshots and exports are in memory. Durable storage, distributed deduplication,
|
|
33
|
+
cross-process session recovery, and provider-state replay remain application responsibilities.
|
|
34
|
+
- Compatibility/load validation across Safari, iOS Safari, Firefox, Android Chrome, long conversations,
|
|
35
|
+
and large concurrent session counts is still required before making broad production-scale claims.
|
|
36
|
+
- Realtime video tracks, SIP transports, durable cost accounting beyond the configurable local estimate,
|
|
37
|
+
and a published latency benchmark methodology remain follow-on work.
|
|
38
|
+
|
|
6
39
|
## Recommended next release: first-class image generation
|
|
7
40
|
|
|
8
41
|
Image generation should be a separate operation family rather than another variation of
|
|
@@ -164,7 +197,8 @@ code should only perform the provider call and normalize its result.
|
|
|
164
197
|
|
|
165
198
|
1. True mixed text/asset outputs and tool results that pass asset references without base64 JSON.
|
|
166
199
|
2. OCR, captioning, visual question answering, image embeddings, and visual moderation.
|
|
167
|
-
3. Realtime
|
|
200
|
+
3. Realtime follow-ons: a second provider, SIP/video transports, durable recovery, browser compatibility
|
|
201
|
+
automation, and published latency/load benchmarks. The native voice foundation is delivered above.
|
|
168
202
|
4. Video generation through the same asynchronous operation, job, and asset contracts.
|
|
169
203
|
5. Media search and RAG using image embeddings.
|
|
170
204
|
6. MCP client/server adapters with asset and tool interoperability.
|
|
@@ -185,4 +219,3 @@ code should only perform the provider call and normalize its result.
|
|
|
185
219
|
6. Add true multimodal outputs and media evaluation.
|
|
186
220
|
7. Promote the media API only after packed-package tests, provider conformance, and image evals
|
|
187
221
|
cover the supported capability matrix.
|
|
188
|
-
|
package/SECURITY.md
CHANGED
|
@@ -13,3 +13,34 @@ Include the affected version, impact, a minimal reproduction, and any suggested
|
|
|
13
13
|
## Scope and safe operation
|
|
14
14
|
|
|
15
15
|
Nexus guardrails reduce common AI application risks, but they are not a complete authorization or security boundary. Applications remain responsible for access control, provider-side safety controls, tool authorization, network egress policy, secret storage, and review of high-impact model actions.
|
|
16
|
+
|
|
17
|
+
## Realtime voice security
|
|
18
|
+
|
|
19
|
+
- Never ship a permanent provider API key to a browser or mobile client. For WebRTC, configure a
|
|
20
|
+
same-origin `sessionEndpoint` that performs provider negotiation or returns a short-lived client
|
|
21
|
+
secret. For WebSocket API-key authentication, run the transport on a trusted server and inject a
|
|
22
|
+
WebSocket factory that can set authorization headers.
|
|
23
|
+
- Authenticate and authorize the session endpoint before contacting the provider. Restrict origins,
|
|
24
|
+
apply CSRF and rate-limit controls, cap request size and duration, and derive the model, tools,
|
|
25
|
+
instructions, retention, and safety settings from trusted server policy rather than client input.
|
|
26
|
+
- Treat microphone permission, remote audio, provider data-channel events, transcripts, and raw events
|
|
27
|
+
as sensitive data. Request microphone access only after a user action, stop owned tracks on cleanup,
|
|
28
|
+
minimize retention, and avoid putting transcripts or audio in logs and telemetry by default.
|
|
29
|
+
- Use `security.maxSessionDurationMs`, `security.maxAudioDurationMs`, `security.toolAllowlist`,
|
|
30
|
+
`security.retainTranscripts`, `security.piiHook`, and bounded raw-event retention as defense-in-depth.
|
|
31
|
+
These process-local controls do not replace tenant quotas, durable retention enforcement, or a DLP
|
|
32
|
+
system.
|
|
33
|
+
- A realtime tool confirmation is a user-experience checkpoint, not authorization. Every read and write
|
|
34
|
+
tool must enforce tenant access and validate arguments server-side. Writes should require explicit
|
|
35
|
+
confirmation, use the supplied idempotency key, and remain safe under retries and duplicate provider
|
|
36
|
+
events.
|
|
37
|
+
- Redact API keys, ephemeral tokens, SDP diagnostics, tool arguments/results, and PII from errors and
|
|
38
|
+
telemetry. Realtime telemetry transcript fields are redacted by default; set
|
|
39
|
+
`telemetry.includeTranscripts: true` only when the destination and retention policy are explicitly
|
|
40
|
+
approved. `piiHook` applies to normalized events and conversation state, not retained raw events.
|
|
41
|
+
- Reconnects can repeat provider events or tool requests and do not guarantee restoration of remote
|
|
42
|
+
conversation state. Keep write tools idempotent, deduplicate by call ID/idempotency key, and surface
|
|
43
|
+
recovery failures to the user.
|
|
44
|
+
- Browser WebRTC still requires a backend security boundary. A direct media path reduces latency; it
|
|
45
|
+
does not remove the need for server-owned credentials, session policy, tool authorization, abuse
|
|
46
|
+
monitoring, and provider-side safety controls.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type RealtimeIdFactory } from './id.js';
|
|
2
|
+
import type { ConversationMetrics, RealtimeAnalyticsExport, RealtimeConversation, RealtimeConversationExportFormat, RealtimeConversationOptions, RealtimeEvent, RealtimeProviderName, RealtimeServerEvent } from './types.js';
|
|
3
|
+
export interface CreateRealtimeConversationOptions {
|
|
4
|
+
id?: string;
|
|
5
|
+
provider: RealtimeProviderName;
|
|
6
|
+
model: string;
|
|
7
|
+
startedAt?: string;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
idFactory?: RealtimeIdFactory;
|
|
10
|
+
}
|
|
11
|
+
export interface RealtimeConversationReducerOptions extends RealtimeConversationOptions {
|
|
12
|
+
idFactory?: RealtimeIdFactory;
|
|
13
|
+
}
|
|
14
|
+
export declare function createConversationMetrics(): ConversationMetrics;
|
|
15
|
+
export declare function createRealtimeConversation(options: CreateRealtimeConversationOptions): RealtimeConversation;
|
|
16
|
+
export declare function reduceRealtimeConversation(conversation: RealtimeConversation, event: RealtimeEvent, options?: RealtimeConversationReducerOptions): RealtimeConversation;
|
|
17
|
+
export declare function withConversationMetrics(conversation: RealtimeConversation, metrics: Partial<ConversationMetrics>): RealtimeConversation;
|
|
18
|
+
export declare function snapshotRealtimeConversation(conversation: RealtimeConversation): RealtimeConversation;
|
|
19
|
+
export declare function exportRealtimeConversation(conversation: RealtimeConversation, format: RealtimeConversationExportFormat, rawEvents?: RealtimeServerEvent[]): RealtimeConversation | RealtimeServerEvent[] | string | RealtimeAnalyticsExport;
|