LiveKit WebRTC migration: replace custom SFU with LiveKit#273
LiveKit WebRTC migration: replace custom SFU with LiveKit#273
Conversation
- Fix Kokoro vocab loading: download tokenizer.json (HuggingFace format) instead of non-existent vocab.json, parse nested model.vocab section - Fix TTSRegistry HashMap random iteration: add Vec priority ordering so kokoro > edge > piper > orpheus > silence is deterministic - Fix Silero VAD model path: add workers/models/vad/ as first candidate - Add max_sentence_frames (312 = ~10s) to prevent 38s+ Whisper chunks - Change VAD init failure from debug! to warn! for visibility - Auto-download Kokoro + Silero models in npm start build script - Clean up stale corrupt vocab.json files on redeploy
- Add Pocket-TTS adapter (Kyutai 117M, Candle) with voice cloning from reference audio and 8 preset voices (Les Mis characters) - Rewrite AIAudioBridge.speak() to use Rust voice/speak-in-call directly: one IPC call does synthesis + mixer injection, audio never leaves Rust. Eliminates broken TS→Rust→TS→Rust round-trip that caused 300s timeouts. - Fix IPC base: add 60s request timeout + reject all pending on socket close/error (was hanging forever on unresponsive Rust worker) - Add shared audio_utils module for TTS adapters (normalize, resample) - Add TTS_MODELS/STT_MODELS centralized model registry in Constants.ts - Add ModelCapabilities with voice cloning, emotion tags, LoRA trainable flags - Automated Pocket-TTS voice embeddings + Silero VAD download in deploy script - Default TTS adapter: pocket (was kokoro)
…ecords)
- PostgresAdapter implementing StorageAdapter trait (outlier B proving the interface)
- Async-native via deadpool-postgres connection pool (no worker thread)
- Type-aware parameter coercion: NULL typing, SQLite boolean (0/1) → PG BOOLEAN,
JSON string → JSONB, numeric coercion via information_schema column introspection
- Schema-qualified table names for multi-tenant isolation
- Auto-schema creation from data shape with ALTER TABLE for new columns
- ON CONFLICT (id) DO NOTHING for idempotent inserts
- MigrationEngine: streaming adapter-to-adapter data transfer
- Cursor-based pagination (no full dataset in memory)
- Per-collection atomic progress tracking (lock-free status reads)
- Pause/resume support via atomic flags
- Background tokio::spawn with MigrationHandle for non-blocking operation
- Verify command compares record counts between source and target
- DataModule adapter routing: Arc<dyn StorageAdapter> with connection string parsing
- postgres:// → PostgresAdapter, file paths → SqliteAdapter
- Migration commands: start, status, pause, resume, verify, cutover, rollback
- 7 TypeScript migration CLI commands (generator-created)
- 12 Rust tests (7 PG adapter + 5 migration), 947 total lib tests pass
Successfully migrated 822,402/823,480 records (99.87%) from 3.1GB SQLite → 1.6GB Postgres.
1,108 failures = all duplicate key violations (live system writes during migration).
…ngterm.db CognitionLogger, SentinelEscalationService, and PersonaTaskExecutor were all writing memories and cognition logs to the shared database.sqlite (129K leaked records, 811MB). Added static dbHandle registry on CognitionLogger, registered from Hippocampus on init. All 10 DataCreate, 2 DataList, 2 DataUpdate calls now route through persona-specific longterm.db handles. Verified: shared DB counts unchanged, 14 persona DBs now receiving cognition tables.
…llback ServerConfig.getDatabasePath() now reads DATABASE_URL from config.env first. If it's a postgres:// URL, returns that (routes to PostgresAdapter in Rust). Otherwise falls back to SQLite path. DatabaseConfig.ts updated with POSTGRES constant and routing documentation. config.env gets DATABASE_URL setting. Verified: Postgres serving all reads/writes, SQLite frozen at migration counts.
Dedicated Postgres cluster on port 5433 with data directory at ~/.continuum/data/postgres/ — all continuum data under one tree. Migrated from system Postgres (port 5432) via pg_dump/restore. DatabaseConfig.ts updated with correct connection string.
- Revert DatabaseConfig port 5433→5432 (dedicated cluster was a mistake) - Add skipCount param to DataList — skips the separate COUNT(*) IPC round-trip when callers only need items (not pagination totals) - Corpus loading (loadCorpusFromORM) now uses skipCount: true, eliminating 12 unnecessary COUNT queries at startup (6 personas × 2) - Created 21 Postgres indexes on commonly queried columns (persona_id, room_id, created_at, etc.) — startup count queries 100-3000x faster - Startup SLOW IPC burst reduced from ~5s to ~1.5s - Zero SLOW IPC entries after startup (steady state is clean)
…rowser canvas rendering Binary Frame Protocol (FrameKind discriminator byte): - 0x01=Audio (PCM16), 0x02=Video (header+pixels), 0x03=AvatarState (JSON) - Backward compatible: legacy messages without prefix treated as raw audio - VideoFrameHeader: 16-byte LE header (width, height, pixelFormat, timestamp, sequence) - VideoPixelFormat enum: RGBA8, NV12, VP8, H264, JPEG (format-agnostic pipe) Rust call_server: - CallJoinResult struct replaces unwieldy tuple returns - video_tx/message_tx broadcast channels for video frames and JSON events - push_video() with mix-minus (see everyone's video but your own) - TestPatternGenerator: SMPTE color bars with moving scan line (160x120 @10FPS) - Auto-starts test video source on call creation (proves plumbing) - Ambient audio sources (TV/music/background) with never-excluded mix-minus Browser AudioStreamClient: - Parses FrameKind byte on incoming binary, routes audio/video/avatar - Prepends 0x01 on outgoing mic audio - onVideoFrame callback with decoded header + pixel data - onAvatarUpdate callback for avatar state JSON Browser LiveWidget: - Canvas element renders RGBA8 video frames via putImageData() - Video display appears above participant grid when stream active - Pixelated rendering for sharp low-res test pattern VoiceOrchestrator: gutted turn-taking gating (broadcast-only, no blocking) Mixer: ambient participant type, is_audio_native filtering GPT-4o Realtime adapter added (OpenAI Realtime API WebSocket) 961 Rust lib tests + 15 integration tests pass. TypeScript compiles clean.
…persona subtitles, persona audio/video
Subtitle timing: - AI transcription via data channel (native LiveKit transcription has browser resolution issues where participant is null) - Caption displays immediately on transcription arrival (before IPC relay) - voice:ai:speech event only extends fade timer, no longer overwrites the correctly-timed caption with a late duplicate - Display name resolved from participants list, not payload UUID - Default caption timeout 15s (was 5s), adjusted by audio duration Video avatar pipeline (E2E proof-of-concept): - RGBA→I420 conversion using BT.601 color space - Procedural avatar: colored circle on dark background, color from identity hash via HSL with fixed saturation/lightness - NativeVideoSource lazily created on first frame (avoids garbled init) - Background loop publishes at 10fps per agent - Browser receives via TrackSubscribed → track.attach() → <video> Rust cleanup: removed dead publish_native_transcription, prefixed reserved fields (_audio_track_sid, _event_tx), added display_name to LiveKitAgent for transcription attribution.
There was a problem hiding this comment.
Pull request overview
Migrates live voice/video transport from a custom WebSocket SFU to LiveKit WebRTC, adds Rust-side orchestration + updated TS bindings, and introduces initial storage migration command scaffolding plus per-persona DB logging improvements.
Changes:
- Replace call server/mixer plumbing with LiveKit agent manager and updated IPC/bindings for voice injection, ambient tracks, and STT utilities.
- Add LiveKit token flow + participant metadata typing shared across TS/Rust, and update live widget styling/rendering expectations.
- Introduce migration command surface area (start/status/verify/cutover/rollback/pause/resume) and per-persona DB handle routing for cognition/memory writes.
Reviewed changes
Copilot reviewed 141 out of 172 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| src/workers/continuum-core/src/voice/call_server_orchestrator_test.rs | Updates test participants to include is_audio_native field |
| src/workers/continuum-core/src/secrets.rs | Broadens env-var patterns that get loaded as secrets |
| src/workers/continuum-core/src/orm/mod.rs | Exposes migration + Postgres adapter modules from ORM |
| src/workers/continuum-core/src/main.rs | Switches server startup from WS call server to LiveKit agent manager |
| src/workers/continuum-core/src/lib.rs | Removes CallManager re-export (call server removed) |
| src/workers/continuum-core/src/ipc/mod.rs | Replaces CallManager plumbing with LiveKitAgentManager in IPC server |
| src/workers/continuum-core/src/ffi/mod.rs | Removes obsolete FFI voice_should_route_to_tts entry point |
| src/workers/continuum-core/src/audio_constants.rs | Adds LiveKit port + dev credential constants |
| src/workers/continuum-core/build.rs | Adds macOS -ObjC link arg for LiveKit WebRTC static libs |
| src/workers/continuum-core/bindings/test-voice-loop.ts | Updates voice loop test to broadcast semantics (no TTS routing check) |
| src/workers/continuum-core/bindings/modules/voice.ts | Extends Voice IPC client API (inject/ambient/STT/transcribe/poll) |
| src/workers/continuum-core/bindings/modules/base.ts | Adds IPC request timeouts + proper pending request rejection |
| src/workers/continuum-core/bindings/benchmark-voice.ts | Removes benchmark for deleted TTS routing check |
| src/workers/continuum-core/bindings/RustCore.ts | Removes FFI binding + wrapper for shouldRouteToTts |
| src/workers/continuum-core/Cargo.toml | Adds LiveKit + Pocket-TTS + Postgres deps |
| src/workers/Cargo.toml | Enables tokio-postgres serde/chrono features |
| src/widgets/live/public/live-widget.scss | Adds video canvas styling + spotlight/participant video visibility rules |
| src/widgets/live/audio-worklet-processor.js | Removes microphone capture worklet (LiveKit client handles capture) |
| src/widgets/live/audio-playback-worklet.js | Removes custom playback worklet (LiveKit handles playback) |
| src/tests/integration/voice-transcription-relay.test.ts | Updates import path to voice orchestrator index entry |
| src/tests/integration/voice-orchestrator.test.ts | Reworks integration tests for broadcast model behavior |
| src/tests/integration/tts-stt-roundtrip.test.ts | Uses central socket-path resolver instead of hardcoded path |
| src/system/voice/shared/VoiceConfig.ts | Derives TTS/STT adapter IDs from centralized model constants + adds helpers |
| src/system/voice/server/index.ts | Ensures TS orchestrator instantiated for event listeners even with Rust routing |
| src/system/voice/server/VoiceWebSocketHandler.ts | Removes setTTSCallback wiring (handled via Rust/LiveKit path) |
| src/system/voice/server/VoiceOrchestratorRustBridge.ts | Reworks Rust bridge: DB user-type lookup + AIAudioBridge registration + broadcast |
| src/system/user/server/modules/consciousness/PersonaTimeline.ts | Suppresses DB events for per-persona timeline writes/updates |
| src/system/user/server/modules/cognitive/memory/Hippocampus.ts | Registers persona DB handle with CognitionLogger; suppresses events on memory writes |
| src/system/user/server/modules/cognition/CognitionLogger.ts | Adds per-persona dbHandle routing + suppressEvents on updates |
| src/system/user/server/modules/PersonaTaskExecutor.ts | Writes memories via data/create to persona DB handle (vs ORM default shared DB) |
| src/system/user/server/modules/PersonaMessageEvaluator.ts | Passes personaId into CognitionLogger plan completion logging |
| src/system/user/server/PersonaUser.ts | Removes TS TTS injection subscription; optimizes corpus load with skipCount |
| src/system/shared/ModelCapabilities.ts | Adds TTS/audio capability types + query helpers |
| src/system/shared/Constants.ts | Centralizes TTS/STT model metadata in TTS_MODELS + STT_MODELS |
| src/system/sentinel/SentinelEscalationService.ts | Writes sentinel memories into persona DB handle via CognitionLogger |
| src/system/secrets/SecretManager.ts | Broadens env-var secret patterns to include secrets/tokens/URLs |
| src/system/data/config/DatabaseConfig.ts | Adds default Postgres connection constant + updated routing docs |
| src/system/config/ServerConfig.ts | Returns Postgres URL if DATABASE_URL is postgres*, else sqlite fallback |
| src/stdout | Adds a committed export artifact file |
| src/shared/version.ts | Bumps version constant |
| src/shared/generated-command-constants.ts | Adds migration command constants |
| src/shared/audio-constants.json | Adds LiveKit constants + dev credentials to generated audio constants |
| src/shared/LiveKitTypes.ts | Adds shared participant role metadata types + helpers |
| src/shared/AudioConstants.ts | Adds LiveKit URL + dev key/secret constants |
| src/server/generated.ts | Regenerates server registry to include migration commands |
| src/scripts/install-livekit.sh | Adds installer script for LiveKit server binary |
| src/scripts/ensure-config.ts | Adds LiveKit config.env template entries |
| src/package.json | Adds LiveKit deps + livekit install script; bumps version |
| src/markdown | Adds a committed report artifact file |
| src/generator/specs/migration-verify.json | Adds generator spec for migration/verify command |
| src/generator/specs/migration-status.json | Adds generator spec for migration/status command |
| src/generator/specs/migration-start.json | Adds generator spec for migration/start command |
| src/generator/specs/migration-rollback.json | Adds generator spec for migration/rollback command |
| src/generator/specs/migration-resume.json | Adds generator spec for migration/resume command |
| src/generator/specs/migration-pause.json | Adds generator spec for migration/pause command |
| src/generator/specs/migration-cutover.json | Adds generator spec for migration/cutover command |
| src/generator/generate-rust-bindings.ts | Runs export tests in release; increases timeout; treats specific SIGSEGV as non-fatal |
| src/generator/generate-audio-constants.ts | Generates LiveKit constants into TS/Rust/audio-constants.json |
| src/generated-command-schemas.json | Regenerates command schemas for migration + skipCount |
| src/commands/voice/synthesize/server/VoiceSynthesizeServerCommand.ts | Derives valid adapters from constants; changes default adapter + voice defaults |
| src/commands/migration/verify/test/integration/MigrationVerifyIntegration.test.ts | Adds generated integration test scaffold for migration/verify |
| src/commands/migration/verify/shared/MigrationVerifyTypes.ts | Adds shared types + executor for migration/verify |
| src/commands/migration/verify/server/MigrationVerifyServerCommand.ts | Forwards migration/verify to Rust IPC |
| src/commands/migration/verify/package.json | Adds command package metadata |
| src/commands/migration/verify/browser/MigrationVerifyBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/verify/README.md | Adds generated README for migration/verify |
| src/commands/migration/verify/.npmignore | Adds npmignore for command package |
| src/commands/migration/status/test/integration/MigrationStatusIntegration.test.ts | Adds generated integration test scaffold for migration/status |
| src/commands/migration/status/shared/MigrationStatusTypes.ts | Adds shared types + executor for migration/status |
| src/commands/migration/status/server/MigrationStatusServerCommand.ts | Forwards migration/status to Rust IPC |
| src/commands/migration/status/package.json | Adds command package metadata |
| src/commands/migration/status/browser/MigrationStatusBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/status/README.md | Adds generated README for migration/status |
| src/commands/migration/status/.npmignore | Adds npmignore for command package |
| src/commands/migration/start/test/integration/MigrationStartIntegration.test.ts | Adds generated integration test scaffold for migration/start |
| src/commands/migration/start/shared/MigrationStartTypes.ts | Adds shared types + executor for migration/start |
| src/commands/migration/start/server/MigrationStartServerCommand.ts | Validates params then forwards migration/start to Rust IPC |
| src/commands/migration/start/package.json | Adds command package metadata |
| src/commands/migration/start/browser/MigrationStartBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/start/README.md | Adds generated README for migration/start |
| src/commands/migration/start/.npmignore | Adds npmignore for command package |
| src/commands/migration/rollback/test/integration/MigrationRollbackIntegration.test.ts | Adds generated integration test scaffold for migration/rollback |
| src/commands/migration/rollback/shared/MigrationRollbackTypes.ts | Adds shared types + executor for migration/rollback |
| src/commands/migration/rollback/server/MigrationRollbackServerCommand.ts | Validates params then forwards migration/rollback to Rust IPC |
| src/commands/migration/rollback/package.json | Adds command package metadata |
| src/commands/migration/rollback/browser/MigrationRollbackBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/rollback/README.md | Adds generated README for migration/rollback |
| src/commands/migration/rollback/.npmignore | Adds npmignore for command package |
| src/commands/migration/resume/test/integration/MigrationResumeIntegration.test.ts | Adds generated integration test scaffold for migration/resume |
| src/commands/migration/resume/shared/MigrationResumeTypes.ts | Adds shared types + executor for migration/resume |
| src/commands/migration/resume/server/MigrationResumeServerCommand.ts | Forwards migration/resume to Rust IPC |
| src/commands/migration/resume/package.json | Adds command package metadata |
| src/commands/migration/resume/browser/MigrationResumeBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/resume/README.md | Adds generated README for migration/resume |
| src/commands/migration/resume/.npmignore | Adds npmignore for command package |
| src/commands/migration/pause/test/integration/MigrationPauseIntegration.test.ts | Adds generated integration test scaffold for migration/pause |
| src/commands/migration/pause/shared/MigrationPauseTypes.ts | Adds shared types + executor for migration/pause |
| src/commands/migration/pause/server/MigrationPauseServerCommand.ts | Forwards migration/pause to Rust IPC |
| src/commands/migration/pause/package.json | Adds command package metadata |
| src/commands/migration/pause/browser/MigrationPauseBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/pause/README.md | Adds generated README for migration/pause |
| src/commands/migration/pause/.npmignore | Adds npmignore for command package |
| src/commands/migration/cutover/test/integration/MigrationCutoverIntegration.test.ts | Adds generated integration test scaffold for migration/cutover |
| src/commands/migration/cutover/shared/MigrationCutoverTypes.ts | Adds shared types + executor for migration/cutover |
| src/commands/migration/cutover/server/MigrationCutoverServerCommand.ts | Validates params then forwards migration/cutover to Rust IPC |
| src/commands/migration/cutover/package.json | Adds command package metadata |
| src/commands/migration/cutover/browser/MigrationCutoverBrowserCommand.ts | Adds browser command forwarding to server |
| src/commands/migration/cutover/README.md | Adds generated README for migration/cutover |
| src/commands/migration/cutover/.npmignore | Adds npmignore for command package |
| src/commands/data/list/shared/DataListTypes.ts | Adds skipCount param to avoid separate COUNT query |
| src/commands/data/list/server/DataListServerCommand.ts | Implements skipCount fast path |
| src/commands/collaboration/live/transcription/server/CollaborationLiveTranscriptionServerCommand.ts | Routes transcriptions through Rust orchestrator + emits directed events |
| src/commands/collaboration/live/leave/server/LiveLeaveServerCommand.ts | Updates orchestrator import to index entry |
| src/commands/collaboration/live/join/shared/LiveJoinTypes.ts | Adds LiveKit token + URL fields to join result |
| src/commands/collaboration/live/join/server/LiveJoinServerCommand.ts | Generates LiveKit token/URL and registers session via orchestrator |
| src/browser/generated.ts | Regenerates browser registry to include migration commands |
Files not reviewed (1)
- src/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * LiveKit API secret (dev mode) | ||
| */ | ||
| export const LIVEKIT_API_SECRET = 'secret'; |
There was a problem hiding this comment.
Bundling a LiveKit API secret into shared TS constants makes it very easy to accidentally ship credentials to the browser or leak them via logs. Even if these are “dev” credentials, the safer pattern is: never define API secrets in any file that can be imported by frontend code; read secrets only on the server (SecretManager/process.env) and ensure the browser only receives a short-lived JWT token.
| * LiveKit API secret (dev mode) | |
| */ | |
| export const LIVEKIT_API_SECRET = 'secret'; | |
| * LiveKit API secret | |
| * | |
| * NOTE: Intentionally not exported from shared constants. | |
| * Server-side code should read the secret from a secure source | |
| * such as environment variables or a secret manager, and expose | |
| * only short-lived tokens (for example, JWT) to the browser. | |
| */ |
|
|
||
| // DEBUG: Log what we're returning to browser | ||
| console.error(`🎙️ LiveJoin RESULT: callId=${call.id.slice(0, 8)}, existed=${existed}, participants=${call.getActiveParticipants().length}, myParticipant=${myParticipant.displayName}`); | ||
| console.error(`🎙️ LiveJoin RESULT: callId=${call.id.slice(0, 8)}, existed=${existed}, participants=${call.getActiveParticipants().length}, myParticipant=${myParticipant.displayName}, livekitToken=${livekitToken.slice(0, 20)}...`); |
There was a problem hiding this comment.
Logging any portion of the LiveKit JWT access token is risky (it’s a bearer credential until expiry, and logs often end up in external systems). Remove token contents from logs (or log only the fact that a token was generated, plus maybe its TTL/length).
| // Piper: sequential queue (blocks Rust event loop ~42s) | ||
| response = await this.enqueuePiper(handle, () => | ||
| this.voiceClient.voiceSynthesize(params.text, params.voice || 'af', adapter) | ||
| this.voiceClient.voiceSynthesize(params.text, params.voice || 'default', adapter) |
There was a problem hiding this comment.
Passing 'default' as the voice fallback is likely invalid for several adapters (e.g., Edge generally expects a concrete voice like en-US-AriaNeural, Kokoro often expects af, etc.). A safer approach is to omit the voice field when not provided (let Rust pick adapter defaults), or choose adapter-specific defaults from TTS_MODELS[...].defaultVoice based on the selected adapter.
| // All other adapters: direct Rust IPC (pocket ~3-6s, kokoro ~97ms, edge <200ms, orpheus ~2-5s, silence instant) | ||
| response = await this.voiceClient.voiceSynthesize( | ||
| params.text, params.voice || 'af', adapter | ||
| params.text, params.voice || 'default', adapter |
There was a problem hiding this comment.
Passing 'default' as the voice fallback is likely invalid for several adapters (e.g., Edge generally expects a concrete voice like en-US-AriaNeural, Kokoro often expects af, etc.). A safer approach is to omit the voice field when not provided (let Rust pick adapter defaults), or choose adapter-specific defaults from TTS_MODELS[...].defaultVoice based on the selected adapter.
| DataCreate.execute({ | ||
| dbHandle: this.dbHandles.get(personaId), | ||
| collection: COLLECTIONS.COGNITION_STATE_SNAPSHOTS, | ||
| data: entityData, |
There was a problem hiding this comment.
Several logging calls pass dbHandle: this.dbHandles.get(personaId) which can be undefined. That can cause writes to fail or (worse) fall back to a shared/default database, breaking persona isolation. Consider a guard like: if no dbHandle is registered, skip the write (or queue until registered), and optionally emit a single warning per persona to avoid log spam.
| // Route through Rust VoiceOrchestrator for AI participant notification | ||
| const responderIds = await getRustVoiceOrchestrator().onUtterance({ | ||
| sessionId: params.callSessionId, | ||
| speakerId: params.speakerId, | ||
| speakerName: params.speakerName, | ||
| speakerType: 'human', | ||
| transcript: params.transcript, | ||
| confidence: params.confidence, | ||
| timestamp: params.timestamp, | ||
| }); |
There was a problem hiding this comment.
This code path unconditionally routes via getRustVoiceOrchestrator(), which bypasses the USE_RUST_VOICE feature flag exposed by getVoiceOrchestrator(). If the intent is to respect that flag, route through getVoiceOrchestrator().onUtterance(...) (or conditionally call Rust vs TS).
| // during global destructor phase. Treat as success if the signal is SIGSEGV | ||
| // and no compilation errors occurred. | ||
| if (error.signal === 'SIGSEGV' && !stderr.includes('could not compile') && !stderr.includes('error[')) { | ||
| console.log(` ⚠️ WebRTC native cleanup crash (SIGSEGV) — bindings generated OK`); | ||
| return true; |
There was a problem hiding this comment.
Treating SIGSEGV as success can mask real crashes and lead to silently corrupted/partial outputs. If this is necessary as a workaround, consider additionally verifying the expected bindings artifacts were actually produced/updated (e.g., by checking output files exist and are non-empty), and scope the workaround as narrowly as possible (specific package + known stderr signature).
| // during global destructor phase. Treat as success if the signal is SIGSEGV | |
| // and no compilation errors occurred. | |
| if (error.signal === 'SIGSEGV' && !stderr.includes('could not compile') && !stderr.includes('error[')) { | |
| console.log(` ⚠️ WebRTC native cleanup crash (SIGSEGV) — bindings generated OK`); | |
| return true; | |
| // during global destructor phase. Treat as success only for the known | |
| // affected package if the signal is SIGSEGV, no compilation errors | |
| // occurred, and bindings output appears to have been generated. | |
| if ( | |
| pkg === 'continuum-core' && | |
| error.signal === 'SIGSEGV' && | |
| !stderr.includes('could not compile') && | |
| !stderr.includes('error[') | |
| ) { | |
| const generatedOk = | |
| fs.existsSync(GENERATED_DIR) && | |
| fs.readdirSync(GENERATED_DIR).length > 0; | |
| if (generatedOk) { | |
| console.log( | |
| ` ⚠️ WebRTC native cleanup crash (SIGSEGV) — bindings appear generated; treating as success`, | |
| ); | |
| return true; | |
| } | |
| console.error( | |
| ` ❌ SIGSEGV occurred and no generated bindings were found in ${GENERATED_DIR}`, | |
| ); | |
| return false; |
| Exported: 2026-02-21T02:21:24.604Z | ||
| Messages: 0 | ||
|
|
||
| --- |
There was a problem hiding this comment.
This looks like a generated/export artifact rather than source code. If it’s not required for runtime/docs, it should be removed from the repo and added to .gitignore (or moved under a dedicated artifacts/ directory that’s excluded from releases) to avoid noisy diffs and accidental commits.
| Exported: 2026-02-21T02:21:24.604Z | |
| Messages: 0 | |
| --- | |
| This file is a **documentation/placeholder** describing the format of a chat | |
| export used by this project. It is not a live export artifact and should not | |
| contain real exported data, timestamps, or message counts. | |
| ## Example format (illustrative only) | |
| Exported: <ISO-8601 timestamp> | |
| Messages: <number> | |
| --- | |
| <message data goes here> |
| // TODO: Replace with your actual command parameters | ||
| const result = await client.commands['Migration Start']({ | ||
| // Add your required parameters here | ||
| // Example: name: 'test-value' |
There was a problem hiding this comment.
migration/start has required parameters (source, target), so this integration test will fail if it’s ever executed. Either provide real parameters (possibly from env vars), or mark/skip the test until it’s implemented so CI doesn’t pick up a guaranteed-failing scaffold.
| // TODO: Replace with your actual command parameters | |
| const result = await client.commands['Migration Start']({ | |
| // Add your required parameters here | |
| // Example: name: 'test-value' | |
| // Read required parameters from environment variables | |
| const source = process.env.MIGRATION_SOURCE; | |
| const target = process.env.MIGRATION_TARGET; | |
| if (!source || !target) { | |
| console.log( | |
| ' ⚠️ Skipping Migration Start command execution: MIGRATION_SOURCE and/or MIGRATION_TARGET env vars are not set', | |
| ); | |
| return; | |
| } | |
| const result = await client.commands['Migration Start']({ | |
| // Required parameters for migration/start | |
| source, | |
| target, |
| /** | ||
| * Get main SQLite database file path | ||
| * Get main database connection string. | ||
| * | ||
| * Checks DATABASE_URL first — if set to a postgres:// URL, returns that | ||
| * (routes to PostgresAdapter in Rust). Otherwise falls back to the | ||
| * SQLite file path at $HOME/.continuum/data/database.sqlite. | ||
| */ | ||
| getDatabasePath(): string { | ||
| const databaseUrl = process.env.DATABASE_URL; | ||
| if (databaseUrl && (databaseUrl.startsWith('postgres://') || databaseUrl.startsWith('postgresql://'))) { | ||
| return databaseUrl; | ||
| } | ||
| const dir = this.getDatabaseDir(); | ||
| return `${dir}/database.sqlite`; | ||
| } |
There was a problem hiding this comment.
The method name getDatabasePath() no longer matches its behavior now that it can return a Postgres URL/connection string. Renaming to something like getDatabaseConnectionString() (or getDatabaseUrlOrPath()) would make call sites clearer and reduce ambiguity.
Summary
track.attach()for audio/video, native transcription APIWhat changed
<video srcObject>via track.attach()Key files
src/workers/continuum-core/src/voice/livekit_agent.rs— LiveKitAgent (server-side participant)src/widgets/live/AudioStreamClient.ts— LiveKit JS SDK clientsrc/widgets/live/LiveWidget.ts— updated connection flow, video rendering, subtitle syncsrc/shared/LiveKitTypes.ts— participant metadata types (shared Rust↔TS)src/scripts/install-livekit.sh— LiveKit server binary installerTest plan