dsh-realtime-audio-ws 0.1.0 → 0.1.2

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.
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * The browser face: microphone in, speaker out, over the host route.
3
4
  *
@@ -22,25 +23,33 @@
22
23
  * worse. That is a deliberate, disclosed trade: swap it for a worklet once the app page's `script-src` has
23
24
  * actually been read, rather than shipping a capture path that a policy might silently kill.
24
25
  */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.INJECTED_KEY = exports.GLOBAL_KEY = exports.MAX_BACKLOG_SECONDS = exports.CAPTURE_BUFFER = exports.SAMPLE_RATE = exports.DEFAULT_PATH = exports.inject = exports.name = void 0;
28
+ exports.socketUrl = socketUrl;
29
+ exports.pcm16FromFloat32 = pcm16FromFloat32;
30
+ exports.float32FromPcm16 = float32FromPcm16;
31
+ exports.defaultDeps = defaultDeps;
32
+ exports.createAudioClient = createAudioClient;
33
+ exports.apply = apply;
25
34
  /** Browser-side plugin name. */
26
- export const name = 'realtime-audio-client';
35
+ exports.name = 'realtime-audio-client';
27
36
  /**
28
37
  * No client services. The socket, the device and the audio graph are all this plugin's own, so it injects
29
38
  * nothing — and a client face that needs nothing cannot be broken by another plugin's absence.
30
39
  */
31
- export const inject = [];
40
+ exports.inject = [];
32
41
  /** Where the host route lives. Duplicated from the host package because a client face cannot import it. */
33
- export const DEFAULT_PATH = '/dsh-realtime/audio';
42
+ exports.DEFAULT_PATH = '/dsh-realtime/audio';
34
43
  /** The session's declared rate. The graph is opened at this rate or the client refuses to run. */
35
- export const SAMPLE_RATE = 24_000;
44
+ exports.SAMPLE_RATE = 24_000;
36
45
  /** Capture chunk in samples. 1024 at 24 kHz is ~43 ms: the 2^10 floor the API allows, so latency is lowest. */
37
- export const CAPTURE_BUFFER = 1024;
46
+ exports.CAPTURE_BUFFER = 1024;
38
47
  /** Backlog past which incoming speech is dropped rather than queued — the same rule the host carries. */
39
- export const MAX_BACKLOG_SECONDS = 0.5;
48
+ exports.MAX_BACKLOG_SECONDS = 0.5;
40
49
  /** Global the client publishes itself on. There is no UI surface yet; this is how it is started. */
41
- export const GLOBAL_KEY = '__dshRealtimeAudio';
50
+ exports.GLOBAL_KEY = '__dshRealtimeAudio';
42
51
  /** Optional host-injected settings. A future index-injection row may set the path; the default matches. */
43
- export const INJECTED_KEY = '__DSH_REALTIME_AUDIO__';
52
+ exports.INJECTED_KEY = '__DSH_REALTIME_AUDIO__';
44
53
  // ---- pure helpers --------------------------------------------------------------------------------------
45
54
  /**
46
55
  * The socket URL for the host route.
@@ -49,7 +58,7 @@ export const INJECTED_KEY = '__DSH_REALTIME_AUDIO__';
49
58
  * @param path - the route pathname.
50
59
  * @returns the URL, or undefined when this page cannot host a socket at all.
51
60
  */
