@kuralle-syrinx/cf-agents 4.4.0 → 4.5.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/dist/build-session.d.ts +105 -0
- package/dist/build-session.d.ts.map +1 -0
- package/dist/build-session.js +74 -0
- package/dist/build-session.js.map +1 -0
- package/dist/connection-socket.d.ts +41 -0
- package/dist/connection-socket.d.ts.map +1 -0
- package/dist/connection-socket.js +90 -0
- package/dist/connection-socket.js.map +1 -0
- package/dist/durable-history.d.ts +17 -0
- package/dist/durable-history.d.ts.map +1 -0
- package/dist/durable-history.js +58 -0
- package/dist/durable-history.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/r2-recorder.d.ts +28 -0
- package/dist/r2-recorder.d.ts.map +1 -0
- package/dist/r2-recorder.js +303 -0
- package/dist/r2-recorder.js.map +1 -0
- package/dist/real-agent.compile-check.d.ts +12 -0
- package/dist/real-agent.compile-check.d.ts.map +1 -0
- package/dist/real-agent.compile-check.js +35 -0
- package/dist/real-agent.compile-check.js.map +1 -0
- package/dist/with-voice.d.ts +150 -0
- package/dist/with-voice.d.ts.map +1 -0
- package/dist/with-voice.js +359 -0
- package/dist/with-voice.js.map +1 -0
- package/package.json +26 -13
- package/src/build-session.test.ts +125 -0
- package/src/connection-socket.test.ts +114 -0
- package/src/r2-recorder.test.ts +185 -0
- package/src/with-voice.test.ts +798 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { VoiceAgentSession, type IdleTimeoutConfig, type Reasoner, type ReasonerMessage, type ReasonerSessionStore, type VoicePlugin, type PluginConfig } from "@kuralle-syrinx/core";
|
|
2
|
+
import { type RealtimeAdapter } from "@kuralle-syrinx/realtime";
|
|
3
|
+
/** Per-session context handed to every pipeline factory. */
|
|
4
|
+
export interface VoicePipelineContext {
|
|
5
|
+
readonly sessionId: string;
|
|
6
|
+
/**
|
|
7
|
+
* G4 resume state for the session, present when durable history is on. The
|
|
8
|
+
* `front()` factory wires it into the adapter: `resumeHistory: ctx.resume.history`
|
|
9
|
+
* on replay providers (OpenAI), `sessionResumptionHandle: ctx.resume.providerHandle`
|
|
10
|
+
* on native-resume providers (Gemini — do NOT also replay, R6).
|
|
11
|
+
*/
|
|
12
|
+
readonly resume?: {
|
|
13
|
+
/** Live view of the durable transcript (call again on reconnect for the current state). */
|
|
14
|
+
readonly history: () => readonly {
|
|
15
|
+
readonly role: "user" | "assistant";
|
|
16
|
+
readonly content: string;
|
|
17
|
+
}[];
|
|
18
|
+
/** Latest provider-native resume handle, when one was issued. */
|
|
19
|
+
readonly providerHandle?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Host wiring (withVoice) threaded into the assembled session. */
|
|
23
|
+
export interface VoiceSessionWiring {
|
|
24
|
+
/** G4: durable store for the cascaded ReasoningBridge's conversation history. */
|
|
25
|
+
readonly reasonerSessionStore?: ReasonerSessionStore;
|
|
26
|
+
/** G4: prior-context provider for realtime delegate turns (live view of durable history). */
|
|
27
|
+
readonly contextProvider?: () => readonly ReasonerMessage[];
|
|
28
|
+
/** G3: ms before a pending tool call fires its "delayed" (still-working) cue. */
|
|
29
|
+
readonly delayCueAfterMs?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Idle-timeout override. Defaults to the 15s telephony re-engagement. Browser/edge
|
|
32
|
+
* hosts pass `{ durationMs: 0 }` to disable it — a playground/demo user granting mic
|
|
33
|
+
* or reading the UI should never be nagged ("Are you still there?") or disconnected.
|
|
34
|
+
*/
|
|
35
|
+
readonly idleTimeout?: Partial<IdleTimeoutConfig>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Realtime front pipeline: a single speech-to-speech model (Gemini Live,
|
|
39
|
+
* OpenAI Realtime, …) owns STT+TTS+turn-taking; the reasoner is consulted via a
|
|
40
|
+
* front-model delegate tool. Assembled exactly as the verified realtime path:
|
|
41
|
+
* `plugins: { realtime: {} }`, `endpointingOwner: "timer"`.
|
|
42
|
+
*/
|
|
43
|
+
export interface RealtimePipeline<Env> {
|
|
44
|
+
readonly kind: "realtime";
|
|
45
|
+
/** The realtime front adapter, e.g. `fromGeminiLive(...)` / `fromOpenAIRealtime(...)`. */
|
|
46
|
+
readonly front: (env: Env, ctx: VoicePipelineContext) => RealtimeAdapter;
|
|
47
|
+
/** Name of the front-model tool routed to the reasoner. @default "consult_knowledge" */
|
|
48
|
+
readonly delegateToolName?: string;
|
|
49
|
+
/**
|
|
50
|
+
* How the delegate answer reaches the front model (G1): `"envelope"` (default) wraps
|
|
51
|
+
* it as a `DelegateResultEnvelope` (`response_text` + `require_repeat_verbatim`);
|
|
52
|
+
* `"string"` injects the raw answer.
|
|
53
|
+
*/
|
|
54
|
+
readonly toolResultFormat?: "envelope" | "string";
|
|
55
|
+
/** Optional `render` directive included in the envelope, e.g. `"translate_faithfully"`. */
|
|
56
|
+
readonly renderDirective?: string;
|
|
57
|
+
}
|
|
58
|
+
/** A cascaded-stage plugin plus its `VoiceAgentSession` plugin config. */
|
|
59
|
+
export interface CascadedStage {
|
|
60
|
+
readonly plugin: VoicePlugin;
|
|
61
|
+
readonly config?: PluginConfig;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Cascaded pipeline: discrete STT → reasoner → TTS stages (optionally VAD + a
|
|
65
|
+
* Smart-Turn EOS). Assembled exactly as the verified cascaded path:
|
|
66
|
+
* `plugins: { stt, vad?, eos?, bridge, tts }`, reasoner wrapped in a
|
|
67
|
+
* `ReasoningBridge` registered as `"bridge"`.
|
|
68
|
+
*/
|
|
69
|
+
export type CascadedEndpointingOwner = "provider_stt" | "smart_turn";
|
|
70
|
+
export interface CascadedPipeline<Env> {
|
|
71
|
+
readonly kind: "cascaded";
|
|
72
|
+
readonly stt: (env: Env, ctx: VoicePipelineContext) => CascadedStage;
|
|
73
|
+
readonly tts: (env: Env, ctx: VoicePipelineContext) => CascadedStage;
|
|
74
|
+
readonly vad?: (env: Env, ctx: VoicePipelineContext) => CascadedStage;
|
|
75
|
+
/** Optional EOS stage. Returning `undefined` is equivalent to omitting the stage. */
|
|
76
|
+
readonly eos?: (env: Env, ctx: VoicePipelineContext) => CascadedStage | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Which component owns end-of-speech. @default "provider_stt". Set to
|
|
79
|
+
* "smart_turn" when supplying an `eos` stage. May be a static literal or an
|
|
80
|
+
* `(env) => owner` factory for per-request selection (e.g. smart_turn only
|
|
81
|
+
* when Workers AI is bound).
|
|
82
|
+
*/
|
|
83
|
+
readonly endpointingOwner?: CascadedEndpointingOwner | ((env: Env) => CascadedEndpointingOwner);
|
|
84
|
+
/**
|
|
85
|
+
* Fallback timeout (ms) before the engine force-finalizes a turn when the STT provider's own
|
|
86
|
+
* endpointing/finalize never fires. Maps to `VoiceAgentSession`'s `sttForceFinalizeTimeoutMs`
|
|
87
|
+
* (engine default 7000). Set it when a provider-endpointed cascade tunes this (e.g. Deepgram at 3500).
|
|
88
|
+
*/
|
|
89
|
+
readonly sttForceFinalizeTimeoutMs?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Speculative generation: start the reasoner on an eager end-of-turn signal
|
|
92
|
+
* (`eos.interim` — Deepgram Flux `eager_eot_threshold`, smart-turn interim) and
|
|
93
|
+
* commit/discard when the endpoint confirms. Trades extra LLM calls for
|
|
94
|
+
* parallelizing LLM TTFT with endpoint confirmation. @default false
|
|
95
|
+
*/
|
|
96
|
+
readonly speculative?: boolean;
|
|
97
|
+
}
|
|
98
|
+
export type VoicePipeline<Env> = RealtimePipeline<Env> | CascadedPipeline<Env>;
|
|
99
|
+
/**
|
|
100
|
+
* Assemble a `VoiceAgentSession` for the configured pipeline. This is the single
|
|
101
|
+
* place that maps the high-level pipeline config onto Syrinx's plugin slots, so
|
|
102
|
+
* the realtime and cascaded shapes stay first-class instead of mode-flagged.
|
|
103
|
+
*/
|
|
104
|
+
export declare function buildVoiceSession<Env>(pipeline: VoicePipeline<Env>, env: Env, reasoner: Reasoner | undefined, ctx: VoicePipelineContext, wiring?: VoiceSessionWiring): VoiceAgentSession;
|
|
105
|
+
//# sourceMappingURL=build-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-session.d.ts","sourceRoot":"","sources":["../src/build-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { VoiceAgentSession, } from "@kuralle-syrinx/core";
|
|
3
|
+
import { RealtimeBridge } from "@kuralle-syrinx/realtime";
|
|
4
|
+
import { ReasoningBridge } from "@kuralle-syrinx/aisdk";
|
|
5
|
+
/**
|
|
6
|
+
* Assemble a `VoiceAgentSession` for the configured pipeline. This is the single
|
|
7
|
+
* place that maps the high-level pipeline config onto Syrinx's plugin slots, so
|
|
8
|
+
* the realtime and cascaded shapes stay first-class instead of mode-flagged.
|
|
9
|
+
*/
|
|
10
|
+
export function buildVoiceSession(pipeline, env, reasoner, ctx, wiring = {}) {
|
|
11
|
+
if (pipeline.kind === "realtime") {
|
|
12
|
+
const front = pipeline.front(env, ctx);
|
|
13
|
+
const bridge = new RealtimeBridge(front, reasoner, pipeline.delegateToolName, {
|
|
14
|
+
...(pipeline.toolResultFormat !== undefined ? { toolResultFormat: pipeline.toolResultFormat } : {}),
|
|
15
|
+
...(pipeline.renderDirective !== undefined ? { renderDirective: pipeline.renderDirective } : {}),
|
|
16
|
+
...(wiring.contextProvider ? { contextProvider: wiring.contextProvider } : {}),
|
|
17
|
+
});
|
|
18
|
+
const session = new VoiceAgentSession({
|
|
19
|
+
plugins: { realtime: {} },
|
|
20
|
+
endpointingOwner: "timer",
|
|
21
|
+
...(wiring.delayCueAfterMs !== undefined ? { delayCueAfterMs: wiring.delayCueAfterMs } : {}),
|
|
22
|
+
...(wiring.idleTimeout !== undefined ? { idleTimeout: wiring.idleTimeout } : {}),
|
|
23
|
+
});
|
|
24
|
+
session.registerPlugin("realtime", bridge);
|
|
25
|
+
return session;
|
|
26
|
+
}
|
|
27
|
+
if (!reasoner) {
|
|
28
|
+
throw new Error("withVoice: a cascaded pipeline needs a reasoner. Set `reasoner` in the options, " +
|
|
29
|
+
"or expose a kuralle `runtime` on the Agent so it defaults to fromKuralleRuntime(this.runtime).");
|
|
30
|
+
}
|
|
31
|
+
const stt = pipeline.stt(env, ctx);
|
|
32
|
+
const tts = pipeline.tts(env, ctx);
|
|
33
|
+
const vad = pipeline.vad?.(env, ctx);
|
|
34
|
+
const eos = pipeline.eos?.(env, ctx);
|
|
35
|
+
const endpointingOwner = typeof pipeline.endpointingOwner === "function"
|
|
36
|
+
? pipeline.endpointingOwner(env)
|
|
37
|
+
: pipeline.endpointingOwner;
|
|
38
|
+
if (endpointingOwner === "smart_turn" && !eos) {
|
|
39
|
+
throw new Error('withVoice: a cascaded pipeline with endpointingOwner "smart_turn" must provide an `eos` stage ' +
|
|
40
|
+
"(e.g. a PipecatEOSPlugin); otherwise no component owns end-of-speech and turns never complete.");
|
|
41
|
+
}
|
|
42
|
+
const plugins = {
|
|
43
|
+
stt: stt.config ?? {},
|
|
44
|
+
bridge: {},
|
|
45
|
+
tts: tts.config ?? {},
|
|
46
|
+
};
|
|
47
|
+
if (vad)
|
|
48
|
+
plugins["vad"] = vad.config ?? {};
|
|
49
|
+
if (eos)
|
|
50
|
+
plugins["eos"] = eos.config ?? {};
|
|
51
|
+
const session = new VoiceAgentSession({
|
|
52
|
+
plugins,
|
|
53
|
+
endpointingOwner: endpointingOwner ?? "provider_stt",
|
|
54
|
+
...(pipeline.sttForceFinalizeTimeoutMs !== undefined
|
|
55
|
+
? { sttForceFinalizeTimeoutMs: pipeline.sttForceFinalizeTimeoutMs }
|
|
56
|
+
: {}),
|
|
57
|
+
...(wiring.delayCueAfterMs !== undefined ? { delayCueAfterMs: wiring.delayCueAfterMs } : {}),
|
|
58
|
+
...(wiring.idleTimeout !== undefined ? { idleTimeout: wiring.idleTimeout } : {}),
|
|
59
|
+
});
|
|
60
|
+
session.registerPlugin("stt", stt.plugin);
|
|
61
|
+
session.registerPlugin("bridge", new ReasoningBridge(reasoner, {
|
|
62
|
+
...(wiring.reasonerSessionStore
|
|
63
|
+
? { sessionStore: wiring.reasonerSessionStore, sessionId: ctx.sessionId }
|
|
64
|
+
: {}),
|
|
65
|
+
...(pipeline.speculative ? { speculative: true } : {}),
|
|
66
|
+
}));
|
|
67
|
+
session.registerPlugin("tts", tts.plugin);
|
|
68
|
+
if (vad)
|
|
69
|
+
session.registerPlugin("vad", vad.plugin);
|
|
70
|
+
if (eos)
|
|
71
|
+
session.registerPlugin("eos", eos.plugin);
|
|
72
|
+
return session;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=build-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-session.js","sourceRoot":"","sources":["../src/build-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ManagedSocket } from "@kuralle-syrinx/ws";
|
|
2
|
+
/**
|
|
3
|
+
* The subset of the `agents` SDK `Connection` (itself a WebSocket) that the
|
|
4
|
+
* voice bridge drives. Kept structural so the package never has to import the
|
|
5
|
+
* full `Connection` type at runtime — the mixin passes the real Connection in.
|
|
6
|
+
*/
|
|
7
|
+
export interface VoiceConnection {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly readyState: number;
|
|
10
|
+
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
11
|
+
close(code?: number, reason?: string): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pumps the Agent's per-connection lifecycle callbacks into the `ManagedSocket`.
|
|
15
|
+
* The mixin feeds this from its patched `onMessage` / `onClose` hooks.
|
|
16
|
+
*/
|
|
17
|
+
export interface ConnectionSocketController {
|
|
18
|
+
message(data: string | ArrayBuffer): void;
|
|
19
|
+
close(code: number, reason: string): void;
|
|
20
|
+
error(err?: Error): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wrap an `agents` `Connection` as a Syrinx `ManagedSocket`.
|
|
24
|
+
*
|
|
25
|
+
* The Agent base delivers inbound frames through its `onMessage(connection, …)`
|
|
26
|
+
* hook — which keeps working across Durable Object hibernation, unlike attaching
|
|
27
|
+
* raw `addEventListener` handlers to the socket (the trap that forces
|
|
28
|
+
* `static options = { hibernate: false }` in the OpenAI/Twilio examples). So this
|
|
29
|
+
* socket is *externally pumped*: the mixin forwards lifecycle events to the
|
|
30
|
+
* returned controller, and the controller fans them out to the handlers
|
|
31
|
+
* `runVoiceEdgeWebSocketConnection` registers.
|
|
32
|
+
*
|
|
33
|
+
* Mirrors the DO-owned controlled socket in `@kuralle-syrinx/ws/workers`, but the
|
|
34
|
+
* Agent has already accepted the connection, so there is no `WebSocketPair` to
|
|
35
|
+
* create or `accept()` to call here.
|
|
36
|
+
*/
|
|
37
|
+
export declare function connectionManagedSocket(connection: VoiceConnection): {
|
|
38
|
+
readonly socket: ManagedSocket;
|
|
39
|
+
readonly controller: ConnectionSocketController;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=connection-socket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-socket.d.ts","sourceRoot":"","sources":["../src/connection-socket.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,oBAAoB,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC;IAC1C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CAC1B;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,eAAe,GAAG;IACpE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,0BAA0B,CAAC;CACjD,CAkEA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
const WEBSOCKET_OPEN = 1;
|
|
3
|
+
/**
|
|
4
|
+
* Wrap an `agents` `Connection` as a Syrinx `ManagedSocket`.
|
|
5
|
+
*
|
|
6
|
+
* The Agent base delivers inbound frames through its `onMessage(connection, …)`
|
|
7
|
+
* hook — which keeps working across Durable Object hibernation, unlike attaching
|
|
8
|
+
* raw `addEventListener` handlers to the socket (the trap that forces
|
|
9
|
+
* `static options = { hibernate: false }` in the OpenAI/Twilio examples). So this
|
|
10
|
+
* socket is *externally pumped*: the mixin forwards lifecycle events to the
|
|
11
|
+
* returned controller, and the controller fans them out to the handlers
|
|
12
|
+
* `runVoiceEdgeWebSocketConnection` registers.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors the DO-owned controlled socket in `@kuralle-syrinx/ws/workers`, but the
|
|
15
|
+
* Agent has already accepted the connection, so there is no `WebSocketPair` to
|
|
16
|
+
* create or `accept()` to call here.
|
|
17
|
+
*/
|
|
18
|
+
export function connectionManagedSocket(connection) {
|
|
19
|
+
const messageHandlers = new Set();
|
|
20
|
+
const closeHandlers = new Set();
|
|
21
|
+
const errorHandlers = new Set();
|
|
22
|
+
let closed = false;
|
|
23
|
+
// Fire the close handlers exactly once, whether the close was initiated by the
|
|
24
|
+
// client (pumped via controller.close from the Agent's onClose hook) or by the
|
|
25
|
+
// edge runner / mixin calling socket.dispose(). The edge runner registers its
|
|
26
|
+
// teardown (recorder finalize, session-lease release) as a close handler, so it
|
|
27
|
+
// must run on a dispose() too — not only when the platform happens to deliver a
|
|
28
|
+
// server-initiated onClose. Iterate a copy so a handler may deregister safely.
|
|
29
|
+
const fireClose = (code, reason) => {
|
|
30
|
+
if (closed)
|
|
31
|
+
return;
|
|
32
|
+
closed = true;
|
|
33
|
+
for (const handler of [...closeHandlers])
|
|
34
|
+
handler(code, reason);
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
socket: {
|
|
38
|
+
get isOpen() {
|
|
39
|
+
return !closed && connection.readyState === WEBSOCKET_OPEN;
|
|
40
|
+
},
|
|
41
|
+
send: (data) => connection.send(data),
|
|
42
|
+
keepAlivePing: () => undefined,
|
|
43
|
+
verify: async () => !closed && connection.readyState === WEBSOCKET_OPEN,
|
|
44
|
+
dispose: () => {
|
|
45
|
+
fireClose(1006, "disposed");
|
|
46
|
+
try {
|
|
47
|
+
connection.close();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* already closing */
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
onOpen: (handler) => {
|
|
54
|
+
// The connection is already open by the time the mixin wraps it.
|
|
55
|
+
if (connection.readyState === WEBSOCKET_OPEN)
|
|
56
|
+
queueMicrotask(handler);
|
|
57
|
+
},
|
|
58
|
+
onMessage: (handler) => {
|
|
59
|
+
messageHandlers.add(handler);
|
|
60
|
+
},
|
|
61
|
+
onClose: (handler) => {
|
|
62
|
+
closeHandlers.add(handler);
|
|
63
|
+
},
|
|
64
|
+
onError: (handler) => {
|
|
65
|
+
errorHandlers.add(handler);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
controller: {
|
|
69
|
+
message(data) {
|
|
70
|
+
if (typeof data === "string") {
|
|
71
|
+
for (const handler of [...messageHandlers])
|
|
72
|
+
handler(data, false);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const bytes = new Uint8Array(data);
|
|
76
|
+
for (const handler of [...messageHandlers])
|
|
77
|
+
handler(bytes, true);
|
|
78
|
+
},
|
|
79
|
+
close(code, reason) {
|
|
80
|
+
fireClose(code, reason);
|
|
81
|
+
},
|
|
82
|
+
error(err) {
|
|
83
|
+
const error = err ?? new Error("WebSocket error");
|
|
84
|
+
for (const handler of [...errorHandlers])
|
|
85
|
+
handler(error);
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=connection-socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-socket.js","sourceRoot":"","sources":["../src/connection-socket.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReasonerMessage, ReasonerSessionStore } from "@kuralle-syrinx/core";
|
|
2
|
+
/** The agents-SDK `Agent.sql` tagged-template surface (synchronous in the DO). */
|
|
3
|
+
export type SqlTag = <T = Record<string, unknown>>(strings: TemplateStringsArray, ...values: (string | number | boolean | null)[]) => T[];
|
|
4
|
+
/**
|
|
5
|
+
* `ReasonerSessionStore` backed by the Agent's SQLite. Also persists the latest
|
|
6
|
+
* provider-native resume handle (Gemini `sessionResumption`) per session.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SqliteReasonerSessionStore implements ReasonerSessionStore {
|
|
9
|
+
private readonly sql;
|
|
10
|
+
constructor(sql: SqlTag);
|
|
11
|
+
load(sessionId: string): readonly ReasonerMessage[];
|
|
12
|
+
save(sessionId: string, messages: readonly ReasonerMessage[]): void;
|
|
13
|
+
clear(sessionId: string): void;
|
|
14
|
+
loadResumeHandle(sessionId: string): string | undefined;
|
|
15
|
+
saveResumeHandle(sessionId: string, handle: string): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=durable-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-history.d.ts","sourceRoot":"","sources":["../src/durable-history.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAElF,kFAAkF;AAClF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/C,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EAAE,KAC5C,CAAC,EAAE,CAAC;AAYT;;;GAGG;AACH,qBAAa,0BAA2B,YAAW,oBAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAexC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE;IAWnD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI;IAQnE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAK9B,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMvD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAI1D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// G4 (RFC bimodel-delegate-seam) — durable reasoner session state over the
|
|
4
|
+
// Agent's DO-SQLite. Survives Durable Object eviction/hibernation, replacing
|
|
5
|
+
// consumer-side module-global memory stores (the SLIIT "F8" finding).
|
|
6
|
+
/**
|
|
7
|
+
* `ReasonerSessionStore` backed by the Agent's SQLite. Also persists the latest
|
|
8
|
+
* provider-native resume handle (Gemini `sessionResumption`) per session.
|
|
9
|
+
*/
|
|
10
|
+
export class SqliteReasonerSessionStore {
|
|
11
|
+
sql;
|
|
12
|
+
constructor(sql) {
|
|
13
|
+
this.sql = sql;
|
|
14
|
+
this.sql `CREATE TABLE IF NOT EXISTS syrinx_reasoner_history (
|
|
15
|
+
session_id TEXT NOT NULL,
|
|
16
|
+
seq INTEGER NOT NULL,
|
|
17
|
+
role TEXT NOT NULL,
|
|
18
|
+
content TEXT NOT NULL,
|
|
19
|
+
tool_call_id TEXT,
|
|
20
|
+
PRIMARY KEY (session_id, seq)
|
|
21
|
+
)`;
|
|
22
|
+
this.sql `CREATE TABLE IF NOT EXISTS syrinx_resume_handle (
|
|
23
|
+
session_id TEXT PRIMARY KEY,
|
|
24
|
+
handle TEXT NOT NULL
|
|
25
|
+
)`;
|
|
26
|
+
}
|
|
27
|
+
load(sessionId) {
|
|
28
|
+
const rows = this.sql `
|
|
29
|
+
SELECT role, content, tool_call_id FROM syrinx_reasoner_history
|
|
30
|
+
WHERE session_id = ${sessionId} ORDER BY seq ASC`;
|
|
31
|
+
return rows.map((row) => ({
|
|
32
|
+
role: row.role,
|
|
33
|
+
content: row.content,
|
|
34
|
+
...(row.tool_call_id ? { toolCallId: row.tool_call_id } : {}),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
save(sessionId, messages) {
|
|
38
|
+
this.sql `DELETE FROM syrinx_reasoner_history WHERE session_id = ${sessionId}`;
|
|
39
|
+
messages.forEach((message, seq) => {
|
|
40
|
+
this.sql `INSERT INTO syrinx_reasoner_history (session_id, seq, role, content, tool_call_id)
|
|
41
|
+
VALUES (${sessionId}, ${seq}, ${message.role}, ${message.content}, ${message.toolCallId ?? null})`;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
clear(sessionId) {
|
|
45
|
+
this.sql `DELETE FROM syrinx_reasoner_history WHERE session_id = ${sessionId}`;
|
|
46
|
+
this.sql `DELETE FROM syrinx_resume_handle WHERE session_id = ${sessionId}`;
|
|
47
|
+
}
|
|
48
|
+
loadResumeHandle(sessionId) {
|
|
49
|
+
const rows = this.sql `
|
|
50
|
+
SELECT handle FROM syrinx_resume_handle WHERE session_id = ${sessionId}`;
|
|
51
|
+
return rows[0]?.handle;
|
|
52
|
+
}
|
|
53
|
+
saveResumeHandle(sessionId, handle) {
|
|
54
|
+
this.sql `DELETE FROM syrinx_resume_handle WHERE session_id = ${sessionId}`;
|
|
55
|
+
this.sql `INSERT INTO syrinx_resume_handle (session_id, handle) VALUES (${sessionId}, ${handle})`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=durable-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"durable-history.js","sourceRoot":"","sources":["../src/durable-history.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,sEAAsE;AAoBtE;;;GAGG;AACH,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QACtC,IAAI,CAAC,GAAG,CAAA;;;;;;;MAON,CAAC;QACH,IAAI,CAAC,GAAG,CAAA;;;MAGN,CAAC;IACL,CAAC;IAED,IAAI,CAAC,SAAiB;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAY;;2BAEV,SAAS,mBAAmB,CAAC;QACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACxB,IAAI,EAAE,GAAG,CAAC,IAA+B;YACzC,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAC,CAAC;IACN,CAAC;IAED,IAAI,CAAC,SAAiB,EAAE,QAAoC;QAC1D,IAAI,CAAC,GAAG,CAAA,0DAA0D,SAAS,EAAE,CAAC;QAC9E,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;YAChC,IAAI,CAAC,GAAG,CAAA;kBACI,SAAS,KAAK,GAAG,KAAK,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,UAAU,IAAI,IAAI,GAAG,CAAC;QACvG,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAiB;QACrB,IAAI,CAAC,GAAG,CAAA,0DAA0D,SAAS,EAAE,CAAC;QAC9E,IAAI,CAAC,GAAG,CAAA,uDAAuD,SAAS,EAAE,CAAC;IAC7E,CAAC;IAED,gBAAgB,CAAC,SAAiB;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAW;mEAC+B,SAAS,EAAE,CAAC;QAC3E,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACzB,CAAC;IAED,gBAAgB,CAAC,SAAiB,EAAE,MAAc;QAChD,IAAI,CAAC,GAAG,CAAA,uDAAuD,SAAS,EAAE,CAAC;QAC3E,IAAI,CAAC,GAAG,CAAA,iEAAiE,SAAS,KAAK,MAAM,GAAG,CAAC;IACnG,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { withVoice, type WithVoiceOptions, type WithVoiceMembers, type ToolCallStartContext, type DelegateQueryContext, type DelegateResultContext, } from "./with-voice.js";
|
|
2
|
+
export type { VoicePipeline, RealtimePipeline, CascadedPipeline, CascadedStage, VoicePipelineContext, VoiceSessionWiring, } from "./build-session.js";
|
|
3
|
+
export { SqliteReasonerSessionStore, type SqlTag } from "./durable-history.js";
|
|
4
|
+
export { connectionManagedSocket } from "./connection-socket.js";
|
|
5
|
+
export type { VoiceConnection, ConnectionSocketController } from "./connection-socket.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// @kuralle-syrinx/cf-agents — add a Syrinx voice pipeline (realtime or cascaded) to
|
|
4
|
+
// a Cloudflare `agents` SDK Agent via the `withVoice(Agent, options)` mixin.
|
|
5
|
+
export { withVoice, } from "./with-voice.js";
|
|
6
|
+
export { SqliteReasonerSessionStore } from "./durable-history.js";
|
|
7
|
+
export { connectionManagedSocket } from "./connection-socket.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,oFAAoF;AACpF,6EAA6E;AAE7E,OAAO,EACL,SAAS,GAMV,MAAM,iBAAiB,CAAC;AASzB,OAAO,EAAE,0BAA0B,EAAe,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EdgeRecorder } from "@kuralle-syrinx/server-websocket/edge";
|
|
2
|
+
export interface R2EdgeRecorderOptions {
|
|
3
|
+
readonly bucket: R2Bucket;
|
|
4
|
+
readonly sessionId: string;
|
|
5
|
+
readonly startedAtMs: number;
|
|
6
|
+
/** Object key prefix. Default "recordings". */
|
|
7
|
+
readonly keyPrefix?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Optional per-stream truncation cap on captured audio bytes. Past it, audio is
|
|
10
|
+
* dropped and the stem is flagged `truncated`. Default: unlimited — memory is bounded
|
|
11
|
+
* by streaming, not by retention, so any length records without OOM.
|
|
12
|
+
*/
|
|
13
|
+
readonly maxBytesPerStream?: number;
|
|
14
|
+
/** Injectable clock (test seam). Defaults to Date.now. */
|
|
15
|
+
readonly now?: () => number;
|
|
16
|
+
}
|
|
17
|
+
export declare class R2EdgeRecorder implements EdgeRecorder {
|
|
18
|
+
#private;
|
|
19
|
+
private readonly opts;
|
|
20
|
+
constructor(opts: R2EdgeRecorderOptions);
|
|
21
|
+
onUserAudio(_contextId: string, audio: Uint8Array, sampleRateHz: number): void;
|
|
22
|
+
onAssistantAudio(_contextId: string, audio: Uint8Array, sampleRateHz: number): void;
|
|
23
|
+
finalize(meta: {
|
|
24
|
+
sessionId: string;
|
|
25
|
+
closedAtMs: number;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=r2-recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-recorder.d.ts","sourceRoot":"","sources":["../src/r2-recorder.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAG1E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,0DAA0D;IAC1D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAkED,qBAAa,cAAe,YAAW,YAAY;;IAQrC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAQxD,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAI9E,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAI7E,QAAQ,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAgL/E"}
|