dsh-realtime-agent 0.2.4 → 0.2.6
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/LICENSE +21 -0
- package/lib/bridge.d.ts +4 -1
- package/lib/bridge.d.ts.map +1 -1
- package/lib/bridge.js +10 -1
- package/lib/bridge.js.map +1 -1
- package/lib/index.d.ts +8 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +28 -5
- package/lib/index.js.map +1 -1
- package/package.json +7 -7
- package/src/bridge.ts +13 -0
- package/src/index.ts +43 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Travis Driessen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/lib/bridge.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface DelegationAsker {
|
|
|
26
26
|
* @returns the text, or a marked prefix of it no longer than `maxChars`.
|
|
27
27
|
*/
|
|
28
28
|
export declare function boundAppend(text: string, maxChars?: number): string;
|
|
29
|
+
/** Which channel an append went out on. What an acknowledgement names, and what it does not. */
|
|
30
|
+
export type DelegationAppend = 'commentary' | 'thinking';
|
|
29
31
|
/**
|
|
30
32
|
* Answer one delegation, or tell the model plainly that it could not be answered.
|
|
31
33
|
*
|
|
@@ -35,7 +37,8 @@ export declare function boundAppend(text: string, maxChars?: number): string;
|
|
|
35
37
|
* @param transcript - the conversation so far, as the responder will see it.
|
|
36
38
|
* @param ask - how to reach a responder.
|
|
37
39
|
* @param timeoutMs - bound on waiting for one.
|
|
40
|
+
* @param onAcknowledged - called once per accepted append, which is not once per thing heard.
|
|
38
41
|
* @returns a promise settling once the session has been answered.
|
|
39
42
|
*/
|
|
40
|
-
export declare function answerDelegation(session: Pick<RealtimeSession, 'id' | 'appendCommentary' | 'appendThinking'>, delegation: RealtimeDelegation, transcript: readonly AgentTranscriptLine[], ask: DelegationAsker, timeoutMs: number): Promise<void>;
|
|
43
|
+
export declare function answerDelegation(session: Pick<RealtimeSession, 'id' | 'appendCommentary' | 'appendThinking'>, delegation: RealtimeDelegation, transcript: readonly AgentTranscriptLine[], ask: DelegationAsker, timeoutMs: number, onAcknowledged: (append: DelegationAppend, delegationId: string) => void): Promise<void>;
|
|
41
44
|
//# sourceMappingURL=bridge.d.ts.map
|
package/lib/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE1F;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,sDAAiD,CAAA;AAK/E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAA;CACnG;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAKrF;AAmBD
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE1F;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,sDAAiD,CAAA;AAK/E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAA;CACnG;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAKrF;AAmBD,gGAAgG;AAChG,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,UAAU,CAAA;AAExD;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,kBAAkB,GAAG,gBAAgB,CAAC,EAC5E,UAAU,EAAE,kBAAkB,EAC9B,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,GACvE,OAAO,CAAC,IAAI,CAAC,CAkCf"}
|
package/lib/bridge.js
CHANGED
|
@@ -52,9 +52,10 @@ function settled(ask, request) {
|
|
|
52
52
|
* @param transcript - the conversation so far, as the responder will see it.
|
|
53
53
|
* @param ask - how to reach a responder.
|
|
54
54
|
* @param timeoutMs - bound on waiting for one.
|
|
55
|
+
* @param onAcknowledged - called once per accepted append, which is not once per thing heard.
|
|
55
56
|
* @returns a promise settling once the session has been answered.
|
|
56
57
|
*/
|
|
57
|
-
export async function answerDelegation(session, delegation, transcript, ask, timeoutMs) {
|
|
58
|
+
export async function answerDelegation(session, delegation, transcript, ask, timeoutMs, onAcknowledged) {
|
|
58
59
|
const request = {
|
|
59
60
|
id: delegation.id,
|
|
60
61
|
offsetMs: delegation.offsetMs,
|
|
@@ -68,14 +69,22 @@ export async function answerDelegation(session, delegation, transcript, ask, tim
|
|
|
68
69
|
});
|
|
69
70
|
const answer = await Promise.race([settled(ask, request), expired]);
|
|
70
71
|
const text = typeof answer?.text === 'string' ? answer.text.trim() : '';
|
|
72
|
+
// Every append below awaits the provider's acknowledgement rather than the send — that is the seam's
|
|
73
|
+
// session contract, written that way for a measured reason. So a resolved await **is** the
|
|
74
|
+
// acknowledgement, and this is the only honest place to record one. What it is not is delivery:
|
|
75
|
+
// nothing here says a speaker rendered anything, and keeping those two apart is the whole of
|
|
76
|
+
// invariant 6 — and the reason the fault-injection matrix has a row for exactly this pair.
|
|
71
77
|
if (text.length === 0) {
|
|
72
78
|
await session.appendCommentary(UNANSWERED_NOTICE, delegation.id);
|
|
79
|
+
onAcknowledged('commentary', delegation.id);
|
|
73
80
|
return;
|
|
74
81
|
}
|
|
75
82
|
if (answer?.mode === 'spoken') {
|
|
76
83
|
await session.appendCommentary(boundAppend(text), delegation.id);
|
|
84
|
+
onAcknowledged('commentary', delegation.id);
|
|
77
85
|
return;
|
|
78
86
|
}
|
|
79
87
|
await session.appendThinking(boundAppend(text), delegation.id);
|
|
88
|
+
onAcknowledged('thinking', delegation.id);
|
|
80
89
|
}
|
|
81
90
|
//# sourceMappingURL=bridge.js.map
|
package/lib/bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAiD,MAAM,cAAc,CAAA;AAG9F;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,8CAA8C,CAAA;AAE/E,iGAAiG;AACjG,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAWxC;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAQ,GAAW,gBAAgB;IAC3E,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAA;IACxC,mFAAmF;IACnF,IAAI,QAAQ,IAAI,iBAAiB,CAAC,MAAM;QAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACrF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,GAAG,iBAAiB,CAAA;AAC/E,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,OAAO,CAAC,GAAoB,EAAE,OAA0B;IAC/D,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACnC,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/lib/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import Schema from '@deepseek-ai/schemastery';
|
|
24
24
|
import type { Context } from '@deepseek-ai/cordis';
|
|
25
25
|
import type { RealtimeDelegationSettlement, RealtimeSession, RealtimeSessionHandlers } from 'dsh-realtime';
|
|
26
|
-
import { type DelegationAsker } from './bridge.ts';
|
|
26
|
+
import { type DelegationAppend, type DelegationAsker } from './bridge.ts';
|
|
27
27
|
import { TranscriptBuffer } from './transcript.ts';
|
|
28
28
|
import type { DelegationAnswer, DelegationRequest, RealtimeAgentConfig } from './types.ts';
|
|
29
29
|
declare module '@deepseek-ai/cordis' {
|
|
@@ -133,6 +133,13 @@ export interface HandlerDeps {
|
|
|
133
133
|
readonly onSessionError: (error: Error) => void;
|
|
134
134
|
/** Where output audio is delivered. Called once per provider delta. */
|
|
135
135
|
readonly onAudio: (pcm16: Uint8Array) => void;
|
|
136
|
+
/**
|
|
137
|
+
* Where an accepted append is reported.
|
|
138
|
+
*
|
|
139
|
+
* An acknowledgement, not a delivery: it says the provider took the append, and nothing about
|
|
140
|
+
* whether a speaker ever rendered it. See the seam's session contract and invariant 6.
|
|
141
|
+
*/
|
|
142
|
+
readonly onAcknowledged: (append: DelegationAppend, delegationId: string) => void;
|
|
136
143
|
}
|
|
137
144
|
/**
|
|
138
145
|
* Build the session handlers.
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAsB,4BAA4B,EAAE,eAAe,EAAE,uBAAuB,EAAsB,MAAM,cAAc,CAAA;AAClJ,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAsB,4BAA4B,EAAE,eAAe,EAAE,uBAAuB,EAAsB,MAAM,cAAc,CAAA;AAClJ,OAAO,EAAoB,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AAE3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAE1F,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd;;;;;;WAMG;QACH,2BAA2B,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAA;QAC7H,2EAA2E;QAC3E,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;QAC1C;;;;;;;;;;;;WAYG;QACH,mCAAmC,CAAC,UAAU,EAAE,4BAA4B,GAAG,IAAI,CAAA;QACnF;;;;;;WAMG;QACH,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;QAC/C;;;;;;WAMG;QACH,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;QAC7C;;;;;;WAMG;QACH,sBAAsB,IAAI,IAAI,CAAA;QAC9B;;;;;WAKG;QACH,qBAAqB,IAAI,IAAI,CAAA;KAC9B;CACF;AAED,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAA;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AAErE,wDAAwD;AACxD,eAAO,MAAM,IAAI,mBAAmB,CAAA;AAEpC,sFAAsF;AACtF,eAAO,MAAM,MAAM,UAAwB,CAAA;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;aAQjB,CAAA;AAEF,yGAAyG;AACzG,MAAM,WAAW,WAAW;IAC1B,wCAAwC;IACxC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAA;IACrC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,eAAe,GAAG,SAAS,CAAA;IACnD,gCAAgC;IAChC,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAA;IAC7B,gCAAgC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/C,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;CAClF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,uBAAuB,CAyBzE;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAwHrE"}
|
package/lib/index.js
CHANGED
|
@@ -66,8 +66,7 @@ export function createHandlers(deps) {
|
|
|
66
66
|
return;
|
|
67
67
|
// Handlers are synchronous, so the answer is dispatched rather than awaited. A rejection is
|
|
68
68
|
// reported rather than thrown: an unanswered delegation is already the failure path.
|
|
69
|
-
void answerDelegation(session, delegation, deps.transcript.lines(), deps.ask, deps.timeoutMs)
|
|
70
|
-
.catch(deps.onSessionError);
|
|
69
|
+
void answerDelegation(session, delegation, deps.transcript.lines(), deps.ask, deps.timeoutMs, deps.onAcknowledged).catch(deps.onSessionError);
|
|
71
70
|
},
|
|
72
71
|
onAudio: (pcm16) => { deps.onAudio(pcm16); },
|
|
73
72
|
onClosed: () => { deps.onClosed(); },
|
|
@@ -81,15 +80,38 @@ export function createHandlers(deps) {
|
|
|
81
80
|
*/
|
|
82
81
|
export function apply(ctx, config) {
|
|
83
82
|
const transcript = new TranscriptBuffer(config.maxTranscriptChars);
|
|
83
|
+
// This plugin owns the session, so it is the only one that can honestly write these entries. The audio
|
|
84
|
+
// route emits a *request* to open a session and deliberately records nothing for it: recording a
|
|
85
|
+
// request as an event is how a journal starts lying.
|
|
86
|
+
const journal = ctx.realtime.journal;
|
|
84
87
|
let session;
|
|
85
88
|
const handlers = createHandlers({
|
|
86
89
|
transcript,
|
|
87
90
|
session: () => session,
|
|
88
91
|
ask: request => ctx.serial('realtime-agent/delegation', request),
|
|
89
92
|
timeoutMs: config.delegationTimeoutMs,
|
|
90
|
-
onClosed: () => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
onClosed: () => {
|
|
94
|
+
session = undefined;
|
|
95
|
+
journal.record('session.closed', {});
|
|
96
|
+
},
|
|
97
|
+
onSessionError: (error) => {
|
|
98
|
+
ctx.emit('realtime-agent/error', error);
|
|
99
|
+
// The class, never the message. A provider error can carry the key it refused, and this plugin
|
|
100
|
+
// holds no credential to redact against — so it records the category and leaves the text to the
|
|
101
|
+
// plugin that does. Naming the setting instead of repeating the value, applied to a log line.
|
|
102
|
+
journal.record('session.failed', { class: error.name });
|
|
103
|
+
},
|
|
104
|
+
onAudio: (pcm16) => {
|
|
105
|
+
ctx.emit('realtime-agent/audio', pcm16);
|
|
106
|
+
// Handed to the transport: all the host can observe, and no more (invariant 6).
|
|
107
|
+
journal.record('speech.sent', { bytes: String(pcm16.byteLength) });
|
|
108
|
+
},
|
|
109
|
+
onAcknowledged: (append, delegationId) => {
|
|
110
|
+
// The one place an acknowledgement can honestly be recorded: the seam's appends resolve on the
|
|
111
|
+
// provider's confirmation, not on the send. Note what is deliberately absent beside it — no entry
|
|
112
|
+
// anywhere claims the audio was heard, which is the pair invariant 6 exists to keep apart.
|
|
113
|
+
journal.record('append.acknowledged', { append, delegationId });
|
|
114
|
+
},
|
|
93
115
|
});
|
|
94
116
|
/**
|
|
95
117
|
* Open a session, or return the one already open.
|
|
@@ -109,6 +131,7 @@ export function apply(ctx, config) {
|
|
|
109
131
|
handlers,
|
|
110
132
|
});
|
|
111
133
|
session = opened;
|
|
134
|
+
journal.record('session.opened', { provider: config.provider, model: config.model });
|
|
112
135
|
return opened;
|
|
113
136
|
};
|
|
114
137
|
/**
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAG7C,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-realtime-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Delegation bridge for the dsh-realtime seam: answers what the voice model delegates, or says plainly that it cannot.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -41,12 +41,8 @@
|
|
|
41
41
|
"access": "public",
|
|
42
42
|
"registry": "https://registry.npmjs.org"
|
|
43
43
|
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsc -b",
|
|
46
|
-
"typecheck": "tsc -b --dry"
|
|
47
|
-
},
|
|
48
44
|
"dependencies": {
|
|
49
|
-
"dsh-realtime": "
|
|
45
|
+
"dsh-realtime": "^0.2.2"
|
|
50
46
|
},
|
|
51
47
|
"peerDependencies": {
|
|
52
48
|
"@deepseek-ai/cordis": "^4.0.3",
|
|
@@ -57,5 +53,9 @@
|
|
|
57
53
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
58
54
|
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
|
|
59
55
|
"@deepseek-ai/schemastery": "^3.18.4"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc -b",
|
|
59
|
+
"typecheck": "tsc -b --dry"
|
|
60
60
|
}
|
|
61
|
-
}
|
|
61
|
+
}
|
package/src/bridge.ts
CHANGED
|
@@ -55,6 +55,9 @@ function settled(ask: DelegationAsker, request: DelegationRequest): Promise<Dele
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/** Which channel an append went out on. What an acknowledgement names, and what it does not. */
|
|
59
|
+
export type DelegationAppend = 'commentary' | 'thinking'
|
|
60
|
+
|
|
58
61
|
/**
|
|
59
62
|
* Answer one delegation, or tell the model plainly that it could not be answered.
|
|
60
63
|
*
|
|
@@ -64,6 +67,7 @@ function settled(ask: DelegationAsker, request: DelegationRequest): Promise<Dele
|
|
|
64
67
|
* @param transcript - the conversation so far, as the responder will see it.
|
|
65
68
|
* @param ask - how to reach a responder.
|
|
66
69
|
* @param timeoutMs - bound on waiting for one.
|
|
70
|
+
* @param onAcknowledged - called once per accepted append, which is not once per thing heard.
|
|
67
71
|
* @returns a promise settling once the session has been answered.
|
|
68
72
|
*/
|
|
69
73
|
export async function answerDelegation(
|
|
@@ -72,6 +76,7 @@ export async function answerDelegation(
|
|
|
72
76
|
transcript: readonly AgentTranscriptLine[],
|
|
73
77
|
ask: DelegationAsker,
|
|
74
78
|
timeoutMs: number,
|
|
79
|
+
onAcknowledged: (append: DelegationAppend, delegationId: string) => void,
|
|
75
80
|
): Promise<void> {
|
|
76
81
|
const request: DelegationRequest = {
|
|
77
82
|
id: delegation.id,
|
|
@@ -89,13 +94,21 @@ export async function answerDelegation(
|
|
|
89
94
|
const answer = await Promise.race([settled(ask, request), expired])
|
|
90
95
|
const text = typeof answer?.text === 'string' ? answer.text.trim() : ''
|
|
91
96
|
|
|
97
|
+
// Every append below awaits the provider's acknowledgement rather than the send — that is the seam's
|
|
98
|
+
// session contract, written that way for a measured reason. So a resolved await **is** the
|
|
99
|
+
// acknowledgement, and this is the only honest place to record one. What it is not is delivery:
|
|
100
|
+
// nothing here says a speaker rendered anything, and keeping those two apart is the whole of
|
|
101
|
+
// invariant 6 — and the reason the fault-injection matrix has a row for exactly this pair.
|
|
92
102
|
if (text.length === 0) {
|
|
93
103
|
await session.appendCommentary(UNANSWERED_NOTICE, delegation.id)
|
|
104
|
+
onAcknowledged('commentary', delegation.id)
|
|
94
105
|
return
|
|
95
106
|
}
|
|
96
107
|
if (answer?.mode === 'spoken') {
|
|
97
108
|
await session.appendCommentary(boundAppend(text), delegation.id)
|
|
109
|
+
onAcknowledged('commentary', delegation.id)
|
|
98
110
|
return
|
|
99
111
|
}
|
|
100
112
|
await session.appendThinking(boundAppend(text), delegation.id)
|
|
113
|
+
onAcknowledged('thinking', delegation.id)
|
|
101
114
|
}
|
package/src/index.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import Schema from '@deepseek-ai/schemastery'
|
|
25
25
|
import type { Context } from '@deepseek-ai/cordis'
|
|
26
26
|
import type { RealtimeDelegation, RealtimeDelegationSettlement, RealtimeSession, RealtimeSessionHandlers, RealtimeTranscript } from 'dsh-realtime'
|
|
27
|
-
import { answerDelegation, type DelegationAsker } from './bridge.ts'
|
|
27
|
+
import { answerDelegation, type DelegationAppend, type DelegationAsker } from './bridge.ts'
|
|
28
28
|
import { voiceToolDefinitions } from './tools.ts'
|
|
29
29
|
import { TranscriptBuffer } from './transcript.ts'
|
|
30
30
|
import type { DelegationAnswer, DelegationRequest, RealtimeAgentConfig } from './types.ts'
|
|
@@ -133,6 +133,13 @@ export interface HandlerDeps {
|
|
|
133
133
|
readonly onSessionError: (error: Error) => void
|
|
134
134
|
/** Where output audio is delivered. Called once per provider delta. */
|
|
135
135
|
readonly onAudio: (pcm16: Uint8Array) => void
|
|
136
|
+
/**
|
|
137
|
+
* Where an accepted append is reported.
|
|
138
|
+
*
|
|
139
|
+
* An acknowledgement, not a delivery: it says the provider took the append, and nothing about
|
|
140
|
+
* whether a speaker ever rendered it. See the seam's session contract and invariant 6.
|
|
141
|
+
*/
|
|
142
|
+
readonly onAcknowledged: (append: DelegationAppend, delegationId: string) => void
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
/**
|
|
@@ -152,8 +159,14 @@ export function createHandlers(deps: HandlerDeps): RealtimeSessionHandlers {
|
|
|
152
159
|
if (session === undefined) return
|
|
153
160
|
// Handlers are synchronous, so the answer is dispatched rather than awaited. A rejection is
|
|
154
161
|
// reported rather than thrown: an unanswered delegation is already the failure path.
|
|
155
|
-
void answerDelegation(
|
|
156
|
-
|
|
162
|
+
void answerDelegation(
|
|
163
|
+
session,
|
|
164
|
+
delegation,
|
|
165
|
+
deps.transcript.lines(),
|
|
166
|
+
deps.ask,
|
|
167
|
+
deps.timeoutMs,
|
|
168
|
+
deps.onAcknowledged,
|
|
169
|
+
).catch(deps.onSessionError)
|
|
157
170
|
},
|
|
158
171
|
onAudio: (pcm16: Uint8Array): void => { deps.onAudio(pcm16) },
|
|
159
172
|
onClosed: (): void => { deps.onClosed() },
|
|
@@ -168,6 +181,10 @@ export function createHandlers(deps: HandlerDeps): RealtimeSessionHandlers {
|
|
|
168
181
|
*/
|
|
169
182
|
export function apply(ctx: Context, config: RealtimeAgentConfig): void {
|
|
170
183
|
const transcript = new TranscriptBuffer(config.maxTranscriptChars)
|
|
184
|
+
// This plugin owns the session, so it is the only one that can honestly write these entries. The audio
|
|
185
|
+
// route emits a *request* to open a session and deliberately records nothing for it: recording a
|
|
186
|
+
// request as an event is how a journal starts lying.
|
|
187
|
+
const journal = ctx.realtime.journal
|
|
171
188
|
let session: RealtimeSession | undefined
|
|
172
189
|
|
|
173
190
|
const handlers = createHandlers({
|
|
@@ -175,9 +192,28 @@ export function apply(ctx: Context, config: RealtimeAgentConfig): void {
|
|
|
175
192
|
session: () => session,
|
|
176
193
|
ask: request => ctx.serial('realtime-agent/delegation', request),
|
|
177
194
|
timeoutMs: config.delegationTimeoutMs,
|
|
178
|
-
onClosed: () => {
|
|
179
|
-
|
|
180
|
-
|
|
195
|
+
onClosed: () => {
|
|
196
|
+
session = undefined
|
|
197
|
+
journal.record('session.closed', {})
|
|
198
|
+
},
|
|
199
|
+
onSessionError: (error) => {
|
|
200
|
+
ctx.emit('realtime-agent/error', error)
|
|
201
|
+
// The class, never the message. A provider error can carry the key it refused, and this plugin
|
|
202
|
+
// holds no credential to redact against — so it records the category and leaves the text to the
|
|
203
|
+
// plugin that does. Naming the setting instead of repeating the value, applied to a log line.
|
|
204
|
+
journal.record('session.failed', { class: error.name })
|
|
205
|
+
},
|
|
206
|
+
onAudio: (pcm16) => {
|
|
207
|
+
ctx.emit('realtime-agent/audio', pcm16)
|
|
208
|
+
// Handed to the transport: all the host can observe, and no more (invariant 6).
|
|
209
|
+
journal.record('speech.sent', { bytes: String(pcm16.byteLength) })
|
|
210
|
+
},
|
|
211
|
+
onAcknowledged: (append, delegationId) => {
|
|
212
|
+
// The one place an acknowledgement can honestly be recorded: the seam's appends resolve on the
|
|
213
|
+
// provider's confirmation, not on the send. Note what is deliberately absent beside it — no entry
|
|
214
|
+
// anywhere claims the audio was heard, which is the pair invariant 6 exists to keep apart.
|
|
215
|
+
journal.record('append.acknowledged', { append, delegationId })
|
|
216
|
+
},
|
|
181
217
|
})
|
|
182
218
|
|
|
183
219
|
/**
|
|
@@ -197,6 +233,7 @@ export function apply(ctx: Context, config: RealtimeAgentConfig): void {
|
|
|
197
233
|
handlers,
|
|
198
234
|
})
|
|
199
235
|
session = opened
|
|
236
|
+
journal.record('session.opened', { provider: config.provider, model: config.model })
|
|
200
237
|
return opened
|
|
201
238
|
}
|
|
202
239
|
|