@kuralle-syrinx/core
v4.6.3
Published
Voice pipeline kernel for Syrinx — pipeline bus, turn arbitration, playout clock, packets, and the framework-agnostic Reasoner seam
Maintainers
Readme
@kuralle-syrinx/core
The Syrinx Kernel v2 — the framework-agnostic core of the voice engine: the PipelineBus, the VoicePlugin contract, the packet vocabulary (stt.* / llm.* / tts.* / reasoning.* …), turn-taking, observability, and the latency-hiding/barge-in primitives. STT/TTS/bridge/transport packages plug into this bus.
This README documents the Reasoner seam added for the Reasoner-bridge generalization; the rest of the kernel surface is in src/index.ts.
Incremental-Unit ledger (dormant)
The IU substrate (IncrementalUnit, InMemoryIuLedger) models speculative voice work as hypothesize → ground → discard: add registers a hypothesis, commit grounds it (optionally with a heard prefix in chars/ms), and revoke discards it. State is monotonic (hypothesized → committed or revoked; terminal ops are idempotent). The ledger is dormant in Sprint 0 — no pipeline consumer is wired yet; C2+ will re-express speculative generation, barge-in truncation, and poison-set bookkeeping on this primitive.
The Reasoner seam
A normalized pull-stream that lets one bridge (ReasoningBridge) drive any reasoning backend (Vercel AI SDK ToolLoopAgent/streamText, Mastra Agent, …) without changing the pipeline primitive. Defined in src/reasoner.ts:
export interface Reasoner {
// Drive one turn. The returned async-iterable IS the response.
// LATENCY INVARIANT: yield every part the instant the backend produces it —
// no buffering, no awaiting to completion, no batching.
stream(turn: ReasonerTurn): AsyncIterable<ReasoningPart>;
}
export interface ReasonerTurn {
readonly userText: string; // finalized transcript (from eos.turn_complete)
readonly messages: readonly ReasonerMessage[]; // prior context — the BRIDGE owns history
readonly signal: AbortSignal; // barge-in / supersede
readonly resume?: { readonly runId: string; readonly data: unknown }; // suspend/resume only
}
export type ReasoningPart =
| { type: "text-delta"; text: string }
| { type: "tool-call"; toolId: string; toolName: string; args: Record<string, unknown> }
| { type: "tool-result"; toolId: string; toolName: string; result: string }
| { type: "suspended"; runId: string; toolId?: string; prompt?: string; payload: unknown } // terminal (HITL)
| { type: "error"; cause: Error; recoverable: boolean } // terminal (→ retry)
| { type: "finish"; reason: "stop" | "tool" | "length"; text: string };Frameworks become a Reasoner via named adapters (never auto-wrap / duck-typing):
- AI SDK →
fromAiSdkAgent/fromStreamText/fromStreamFactory(@kuralle-syrinx/aisdk) - Mastra →
fromMastraAgent(@kuralle-syrinx/mastra)
The bridge (a VoicePlugin) consumes the seam and pushes llm.* packets; the seam adds at most one microtask + a synchronous object remap per part. History, spoken-prefix barge-in, retry, and turn-superseding all stay in the bridge — the backend is stateless-per-turn.
See docs/rfc-reasoner-bridge.md for the full design.
