dsh-realtime-audio-ws 0.2.3 → 0.2.4

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.
@@ -231,6 +231,12 @@ window.__ModuleLoader__.load({ id: "dsh-realtime-audio-ws",
231
231
  const source = context.createMediaStreamSource(stream);
232
232
  const node = context.createScriptProcessor(exports.CAPTURE_BUFFER, 1, 1);
233
233
  node.onaudioprocess = (event) => {
234
+ // Every quantum ships, **silent ones included, and that is load-bearing rather than incidental**: the
235
+ // provider's session timeline advances with the audio this client sends, and a context append is
236
+ // placed only while that timeline is advancing — so a client that skipped silent blocks (an
237
+ // obvious-looking saving) would silently freeze the session and lose every append after it. Measured,
238
+ // with the mechanism, in `docs/usable-window.md`; guarded by the capture-continuity case in
239
+ // `tests/client.spec.ts`.
234
240
  const pcm16 = pcm16FromFloat32(event.inputBuffer.getChannelData(0));
235
241
  socket?.send(new Uint8Array(pcm16.buffer));
236
242
  };
@@ -227,6 +227,12 @@ function createAudioClient(deps) {
227
227
  const source = context.createMediaStreamSource(stream);
228
228
  const node = context.createScriptProcessor(exports.CAPTURE_BUFFER, 1, 1);
229
229
  node.onaudioprocess = (event) => {
230
+ // Every quantum ships, **silent ones included, and that is load-bearing rather than incidental**: the
231
+ // provider's session timeline advances with the audio this client sends, and a context append is
232
+ // placed only while that timeline is advancing — so a client that skipped silent blocks (an
233
+ // obvious-looking saving) would silently freeze the session and lose every append after it. Measured,
234
+ // with the mechanism, in `docs/usable-window.md`; guarded by the capture-continuity case in
235
+ // `tests/client.spec.ts`.
230
236
  const pcm16 = pcm16FromFloat32(event.inputBuffer.getChannelData(0));
231
237
  socket?.send(new Uint8Array(pcm16.buffer));
232
238
  };
package/lib/index.d.ts CHANGED
@@ -40,12 +40,14 @@ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
40
40
  maxConnections: Schema<number, number, "defined">;
41
41
  openSessionOnConnect: Schema<boolean, boolean, "defined">;
42
42
  diagnosticsPath: Schema<string, string, "defined">;
43
+ journalPath: Schema<string, string, "defined">;
43
44
  }>>, Schemastery.ObjectT<NoInfer<{
44
45
  path: Schema<string, string, "defined">;
45
46
  maxFrameBytes: Schema<number, number, "defined">;
46
47
  maxConnections: Schema<number, number, "defined">;
47
48
  openSessionOnConnect: Schema<boolean, boolean, "defined">;
48
49
  diagnosticsPath: Schema<string, string, "defined">;
50
+ journalPath: Schema<string, string, "defined">;
49
51
  }>>, "plain">;
50
52
  export declare function apply(ctx: Context, config: RealtimeAudioWsConfig): void;
51
53
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAkBlD,OAAO,EAOL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAA;AAEnB,cAAc,YAAY,CAAA;AAC1B;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC5F,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACzH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACrF,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEpE,mBAAmB;AACnB,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC,eAAO,MAAM,MAAM;;;;;;;;;;;;aASjB,CAAA;AAqCF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAqKvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAkBlD,OAAO,EAOL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAA;AAEnB,cAAc,YAAY,CAAA;AAC1B;;;;;;GAMG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAC5F,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACzH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACrF,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIpE,mBAAmB;AACnB,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;aAajB,CAAA;AAqCF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CA4KvE"}
package/lib/index.js CHANGED
@@ -36,6 +36,7 @@ export { createControlHandler, parseControlFrame, } from './control.js';
36
36
  export { createUpgradeAcceptor, rejectUpgrade } from './upgrade.js';
37
37
  export { diagnosticsRoute } from './diagnostics.js';
38
38
  export { STRIP_ELEMENT_ID, stripBootstrap, stripMarkup, stripRows } from './strip.js';
39
+ import { createJournalFileSink } from './journal-file.js';
39
40
  /** Plugin name. */
40
41
  export const name = 'realtime-audio-ws';
41
42
  export const Config = Schema.object({
@@ -47,6 +48,10 @@ export const Config = Schema.object({
47
48
  // microphone: a connection takes an explicit action, an authenticated one, and its absence is silence.
48
49
  openSessionOnConnect: Schema.boolean().default(DEFAULT_OPEN_SESSION_ON_CONNECT),
49
50
  diagnosticsPath: Schema.string().default(DEFAULT_DIAGNOSTICS_PATH),
51
+ // Empty means no file, and empty is the default: a plugin that writes files because it was installed is a
52
+ // plugin writing files in somebody else's directory. The deployment that wants a recording is the
53
+ // deployment that names a path.
54
+ journalPath: Schema.string().default(''),
50
55
  });
51
56
  export function apply(ctx, config) {
52
57
  ctx.inject(['connection', 'webServer', 'realtime'], (injected) => {
@@ -64,6 +69,13 @@ export function apply(ctx, config) {
64
69
  // constructor argument: the plugin that mints the token is the only one that can name it, and it
65
70
  // mints it here, after the journal already exists.
66
71
  journal.addSecrets([token]);
72
+ // The record leaves the process when a path is named. Registered *after* the token, so that every entry
73
+ // the file receives has already been through the redaction that knows about it — a sink is handed
74
+ // entries after `record` has redacted them, and that ordering is the reason this is a sink rather than a
75
+ // second writer with its own idea of what a secret looks like.
76
+ if (config.journalPath.length > 0) {
77
+ journal.onEntry(createJournalFileSink(config.journalPath));
78
+ }
67
79
  // The injection event is declared by `@deepseek-ai/dsh-host-webserver`, which this package deliberately
68
80
  // does not import: importing it would augment `Context` with a second declaration of `webServer` and
69
81
  // turn the structural dependency above into a compile error. The name is cast at this one boundary.
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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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A journal sink that appends to a file, so the record outlives the process that made it.
3
+ *
4
+ * The journal has always *held* everything worth knowing and has always been unable to hand it over: it is
5
+ * served over a route that wants a per-boot capability token, which means the record can be read by the page
6
+ * that holds the token and by nobody else, and it cannot be read after the fact at all. Every failure this
7
+ * plugin has had was diagnosed by reading somebody else's log — a harness session file off disk, or a
8
+ * `curl` at a route that answered 401. This is the missing half: the same entries, on disk, readable with
9
+ * `cat` by whoever is handed the file.
10
+ *
11
+ * ## Why `appendFileSync`, deliberately
12
+ *
13
+ * The ordering of a diagnostic record is the record. An asynchronous append can interleave two entries or
14
+ * lose one to a process that exits during the write, and the failure this exists to explain is usually the
15
+ * last thing that happened. So each line is written synchronously, as one line, and a reader can trust that
16
+ * the file is ordered and complete up to the last entry it contains.
17
+ *
18
+ * ## Redaction already happened
19
+ *
20
+ * A sink is handed entries *after* `Journal.record` has redacted them, so the file carries exactly what the
21
+ * route would have served and never the values named in `redactSecrets`. That ordering is the whole reason
22
+ * this is a sink and not a second writer with its own copy of the redaction rules — two code paths that must
23
+ * agree about what a secret looks like are two code paths that eventually disagree.
24
+ *
25
+ * @module dsh-realtime-audio-ws/journal-file
26
+ */
27
+ import type { JournalEntry } from 'dsh-realtime';
28
+ /**
29
+ * Build a sink that appends each entry to `path` as one JSON object per line.
30
+ *
31
+ * The parent directory is created here, at construction, rather than on the first write. That is the one
32
+ * failure this can have that is worth being loud about — a path that cannot be written — and at construction
33
+ * it is a startup error a caller can refuse to launch on, instead of a silent nothing that looks exactly
34
+ * like a quiet session. A *later* write that fails is swallowed by `Journal.onEntry`'s guard, by design.
35
+ * @param path - absolute or relative file path; the file is appended to, never truncated.
36
+ * @returns the sink, ready for `Journal.onEntry`.
37
+ */
38
+ export declare function createJournalFileSink(path: string): (entry: JournalEntry) => void;
39
+ //# sourceMappingURL=journal-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal-file.d.ts","sourceRoot":"","sources":["../src/journal-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAKjF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A journal sink that appends to a file, so the record outlives the process that made it.
3
+ *
4
+ * The journal has always *held* everything worth knowing and has always been unable to hand it over: it is
5
+ * served over a route that wants a per-boot capability token, which means the record can be read by the page
6
+ * that holds the token and by nobody else, and it cannot be read after the fact at all. Every failure this
7
+ * plugin has had was diagnosed by reading somebody else's log — a harness session file off disk, or a
8
+ * `curl` at a route that answered 401. This is the missing half: the same entries, on disk, readable with
9
+ * `cat` by whoever is handed the file.
10
+ *
11
+ * ## Why `appendFileSync`, deliberately
12
+ *
13
+ * The ordering of a diagnostic record is the record. An asynchronous append can interleave two entries or
14
+ * lose one to a process that exits during the write, and the failure this exists to explain is usually the
15
+ * last thing that happened. So each line is written synchronously, as one line, and a reader can trust that
16
+ * the file is ordered and complete up to the last entry it contains.
17
+ *
18
+ * ## Redaction already happened
19
+ *
20
+ * A sink is handed entries *after* `Journal.record` has redacted them, so the file carries exactly what the
21
+ * route would have served and never the values named in `redactSecrets`. That ordering is the whole reason
22
+ * this is a sink and not a second writer with its own copy of the redaction rules — two code paths that must
23
+ * agree about what a secret looks like are two code paths that eventually disagree.
24
+ *
25
+ * @module dsh-realtime-audio-ws/journal-file
26
+ */
27
+ import { appendFileSync, mkdirSync } from 'node:fs';
28
+ import { dirname } from 'node:path';
29
+ /**
30
+ * Build a sink that appends each entry to `path` as one JSON object per line.
31
+ *
32
+ * The parent directory is created here, at construction, rather than on the first write. That is the one
33
+ * failure this can have that is worth being loud about — a path that cannot be written — and at construction
34
+ * it is a startup error a caller can refuse to launch on, instead of a silent nothing that looks exactly
35
+ * like a quiet session. A *later* write that fails is swallowed by `Journal.onEntry`'s guard, by design.
36
+ * @param path - absolute or relative file path; the file is appended to, never truncated.
37
+ * @returns the sink, ready for `Journal.onEntry`.
38
+ */
39
+ export function createJournalFileSink(path) {
40
+ mkdirSync(dirname(path), { recursive: true });
41
+ return (entry) => {
42
+ appendFileSync(path, `${JSON.stringify(entry)}\n`);
43
+ };
44
+ }
45
+ //# sourceMappingURL=journal-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal-file.js","sourceRoot":"","sources":["../src/journal-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,OAAO,CAAC,KAAmB,EAAQ,EAAE;QACnC,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACpD,CAAC,CAAA;AACH,CAAC"}
package/lib/strip.d.ts CHANGED
@@ -33,10 +33,17 @@ export declare const STRIP_ELEMENT_ID = "dsh-realtime-strip";
33
33
  /**
34
34
  * The panel's container, as markup.
35
35
  *
36
- * Fixed to the bottom right and deliberately nearly unstyled: it sits inside the host's own page, so it
37
- * inherits that page's font and colour rather than inventing a second visual language beside it. A
38
- * translucent wash rather than a border, because a border here would be visual noise around a strip whose
39
- * job is to be glanced at.
36
+ * Fixed to the bottom right, inheriting the host's font and colour rather than inventing a second visual
37
+ * language beside it — but **opaque**, and that is a correction rather than a taste. This panel sits over
38
+ * the host's own text, so a translucent wash lets the page show through and the two become unreadable
39
+ * together: two texts superimposed, neither of them legible. That was reported from a screenshot of the
40
+ * running app, which is the only way it was ever going to be found — nothing in a DOM test can see what is
41
+ * *behind* the panel.
42
+ *
43
+ * `Canvas` is the theme's own surface colour, so the panel follows light or dark mode without naming a
44
+ * palette of its own, and the 6% tint of the inherited colour keeps the surface from reading as a hole
45
+ * punched in the page. The soft shadow says it sits above the page — not a border, which would be visual
46
+ * noise around a strip whose job is to be glanced at.
40
47
  * @returns one `html` row's payload.
41
48
  */
42
49
  export declare function stripMarkup(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../src/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,uBAAuB,CAAA;AAEpD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,MAAM,CASpC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,WAAW,EAAE,CAKzC"}
1
+ {"version":3,"file":"strip.d.ts","sourceRoot":"","sources":["../src/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,uBAAuB,CAAA;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAUpC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,WAAW,EAAE,CAKzC"}
package/lib/strip.js CHANGED
@@ -32,10 +32,17 @@ export const STRIP_ELEMENT_ID = 'dsh-realtime-strip';
32
32
  /**
33
33
  * The panel's container, as markup.
34
34
  *
35
- * Fixed to the bottom right and deliberately nearly unstyled: it sits inside the host's own page, so it
36
- * inherits that page's font and colour rather than inventing a second visual language beside it. A
37
- * translucent wash rather than a border, because a border here would be visual noise around a strip whose
38
- * job is to be glanced at.
35
+ * Fixed to the bottom right, inheriting the host's font and colour rather than inventing a second visual
36
+ * language beside it — but **opaque**, and that is a correction rather than a taste. This panel sits over
37
+ * the host's own text, so a translucent wash lets the page show through and the two become unreadable
38
+ * together: two texts superimposed, neither of them legible. That was reported from a screenshot of the
39
+ * running app, which is the only way it was ever going to be found — nothing in a DOM test can see what is
40
+ * *behind* the panel.
41
+ *
42
+ * `Canvas` is the theme's own surface colour, so the panel follows light or dark mode without naming a
43
+ * palette of its own, and the 6% tint of the inherited colour keeps the surface from reading as a hole
44
+ * punched in the page. The soft shadow says it sits above the page — not a border, which would be visual
45
+ * noise around a strip whose job is to be glanced at.
39
46
  * @returns one `html` row's payload.
40
47
  */
41
48
  export function stripMarkup() {
@@ -44,7 +51,8 @@ export function stripMarkup() {
44
51
  ' style="position:fixed;right:12px;bottom:12px;z-index:40;',
45
52
  'max-width:26rem;max-height:60vh;overflow:auto;padding:10px 12px;',
46
53
  'font:inherit;color:inherit;line-height:1.4;',
47
- 'background:color-mix(in srgb, currentColor 8%, transparent);border-radius:10px">',
54
+ 'background:color-mix(in srgb, Canvas 94%, currentColor);',
55
+ 'box-shadow:0 4px 16px rgb(0 0 0 / 0.18);border-radius:10px">',
48
56
  '</div>',
49
57
  ].join('');
50
58
  }
package/lib/strip.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"strip.js","sourceRoot":"","sources":["../src/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAA;AAEpD;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,YAAY,gBAAgB,GAAG;QAC/B,2DAA2D;QAC3D,kEAAkE;QAClE,6CAA6C;QAC7C,kFAAkF;QAClF,QAAQ;KACT,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,iBAAiB;QACjB,iHAAiH;QACjH,WAAW;QACX,kIAAkI;QAClI,MAAM;KACP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE;KAC9D,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"strip.js","sourceRoot":"","sources":["../src/strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAA;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,YAAY,gBAAgB,GAAG;QAC/B,2DAA2D;QAC3D,kEAAkE;QAClE,6CAA6C;QAC7C,0DAA0D;QAC1D,8DAA8D;QAC9D,QAAQ;KACT,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,iBAAiB;QACjB,iHAAiH;QACjH,WAAW;QACX,kIAAkI;QAClI,MAAM;KACP,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;QACxD,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE;KAC9D,CAAA;AACH,CAAC"}
package/lib/types.d.ts CHANGED
@@ -61,6 +61,14 @@ export interface RealtimeAudioWsConfig {
61
61
  * more to a stranger than an empty socket.
62
62
  */
63
63
  readonly diagnosticsPath: string;
64
+ /**
65
+ * File the journal is appended to, one JSON object per line. Empty means no file.
66
+ *
67
+ * Off by default, and named by the deployment that wants a recording: the journal is held in memory and
68
+ * served over a route that wants a capability token, so a deployment that needs the record to outlive the
69
+ * process — or to be handed to somebody else — says so here.
70
+ */
71
+ readonly journalPath: string;
64
72
  }
65
73
  /**
66
74
  * 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,0GAA0G;AAC1G,eAAO,MAAM,+BAA+B,OAAO,CAAA;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,8BAA8B,CAAA;AAEnE,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;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;IACrC,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;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,8BAA8B,CAAA;AAEnE,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;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAA;IACrC,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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-realtime-audio-ws",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
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,8 +52,8 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "ws": "^8.22.0",
55
- "dsh-realtime": "^0.2.3",
56
- "dsh-realtime-agent": "^0.2.7"
55
+ "dsh-realtime-agent": "^0.2.8",
56
+ "dsh-realtime": "^0.2.5"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@deepseek-ai/cordis": "^4.0.3",
@@ -359,6 +359,12 @@ export function createAudioClient(deps: ClientAudioDeps): ClientAudio {
359
359
  const source = context.createMediaStreamSource(stream)
360
360
  const node = context.createScriptProcessor(CAPTURE_BUFFER, 1, 1)
361
361
  node.onaudioprocess = (event) => {
362
+ // Every quantum ships, **silent ones included, and that is load-bearing rather than incidental**: the
363
+ // provider's session timeline advances with the audio this client sends, and a context append is
364
+ // placed only while that timeline is advancing — so a client that skipped silent blocks (an
365
+ // obvious-looking saving) would silently freeze the session and lose every append after it. Measured,
366
+ // with the mechanism, in `docs/usable-window.md`; guarded by the capture-continuity case in
367
+ // `tests/client.spec.ts`.
362
368
  const pcm16 = pcm16FromFloat32(event.inputBuffer.getChannelData(0))
363
369
  socket?.send(new Uint8Array(pcm16.buffer))
364
370
  }
package/src/index.ts CHANGED
@@ -72,6 +72,8 @@ export { diagnosticsRoute, type DiagnosticsDeps, type DiagnosticsJournal, type D
72
72
  export { STRIP_ELEMENT_ID, stripBootstrap, stripMarkup, stripRows } from './strip.ts'
73
73
  export type { InjectedGlobalRow, InjectedRow } from './injection.ts'
74
74
 
75
+ import { createJournalFileSink } from './journal-file.ts'
76
+
75
77
  /** Plugin name. */
76
78
  export const name = 'realtime-audio-ws'
77
79
 
@@ -84,6 +86,10 @@ export const Config = Schema.object({
84
86
  // microphone: a connection takes an explicit action, an authenticated one, and its absence is silence.
85
87
  openSessionOnConnect: Schema.boolean().default(DEFAULT_OPEN_SESSION_ON_CONNECT),
86
88
  diagnosticsPath: Schema.string().default(DEFAULT_DIAGNOSTICS_PATH),
89
+ // Empty means no file, and empty is the default: a plugin that writes files because it was installed is a
90
+ // plugin writing files in somebody else's directory. The deployment that wants a recording is the
91
+ // deployment that names a path.
92
+ journalPath: Schema.string().default(''),
87
93
  })
88
94
 
89
95
  /**
@@ -137,6 +143,13 @@ export function apply(ctx: Context, config: RealtimeAudioWsConfig): void {
137
143
  // constructor argument: the plugin that mints the token is the only one that can name it, and it
138
144
  // mints it here, after the journal already exists.
139
145
  journal.addSecrets([token])
146
+ // The record leaves the process when a path is named. Registered *after* the token, so that every entry
147
+ // the file receives has already been through the redaction that knows about it — a sink is handed
148
+ // entries after `record` has redacted them, and that ordering is the reason this is a sink rather than a
149
+ // second writer with its own idea of what a secret looks like.
150
+ if (config.journalPath.length > 0) {
151
+ journal.onEntry(createJournalFileSink(config.journalPath))
152
+ }
140
153
  // The injection event is declared by `@deepseek-ai/dsh-host-webserver`, which this package deliberately
141
154
  // does not import: importing it would augment `Context` with a second declaration of `webServer` and
142
155
  // turn the structural dependency above into a compile error. The name is cast at this one boundary.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A journal sink that appends to a file, so the record outlives the process that made it.
3
+ *
4
+ * The journal has always *held* everything worth knowing and has always been unable to hand it over: it is
5
+ * served over a route that wants a per-boot capability token, which means the record can be read by the page
6
+ * that holds the token and by nobody else, and it cannot be read after the fact at all. Every failure this
7
+ * plugin has had was diagnosed by reading somebody else's log — a harness session file off disk, or a
8
+ * `curl` at a route that answered 401. This is the missing half: the same entries, on disk, readable with
9
+ * `cat` by whoever is handed the file.
10
+ *
11
+ * ## Why `appendFileSync`, deliberately
12
+ *
13
+ * The ordering of a diagnostic record is the record. An asynchronous append can interleave two entries or
14
+ * lose one to a process that exits during the write, and the failure this exists to explain is usually the
15
+ * last thing that happened. So each line is written synchronously, as one line, and a reader can trust that
16
+ * the file is ordered and complete up to the last entry it contains.
17
+ *
18
+ * ## Redaction already happened
19
+ *
20
+ * A sink is handed entries *after* `Journal.record` has redacted them, so the file carries exactly what the
21
+ * route would have served and never the values named in `redactSecrets`. That ordering is the whole reason
22
+ * this is a sink and not a second writer with its own copy of the redaction rules — two code paths that must
23
+ * agree about what a secret looks like are two code paths that eventually disagree.
24
+ *
25
+ * @module dsh-realtime-audio-ws/journal-file
26
+ */
27
+
28
+ import { appendFileSync, mkdirSync } from 'node:fs'
29
+ import { dirname } from 'node:path'
30
+ import type { JournalEntry } from 'dsh-realtime'
31
+
32
+ /**
33
+ * Build a sink that appends each entry to `path` as one JSON object per line.
34
+ *
35
+ * The parent directory is created here, at construction, rather than on the first write. That is the one
36
+ * failure this can have that is worth being loud about — a path that cannot be written — and at construction
37
+ * it is a startup error a caller can refuse to launch on, instead of a silent nothing that looks exactly
38
+ * like a quiet session. A *later* write that fails is swallowed by `Journal.onEntry`'s guard, by design.
39
+ * @param path - absolute or relative file path; the file is appended to, never truncated.
40
+ * @returns the sink, ready for `Journal.onEntry`.
41
+ */
42
+ export function createJournalFileSink(path: string): (entry: JournalEntry) => void {
43
+ mkdirSync(dirname(path), { recursive: true })
44
+ return (entry: JournalEntry): void => {
45
+ appendFileSync(path, `${JSON.stringify(entry)}\n`)
46
+ }
47
+ }
package/src/strip.ts CHANGED
@@ -36,10 +36,17 @@ export const STRIP_ELEMENT_ID = 'dsh-realtime-strip'
36
36
  /**
37
37
  * The panel's container, as markup.
38
38
  *
39
- * Fixed to the bottom right and deliberately nearly unstyled: it sits inside the host's own page, so it
40
- * inherits that page's font and colour rather than inventing a second visual language beside it. A
41
- * translucent wash rather than a border, because a border here would be visual noise around a strip whose
42
- * job is to be glanced at.
39
+ * Fixed to the bottom right, inheriting the host's font and colour rather than inventing a second visual
40
+ * language beside it — but **opaque**, and that is a correction rather than a taste. This panel sits over
41
+ * the host's own text, so a translucent wash lets the page show through and the two become unreadable
42
+ * together: two texts superimposed, neither of them legible. That was reported from a screenshot of the
43
+ * running app, which is the only way it was ever going to be found — nothing in a DOM test can see what is
44
+ * *behind* the panel.
45
+ *
46
+ * `Canvas` is the theme's own surface colour, so the panel follows light or dark mode without naming a
47
+ * palette of its own, and the 6% tint of the inherited colour keeps the surface from reading as a hole
48
+ * punched in the page. The soft shadow says it sits above the page — not a border, which would be visual
49
+ * noise around a strip whose job is to be glanced at.
43
50
  * @returns one `html` row's payload.
44
51
  */
45
52
  export function stripMarkup(): string {
@@ -48,7 +55,8 @@ export function stripMarkup(): string {
48
55
  ' style="position:fixed;right:12px;bottom:12px;z-index:40;',
49
56
  'max-width:26rem;max-height:60vh;overflow:auto;padding:10px 12px;',
50
57
  'font:inherit;color:inherit;line-height:1.4;',
51
- 'background:color-mix(in srgb, currentColor 8%, transparent);border-radius:10px">',
58
+ 'background:color-mix(in srgb, Canvas 94%, currentColor);',
59
+ 'box-shadow:0 4px 16px rgb(0 0 0 / 0.18);border-radius:10px">',
52
60
  '</div>',
53
61
  ].join('')
54
62
  }
package/src/types.ts CHANGED
@@ -67,6 +67,14 @@ export interface RealtimeAudioWsConfig {
67
67
  * more to a stranger than an empty socket.
68
68
  */
69
69
  readonly diagnosticsPath: string
70
+ /**
71
+ * File the journal is appended to, one JSON object per line. Empty means no file.
72
+ *
73
+ * Off by default, and named by the deployment that wants a recording: the journal is held in memory and
74
+ * served over a route that wants a capability token, so a deployment that needs the record to outlive the
75
+ * process — or to be handed to somebody else — says so here.
76
+ */
77
+ readonly journalPath: string
70
78
  }
71
79
 
72
80
  /**