52
- export function socketUrl(location, path) {
61
+ function socketUrl(location, path) {
53
62
  if (location === undefined || location.host === '')
54
63
  return undefined;
55
64
  // An https page may not open a ws:// socket — the browser blocks it as mixed content — so the scheme has
@@ -67,7 +76,7 @@ export function socketUrl(location, path) {
67
76
  * @param input - samples in the range [-1, 1].
68
77
  * @returns the same samples as little-endian PCM16 bytes.
69
78
  */
70
- export function pcm16FromFloat32(input) {
79
+ function pcm16FromFloat32(input) {
71
80
  const out = new Int16Array(input.length);
72
81
  // `entries()` rather than an index: under `noUncheckedIndexedAccess` an indexed read is
73
82
  // `number | undefined`, and guarding that would add a branch that can never be taken at runtime.
@@ -83,7 +92,7 @@ export function pcm16FromFloat32(input) {
83
92
  * @param bytes - the frame.
84
93
  * @returns samples in [-1, 1).
85
94
  */
86
- export function float32FromPcm16(bytes) {
95
+ function float32FromPcm16(bytes) {
87
96
  const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
88
97
  const out = new Float32Array(samples.length);
89
98
  for (const [index, sample] of samples.entries())
@@ -98,7 +107,7 @@ export function float32FromPcm16(bytes) {
98
107
  * @returns the production dependencies. Every one may be absent, which is why `start` reports rather than
99
108
  * throws: a page without a microphone API is a fact about the page, not a defect in the caller.
100
109
  */
101
- export function defaultDeps(scope = globalThis) {
110
+ function defaultDeps(scope = globalThis) {
102
111
  const media = scope.navigator?.mediaDevices;
103
112
  const Audio = scope.AudioContext;
104
113
  const Socket = scope.WebSocket;
@@ -116,7 +125,7 @@ export function defaultDeps(scope = globalThis) {
116
125
  * @param deps - the outside world, injected so every path is testable.
117
126
  * @returns the start/stop/state handle.
118
127
  */
119
- export function createAudioClient(deps) {
128
+ function createAudioClient(deps) {
120
129
  let current = { kind: 'idle' };
121
130
  let socket;
122
131
  let context;
@@ -152,13 +161,13 @@ export function createAudioClient(deps) {
152
161
  const samples = float32FromPcm16(bytes);
153
162
  if (samples.length === 0)
154
163
  return;
155
- const buffer = context.createBuffer(1, samples.length, SAMPLE_RATE);
164
+ const buffer = context.createBuffer(1, samples.length, exports.SAMPLE_RATE);
156
165
  buffer.getChannelData(0).set(samples);
157
166
  const now = context.currentTime;
158
167
  const at = Math.max(now, playsAt);
159
168
  // Unbuffered, like the host: audio that is already late is dropped rather than played behind, because a
160
169
  // queue that grows converts a stutter into a permanent delay that never recovers.
161
- if (at - now > MAX_BACKLOG_SECONDS)
170
+ if (at - now > exports.MAX_BACKLOG_SECONDS)
162
171
  return;
163
172
  const source = context.createBufferSource();
164
173
  source.buffer = buffer;
@@ -172,7 +181,7 @@ export function createAudioClient(deps) {
172
181
  return fail('the audio graph went away before the socket opened');
173
182
  }
174
183
  const source = context.createMediaStreamSource(stream);
175
- const node = context.createScriptProcessor(CAPTURE_BUFFER, 1, 1);
184
+ const node = context.createScriptProcessor(exports.CAPTURE_BUFFER, 1, 1);
176
185
  node.onaudioprocess = (event) => {
177
186
  const pcm16 = pcm16FromFloat32(event.inputBuffer.getChannelData(0));
178
187
  socket?.send(new Uint8Array(pcm16.buffer));
@@ -197,7 +206,7 @@ export function createAudioClient(deps) {
197
206
  const start = async () => {
198
207
  if (current.kind === 'live')
199
208
  return current;
200
- const url = socketUrl(deps.location, deps.injected?.path ?? DEFAULT_PATH);
209
+ const url = socketUrl(deps.location, deps.injected?.path ?? exports.DEFAULT_PATH);
201
210
  if (url === undefined)
202
211
  return fail('this page has no location to open a socket against');
203
212
  if (deps.getUserMedia === undefined)
@@ -216,13 +225,13 @@ export function createAudioClient(deps) {
216
225
  // user whether to grant it or to look somewhere else.
217
226
  return fail(error instanceof Error ? error.message : String(error));
218
227
  }
219
- context = deps.createAudioContext(SAMPLE_RATE);
220
- if (context.sampleRate !== SAMPLE_RATE) {
228
+ context = deps.createAudioContext(exports.SAMPLE_RATE);
229
+ if (context.sampleRate !== exports.SAMPLE_RATE) {
221
230
  // Refuse rather than mislabel. Streaming 48 kHz samples declared as 24 kHz arrives at half speed and
222
231
  // sounds like a provider fault, which is the most expensive possible way to learn about a resampler.
223
232
  const opened = context.sampleRate;
224
233
  release();
225
- return fail(`the audio graph opened at ${String(opened)} Hz, not ${String(SAMPLE_RATE)}`);
234
+ return fail(`the audio graph opened at ${String(opened)} Hz, not ${String(exports.SAMPLE_RATE)}`);
226
235
  }
227
236
  const opened = deps.createSocket(url);
228
237
  socket = opened;
@@ -250,9 +259,9 @@ export function createAudioClient(deps) {
250
259
  *
251
260
  * @param ctx - the client context, used only to own the lifetime.
252
261
  */
253
- export function apply(ctx) {
262
+ function apply(ctx) {
254
263
  const client = createAudioClient(defaultDeps());
255
- globalThis[GLOBAL_KEY] = {
264
+ globalThis[exports.GLOBAL_KEY] = {
256
265
  start: () => client.start(),
257
266
  stop: () => { client.stop(); },
258
267
  state: () => client.state(),
@@ -260,4 +269,3 @@ export function apply(ctx) {
260
269
  // A held microphone and a live audio graph must not outlive the plugin that opened them.
261
270
  ctx.effect(() => () => { client.stop(); }, 'realtime-audio-client');
262
271
  }
263
- //# sourceMappingURL=index.js.map
package/lib/index.d.ts CHANGED
@@ -22,10 +22,12 @@ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
22
22
  path: Schema<string, string, "defined">;
23
23
  maxFrameBytes: Schema<number, number, "defined">;
24
24
  maxConnections: Schema<number, number, "defined">;
25
+ openSessionOnConnect: Schema<boolean, boolean, "defined">;
25
26
  }>>, Schemastery.ObjectT<NoInfer<{
26
27
  path: Schema<string, string, "defined">;
27
28
  maxFrameBytes: Schema<number, number, "defined">;
28
29
  maxConnections: Schema<number, number, "defined">;
30
+ openSessionOnConnect: Schema<boolean, boolean, "defined">;
29
31
  }>>, "plain">;
30
32
  export declare function apply(ctx: Context, config: RealtimeAudioWsConfig): void;
31
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAA;AAEnB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAEzF,mBAAmB;AACnB,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC,eAAO,MAAM,MAAM;;;;;;;;aAIjB,CAAA;AAqBF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CA4CvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,OAAO,EAML,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAA;AAEnB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAEzF,mBAAmB;AACnB,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC,eAAO,MAAM,MAAM;;;;;;;;;;aAQjB,CAAA;AAqBF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAoDvE"}
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  import Schema from '@deepseek-ai/schemastery';
14
14
  import { attachAudioSocket } from './bridge.js';
15
15
  import { createUpgradeAcceptor, rejectUpgrade } from './upgrade.js';
16
- import { DEFAULT_MAX_CONNECTIONS, DEFAULT_MAX_FRAME_BYTES, DEFAULT_PATH, } from './types.js';
16
+ import { DEFAULT_MAX_CONNECTIONS, DEFAULT_MAX_FRAME_BYTES, DEFAULT_OPEN_SESSION_ON_CONNECT, DEFAULT_PATH, } from './types.js';
17
17
  export * from './types.js';
18
18
  export { attachAudioSocket, toBytes } from './bridge.js';
19
19
  export { createUpgradeAcceptor, rejectUpgrade } from './upgrade.js';
@@ -23,6 +23,10 @@ export const Config = Schema.object({
23
23
  path: Schema.string().default(DEFAULT_PATH),
24
24
  maxFrameBytes: Schema.natural().default(DEFAULT_MAX_FRAME_BYTES),
25
25
  maxConnections: Schema.natural().default(DEFAULT_MAX_CONNECTIONS),
26
+ // True, and deliberately not the same decision as the agent's `autoStart`. That one opens a session at
27
+ // boot with nobody asking, which is why it is false. This one opens a session because someone connected a
28
+ // microphone: a connection takes an explicit action, an authenticated one, and its absence is silence.
29
+ openSessionOnConnect: Schema.boolean().default(DEFAULT_OPEN_SESSION_ON_CONNECT),
26
30
  });
27
31
  export function apply(ctx, config) {
28
32
  ctx.inject(['connection', 'webServer'], (injected) => {
@@ -50,11 +54,21 @@ export function apply(ctx, config) {
50
54
  return;
51
55
  }
52
56
  clients.add(client);
57
+ // Ask for the session before the bridge goes in, so the first frames are written into a session
58
+ // that is being opened rather than dropped by the mic seam's no-session rule.
59
+ if (config.openSessionOnConnect)
60
+ ctx.emit('realtime-agent/start');
53
61
  attachAudioSocket(client, {
54
62
  emitMic: (pcm16) => { ctx.emit('realtime-agent/mic', pcm16); },
55
63
  subscribeAudio: (listener) => ctx.on('realtime-agent/audio', listener),
56
64
  maxFrameBytes: config.maxFrameBytes,
57
- onDetach: () => { clients.delete(client); },
65
+ onDetach: () => {
66
+ clients.delete(client);
67
+ // The last client leaving ends the session. This also covers the transport's own disposal,
68
+ // which terminates its clients — so a profile reload does not leave a session nobody holds.
69
+ if (config.openSessionOnConnect && clients.size === 0)
70
+ ctx.emit('realtime-agent/stop');
71
+ },
58
72
  });
59
73
  });
60
74
  },
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts CHANGED
@@ -17,6 +17,8 @@ export declare const DEFAULT_PATH = "/dsh-realtime/audio";
17
17
  export declare const DEFAULT_MAX_FRAME_BYTES = 480000;
18
18
  /** How many sockets may be attached at once. One microphone is the honest default. */
19
19
  export declare const DEFAULT_MAX_CONNECTIONS = 1;
20
+ /** Whether an authenticated connection asks the agent to open the voice session. See the config field. */
21
+ export declare const DEFAULT_OPEN_SESSION_ON_CONNECT = true;
20
22
  export interface RealtimeAudioWsConfig {
21
23
  /**
22
24
  * Absolute pathname to claim. Registered exactly, so it must be distinct from every other route in the
@@ -33,6 +35,16 @@ export interface RealtimeAudioWsConfig {
33
35
  * two microphones on one session is a fault, not a feature.
34
36
  */
35
37
  readonly maxConnections: number;
38
+ /**
39
+ * Ask the agent to open the voice session when an authenticated client connects, and to close it when the
40
+ * last one leaves. Default true.
41
+ *
42
+ * This is not the agent's `autoStart`, and the two defaults differ for a reason. `autoStart` opens a
43
+ * session at boot with nobody asking, which is why it ships false. This one responds to a connection,
44
+ * which takes an explicit authenticated action — and with it false, a working microphone produces silence
45
+ * that looks like a fault anywhere but in this file.
46
+ */
47
+ readonly openSessionOnConnect: boolean;
36
48
  }
37
49
  /**
38
50
  * The slice of a WebSocket this package uses, described structurally.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0GAA0G;AAC1G,eAAO,MAAM,YAAY,wBAAwB,CAAA;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,uBAAuB,SAAU,CAAA;AAE9C,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAChC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,SAAS,IAAI,IAAI,CAAA;IACjB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAA;IACnF,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,OAAO,CAAA;CAC9E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0GAA0G;AAC1G,eAAO,MAAM,YAAY,wBAAwB,CAAA;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,uBAAuB,SAAU,CAAA;AAE9C,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,0GAA0G;AAC1G,eAAO,MAAM,+BAA+B,OAAO,CAAA;AAEnD,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAA;CACvC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,SAAS,IAAI,IAAI,CAAA;IACjB,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAA;IACnF,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,OAAO,CAAA;CAC9E"}
package/lib/types.js CHANGED
@@ -17,4 +17,6 @@ export const DEFAULT_PATH = '/dsh-realtime/audio';
17
17
  export const DEFAULT_MAX_FRAME_BYTES = 480_000;
18
18
  /** How many sockets may be attached at once. One microphone is the honest default. */
19
19
  export const DEFAULT_MAX_CONNECTIONS = 1;
20
+ /** Whether an authenticated connection asks the agent to open the voice session. See the config field. */
21
+ export const DEFAULT_OPEN_SESSION_ON_CONNECT = true;
20
22
  //# sourceMappingURL=types.js.map
package/lib/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0GAA0G;AAC1G,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAA;AAEjD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAA;AAE9C,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAA"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0GAA0G;AAC1G,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAA;AAEjD,6FAA6F;AAC7F,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAA;AAE9C,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAA;AAExC,0GAA0G;AAC1G,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-realtime-audio-ws",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "The host end of the client half's transport: a WebSocket upgrade route that bridges microphone audio in and agent speech out.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "ws": "^8.22.0",
55
- "dsh-realtime-agent": "^0.2.1"
55
+ "dsh-realtime-agent": "^0.2.2"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@deepseek-ai/cordis": "^4.0.3",
@@ -64,7 +64,7 @@
64
64
  "@types/ws": "^8.18.2"
65
65
  },
66
66
  "scripts": {
67
- "build": "tsc -b",
67
+ "build": "tsc -b && tsc -p tsconfig.client.json",
68
68
  "typecheck": "tsc -b --dry"
69
69
  }
70
70
  }
@@ -23,8 +23,6 @@
23
23
  * actually been read, rather than shipping a capture path that a policy might silently kill.
24
24
  */
25
25
 
26
- import type { Context as ClientContext } from '@deepseek-ai/cordis'
27
-
28
26
  /** Browser-side plugin name. */
29
27
  export const name = 'realtime-audio-client'
30
28
 
@@ -83,6 +81,17 @@ export interface SocketLike {
83
81
  onclose: (() => void) | null
84
82
  }
85
83
 
84
+ /**
85
+ * The slice of a client Cordis context this plugin uses.
86
+ *
87
+ * Structural, and not only for testability: this face is served as a single CommonJS module, so any import
88
+ * it carried would have to be resolvable by whatever the module table hands the bundle. It needs one
89
+ * method, so it states one and imports nothing at all.
90
+ */
91
+ export interface ClientContextLike {
92
+ effect(run: unknown, label?: string): unknown
93
+ }
94
+
86
95
  /** Everything this plugin touches outside itself, injectable so every path is testable. */
87
96
  export interface ClientAudioDeps {
88
97
  readonly location: LocationLike | undefined
@@ -328,7 +337,7 @@ export function createAudioClient(deps: ClientAudioDeps): ClientAudio {
328
337
  *
329
338
  * @param ctx - the client context, used only to own the lifetime.
330
339
  */
331
- export function apply(ctx: ClientContext): void {
340
+ export function apply(ctx: ClientContextLike): void {
332
341
  const client = createAudioClient(defaultDeps())
333
342
  ;(globalThis as unknown as Record<string, unknown>)[GLOBAL_KEY] = {
334
343
  start: () => client.start(),
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ import { createUpgradeAcceptor, rejectUpgrade } from './upgrade.ts'
22
22
  import {
23
23
  DEFAULT_MAX_CONNECTIONS,
24
24
  DEFAULT_MAX_FRAME_BYTES,
25
+ DEFAULT_OPEN_SESSION_ON_CONNECT,
25
26
  DEFAULT_PATH,
26
27
  type AudioSocket,
27
28
  type RealtimeAudioWsConfig,
@@ -38,6 +39,10 @@ export const Config = Schema.object({
38
39
  path: Schema.string().default(DEFAULT_PATH),
39
40
  maxFrameBytes: Schema.natural().default(DEFAULT_MAX_FRAME_BYTES),
40
41
  maxConnections: Schema.natural().default(DEFAULT_MAX_CONNECTIONS),
42
+ // True, and deliberately not the same decision as the agent's `autoStart`. That one opens a session at
43
+ // boot with nobody asking, which is why it is false. This one opens a session because someone connected a
44
+ // microphone: a connection takes an explicit action, an authenticated one, and its absence is silence.
45
+ openSessionOnConnect: Schema.boolean().default(DEFAULT_OPEN_SESSION_ON_CONNECT),
41
46
  })
42
47
 
43
48
  /**
@@ -86,11 +91,19 @@ export function apply(ctx: Context, config: RealtimeAudioWsConfig): void {
86
91
  return
87
92
  }
88
93
  clients.add(client)
94
+ // Ask for the session before the bridge goes in, so the first frames are written into a session
95
+ // that is being opened rather than dropped by the mic seam's no-session rule.
96
+ if (config.openSessionOnConnect) ctx.emit('realtime-agent/start')
89
97
  attachAudioSocket(client, {
90
98
  emitMic: (pcm16) => { ctx.emit('realtime-agent/mic', pcm16) },
91
99
  subscribeAudio: (listener) => ctx.on('realtime-agent/audio', listener),
92
100
  maxFrameBytes: config.maxFrameBytes,
93
- onDetach: () => { clients.delete(client) },
101
+ onDetach: () => {
102
+ clients.delete(client)
103
+ // The last client leaving ends the session. This also covers the transport's own disposal,
104
+ // which terminates its clients — so a profile reload does not leave a session nobody holds.
105
+ if (config.openSessionOnConnect && clients.size === 0) ctx.emit('realtime-agent/stop')
106
+ },
94
107
  })
95
108
  })
96
109
  },
package/src/types.ts CHANGED
@@ -21,6 +21,9 @@ export const DEFAULT_MAX_FRAME_BYTES = 480_000
21
21
  /** How many sockets may be attached at once. One microphone is the honest default. */
22
22
  export const DEFAULT_MAX_CONNECTIONS = 1
23
23
 
24
+ /** Whether an authenticated connection asks the agent to open the voice session. See the config field. */
25
+ export const DEFAULT_OPEN_SESSION_ON_CONNECT = true
26
+
24
27
  export interface RealtimeAudioWsConfig {
25
28
  /**
26
29
  * Absolute pathname to claim. Registered exactly, so it must be distinct from every other route in the
@@ -37,6 +40,16 @@ export interface RealtimeAudioWsConfig {
37
40
  * two microphones on one session is a fault, not a feature.
38
41
  */
39
42
  readonly maxConnections: number
43
+ /**
44
+ * Ask the agent to open the voice session when an authenticated client connects, and to close it when the
45
+ * last one leaves. Default true.
46
+ *
47
+ * This is not the agent's `autoStart`, and the two defaults differ for a reason. `autoStart` opens a
48
+ * session at boot with nobody asking, which is why it ships false. This one responds to a connection,
49
+ * which takes an explicit authenticated action — and with it false, a working microphone produces silence
50
+ * that looks like a fault anywhere but in this file.
51
+ */
52
+ readonly openSessionOnConnect: boolean
40
53
  }
41
54
 
42
55
  /**
@@ -1,192 +0,0 @@
1
- /**
2
- * The browser face: microphone in, speaker out, over the host route.
3
- *
4
- * This is the client half of the transport. `dsh-realtime-audio-ws` claims the socket on the host and
5
- * bridges it to the agent's audio events; this file opens that socket, captures the microphone into it, and
6
- * plays what comes back.
7
- *
8
- * **One file, on purpose.** A client face is served as a single resource (`<package>/client.js`), so a
9
- * relative value import would emit a second file the loader never serves. Everything therefore lives here,
10
- * including the API shapes it needs.
11
- *
12
- * **No DOM lib, also on purpose.** The browser APIs are described structurally and read off `globalThis`
13
- * through one cast, so this compiles in the package's ordinary program — no second tsconfig, no DOM lib,
14
- * no bundler — and every path is testable by injecting fakes. `standard-browser-types` would have bought
15
- * accurate types for six objects at the cost of a whole second build face; this is the cheaper trade.
16
- *
17
- * **`ScriptProcessorNode`, and why.** The modern capture path is an `AudioWorklet`, whose module must be
18
- * fetched from a URL — a `blob:` URL, which a page's Content-Security-Policy can refuse. The desktop app
19
- * demonstrably *has* a CSP (a `connect-src 'none'` string is in its bundle, governing the shell pages), and
20
- * I have not read the app page's policy. `ScriptProcessorNode` needs no URL, no module fetch and no policy
21
- * grant, and it runs in the page, so the whole capture path is testable. It is deprecated and its latency is
22
- * worse. That is a deliberate, disclosed trade: swap it for a worklet once the app page's `script-src` has
23
- * actually been read, rather than shipping a capture path that a policy might silently kill.
24
- */
25
- import type { Context as ClientContext } from '@deepseek-ai/cordis';
26
- /** Browser-side plugin name. */
27
- export declare const name = "realtime-audio-client";
28
- /**
29
- * No client services. The socket, the device and the audio graph are all this plugin's own, so it injects
30
- * nothing — and a client face that needs nothing cannot be broken by another plugin's absence.
31
- */
32
- export declare const inject: string[];
33
- /** Where the host route lives. Duplicated from the host package because a client face cannot import it. */
34
- export declare const DEFAULT_PATH = "/dsh-realtime/audio";
35
- /** The session's declared rate. The graph is opened at this rate or the client refuses to run. */
36
- export declare const SAMPLE_RATE = 24000;
37
- /** Capture chunk in samples. 1024 at 24 kHz is ~43 ms: the 2^10 floor the API allows, so latency is lowest. */
38
- export declare const CAPTURE_BUFFER = 1024;
39
- /** Backlog past which incoming speech is dropped rather than queued — the same rule the host carries. */
40
- export declare const MAX_BACKLOG_SECONDS = 0.5;
41
- /** Global the client publishes itself on. There is no UI surface yet; this is how it is started. */
42
- export declare const GLOBAL_KEY = "__dshRealtimeAudio";
43
- /** Optional host-injected settings. A future index-injection row may set the path; the default matches. */
44
- export declare const INJECTED_KEY = "__DSH_REALTIME_AUDIO__";
45
- export interface LocationLike {
46
- readonly protocol: string;
47
- readonly host: string;
48
- }
49
- export interface TrackLike {
50
- stop(): void;
51
- readonly kind?: string;
52
- }
53
- export interface StreamLike {
54
- getTracks(): TrackLike[];
55
- }
56
- export interface MediaNodeLike {
57
- connect(target: MediaNodeLike | unknown): void;
58
- disconnect(): void;
59
- }
60
- export interface BufferLike {
61
- readonly duration: number;
62
- getChannelData(channel: number): Float32Array;
63
- }
64
- export interface SourceLike {
65
- buffer: unknown;
66
- connect(target: unknown): void;
67
- start(when?: number): void;
68
- }
69
- export interface ContextLike {
70
- readonly sampleRate: number;
71
- readonly currentTime: number;
72
- readonly destination: unknown;
73
- createMediaStreamSource(stream: StreamLike): MediaNodeLike;
74
- createScriptProcessor(buffer: number, inputs: number, outputs: number): MediaNodeLike & {
75
- onaudioprocess: ((event: {
76
- inputBuffer: BufferLike;
77
- }) => void) | null;
78
- };
79
- createBuffer(channels: number, frames: number, rate: number): BufferLike;
80
- createGain(): MediaNodeLike & {
81
- readonly gain: {
82
- value: number;
83
- };
84
- };
85
- createBufferSource(): SourceLike;
86
- close(): Promise<void>;
87
- }
88
- export interface SocketLike {
89
- binaryType?: string;
90
- send(data: Uint8Array): void;
91
- close(): void;
92
- onopen: (() => void) | null;
93
- onmessage: ((event: {
94
- data: unknown;
95
- }) => void) | null;
96
- onerror: (() => void) | null;
97
- onclose: (() => void) | null;
98
- }
99
- /** Everything this plugin touches outside itself, injectable so every path is testable. */
100
- export interface ClientAudioDeps {
101
- readonly location: LocationLike | undefined;
102
- readonly getUserMedia: ((constraints: unknown) => Promise<StreamLike>) | undefined;
103
- readonly createAudioContext: ((sampleRate: number) => ContextLike) | undefined;
104
- readonly createSocket: ((url: string) => SocketLike) | undefined;
105
- readonly injected: {
106
- readonly path?: string;
107
- } | undefined;
108
- }
109
- /** What the caller — or a test — gets to see. */
110
- export type ClientAudioState = {
111
- readonly kind: 'idle';
112
- } | {
113
- readonly kind: 'live';
114
- } | {
115
- readonly kind: 'failed';
116
- readonly reason: string;
117
- };
118
- export interface ClientAudio {
119
- /** Acquire the microphone and connect. Resolves with the state it reached. */
120
- start(): Promise<ClientAudioState>;
121
- /** Release everything. Safe to call when not started. */
122
- stop(): void;
123
- /** Current state. */
124
- state(): ClientAudioState;
125
- }
126
- /**
127
- * The socket URL for the host route.
128
- *
129
- * @param location - the page's location, or undefined when there is none.
130
- * @param path - the route pathname.
131
- * @returns the URL, or undefined when this page cannot host a socket at all.
132
- */
133
- export declare function socketUrl(location: LocationLike | undefined, path: string): string | undefined;
134
- /**
135
- * Convert one captured float block to the wire's PCM16.
136
- *
137
- * Clamped, not wrapped: a sample above 1.0 is a loud peak, and letting it wrap turns a loud voice into
138
- * noise. Asymmetric on purpose — 32767 is the largest Int16 and -32768 the smallest, so scaling both by the
139
- * same factor would clip every negative peak one step early.
140
- *
141
- * @param input - samples in the range [-1, 1].
142
- * @returns the same samples as little-endian PCM16 bytes.
143
- */
144
- export declare function pcm16FromFloat32(input: Float32Array): Int16Array;
145
- /**
146
- * Read one PCM16 frame as playback samples.
147
- *
148
- * @param bytes - the frame.
149
- * @returns samples in [-1, 1).
150
- */
151
- export declare function float32FromPcm16(bytes: Uint8Array): Float32Array;
152
- export interface ScopeLike {
153
- location?: LocationLike;
154
- navigator?: {
155
- mediaDevices?: {
156
- getUserMedia(constraints: unknown): Promise<StreamLike>;
157
- };
158
- };
159
- AudioContext?: new (options: {
160
- sampleRate: number;
161
- }) => ContextLike;
162
- WebSocket?: new (url: string) => SocketLike;
163
- __DSH_REALTIME_AUDIO__?: {
164
- readonly path?: string;
165
- };
166
- }
167
- /**
168
- * Read the real browser APIs, and any settings the host injected.
169
- *
170
- * @param scope - the global scope, injectable so both the present and absent cases are testable without
171
- * mutating the process's own globals.
172
- * @returns the production dependencies. Every one may be absent, which is why `start` reports rather than
173
- * throws: a page without a microphone API is a fact about the page, not a defect in the caller.
174
- */
175
- export declare function defaultDeps(scope?: ScopeLike): ClientAudioDeps;
176
- /**
177
- * The client: one microphone in, one socket out, one speaker fed.
178
- *
179
- * @param deps - the outside world, injected so every path is testable.
180
- * @returns the start/stop/state handle.
181
- */
182
- export declare function createAudioClient(deps: ClientAudioDeps): ClientAudio;
183
- /**
184
- * The client plugin. Publishes the client on a global and releases it with the plugin.
185
- *
186
- * There is no UI surface yet, so a global is how it is driven — a settings card is its own increment and a
187
- * bigger one than this. `inject` is empty for the same reason: nothing here needs another plugin.
188
- *
189
- * @param ctx - the client context, used only to own the lifetime.
190
- */
191
- export declare function apply(ctx: ClientContext): void;
192
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}