osborn 0.9.193 → 0.9.195

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/config.d.ts CHANGED
@@ -2,8 +2,8 @@ import type { McpServerConfig } from './claude-handler.js';
2
2
  export type VoiceMode = 'pipeline';
3
3
  export type EditMode = 'read-only' | 'edit';
4
4
  export type AgentMode = 'plan' | 'execute' | 'research';
5
- export type STTProvider = 'soniox' | 'deepgram' | 'groq-whisper' | 'openai-whisper';
6
- export type TTSProvider = 'soniox' | 'openai' | 'deepgram';
5
+ export type STTProvider = 'soniox' | 'deepgram' | 'deepgram-flux' | 'groq-whisper' | 'openai-whisper';
6
+ export type TTSProvider = 'soniox' | 'openai' | 'deepgram' | 'groq-orpheus' | 'fishaudio' | 'rime';
7
7
  export interface DirectConfig {
8
8
  stt?: {
9
9
  provider?: STTProvider;
@@ -16,11 +16,16 @@ export interface DirectConfig {
16
16
  voice?: string;
17
17
  };
18
18
  }
19
+ export interface InferenceConfig {
20
+ fastBrainModel?: string;
21
+ fastBrainProvider?: 'openrouter';
22
+ }
19
23
  export interface OsbornConfig {
20
24
  workingDirectory?: string;
21
25
  mcpServers?: Record<string, McpServerConfigYaml>;
22
26
  voiceMode?: VoiceMode;
23
27
  direct?: DirectConfig;
28
+ inference?: InferenceConfig;
24
29
  }
25
30
  interface McpServerConfigYaml {
26
31
  enabled?: boolean;
@@ -76,7 +81,8 @@ export declare function getVoiceMode(_config: OsbornConfig): VoiceMode;
76
81
  /**
77
82
  * Get pipeline mode config with defaults merged
78
83
  */
79
- export declare function getDirectConfig(config: OsbornConfig): Required<DirectConfig>;
84
+ export declare function getDirectConfig(_config: OsbornConfig): Required<DirectConfig>;
85
+ export declare function getInferenceConfig(_config: OsbornConfig): Required<InferenceConfig>;
80
86
  /**
81
87
  * Save config to file
82
88
  */
package/dist/config.js CHANGED
@@ -43,35 +43,74 @@ export const MCP_CATALOG = [
43
43
  // (`/usr/local/nvm/.../osborn`) and gets persisted to ~/.osborn/config.yaml forever.
44
44
  // Leaving it undefined lets the runtime self-heal in index.ts resolve it on every boot
45
45
  // from OSBORN_CWD → process.cwd() at the actual time the agent starts.
46
+ // ============================================================
47
+ // PROVIDER DEFAULTS — Single source of truth for STT, TTS, and inference.
48
+ // To switch provider: change `provider` (and model/voice) here.
49
+ // All alternatives are listed as comments. Env vars override without redeploy:
50
+ // STT: OSBORN_STT_PROVIDER, OSBORN_STT_MODEL, OSBORN_STT_LANGUAGE
51
+ // TTS: OSBORN_TTS_PROVIDER, OSBORN_TTS_MODEL, OSBORN_TTS_VOICE
52
+ // Inference: OSBORN_FAST_BRAIN_MODEL
53
+ // These are admin-owned defaults — NOT written to config.yaml on disk.
54
+ // ============================================================
46
55
  const DEFAULT_CONFIG = {
47
56
  voiceMode: 'pipeline',
48
57
  direct: {
49
58
  stt: {
50
- // Soniox stt-rt-v4 — semantic endpointing (ML, not silence), word timestamps,
51
- // custom vocabulary via context.terms. ~60% cheaper than nova-3 ($0.0017 vs $0.0043/min).
52
- // Needs SONIOX_API_KEY.
59
+ // ── Active ─────────────────────────────────────────────────────────────
53
60
  provider: 'soniox',
54
61
  model: 'stt-rt-v4',
55
- // Previous: Deepgram nova-3, silence-based endpointing (550ms configured in voice-io.ts).
56
- // Switch back: provider: 'deepgram', model: 'nova-3'
62
+ // Semantic endpointing — ML model holds on incomplete thoughts, commits on
63
+ // natural sentence ends. ~60% cheaper than nova-3 ($0.0017 vs $0.0043/min).
64
+ // Needs SONIOX_API_KEY. See voice-io.ts createSTT for tuning params.
65
+ //
66
+ // ── Alternatives ───────────────────────────────────────────────────────
67
+ // provider: 'deepgram', model: 'nova-3'
68
+ // Silence-based endpointing (550ms). Fast, reliable. $0.0043/min.
69
+ // provider: 'deepgram-flux', model: 'flux-general-en'
70
+ // ML turn detection (server-side). Flux V2 has 30s silent keepalive bug.
71
+ // provider: 'groq-whisper', model: 'whisper-large-v3-turbo'
72
+ // Batch STT via Groq (very fast). Requires VAD for turn detection.
73
+ // provider: 'openai-whisper', model: 'whisper-1'
74
+ // Batch STT via OpenAI. Requires VAD. $0.006/min.
57
75
  },
58
76
  tts: {
59
- // Soniox tts-rt-v1 — real-time WebSocket streaming, speed control (0.7–1.3x),
60
- // clean abort on interruption. Estimated ~$4–16/M chars vs OpenAI tts-1-hd $30/M.
61
- // Pricing: $0.70/hr of generated speech (preview). Needs SONIOX_API_KEY.
77
+ // ── Active ─────────────────────────────────────────────────────────────
62
78
  provider: 'soniox',
63
79
  model: 'tts-rt-v1',
64
80
  voice: 'Victoria',
65
- // Previous: OpenAI tts-1-hd, voice fable — high quality, $30/M chars, ~500ms TTFB.
66
- // Switch back: provider: 'openai', model: 'tts-1-hd', voice: 'fable'
67
- // Other options already wired in voice-io.ts:
68
- // Rime Mist v3: provider: 'rime', voice: 'cove' — 37ms TTFB, $30/M, WebSocket
69
- // Fish Audio s2-pro: provider: 'fishaudio', voice: '<id>' — $15/M, voice cloning
70
- // Groq Orpheus: provider: 'groq-orpheus', voice: 'autumn' — fast Groq chips, $22/M
71
- // Deepgram Aura-2: provider: 'deepgram', model: 'aura-2-asteria-en' — $15/M, ~100ms TTFB
72
- // OpenAI tts-1: provider: 'openai', model: 'tts-1', voice: 'fable' — $15/M
81
+ // WebSocket streaming — clean abort on interruption. Speed control 0.7–1.3x.
82
+ // Est. ~$4–16/M chars vs OpenAI $30/M. Victoria = en-GB female, refined.
83
+ // Also: Isla (en-GB, lively), Maya (en-US female). Needs SONIOX_API_KEY.
84
+ //
85
+ // ── Alternatives ───────────────────────────────────────────────────────
86
+ // provider: 'openai', model: 'tts-1-hd', voice: 'fable'
87
+ // $30/M chars, ~500ms TTFB, HTTP streaming. 6 voices: alloy echo fable onyx nova shimmer.
88
+ // provider: 'openai', model: 'tts-1', voice: 'fable'
89
+ // $15/M chars, slightly lower quality, same voices.
90
+ // provider: 'deepgram', model: 'aura-2-asteria-en'
91
+ // $15/M chars, ~100ms TTFB, WebSocket. Voices: asteria luna stella hera orion arcas perseus angus orpheus.
92
+ // provider: 'rime', model: 'mistv3', voice: 'cove'
93
+ // $30/M chars, 37ms TTFB, WebSocket (clean abort). Voices: aurora ember cove. Needs RIME_API_KEY.
94
+ // provider: 'fishaudio', model: 's2-pro', voice: '<voice-id>'
95
+ // $15/M chars, voice cloning, HTTP streaming. Needs FISH_AUDIO_API_KEY.
96
+ // provider: 'groq-orpheus', model: 'canopylabs/orpheus-v1-english', voice: 'autumn'
97
+ // $22/M chars, fast (Groq chips). Voices: autumn diana hannah austin daniel troy.
73
98
  },
74
99
  },
100
+ inference: {
101
+ // ── Active ───────────────────────────────────────────────────────────────
102
+ fastBrainModel: 'deepseek/deepseek-chat',
103
+ fastBrainProvider: 'openrouter',
104
+ // Parallel fast brain: answers greetings/follow-ups instantly while main agent
105
+ // processes. Uses OpenRouter (OPENROUTER_API_KEY). ~20ms TTFB on deepseek-chat.
106
+ //
107
+ // ── Alternatives ─────────────────────────────────────────────────────────
108
+ // fastBrainModel: 'deepseek/deepseek-reasoner' — slower, deeper CoT
109
+ // fastBrainModel: 'google/gemini-flash-1.5' — Google via OpenRouter
110
+ // fastBrainModel: 'openai/gpt-4o-mini' — OpenAI via OpenRouter
111
+ // fastBrainModel: 'meta-llama/llama-3.1-8b-instruct:free' — free tier via OpenRouter
112
+ // fastBrainModel: 'anthropic/claude-haiku-4-5-20251001' — Haiku via OpenRouter
113
+ },
75
114
  mcpServers: {
76
115
  // ─────────────────────────────────────────────────────────────────────────
77
116
  // MCP Servers for Read-Only Plan Mode
@@ -139,10 +178,12 @@ export function loadConfig() {
139
178
  mkdirSync(CONFIG_DIR, { recursive: true });
140
179
  console.log(`📁 Created config directory: ${CONFIG_DIR}`);
141
180
  }
142
- // Create default config if it doesn't exist
181
+ // Create default config if it doesn't exist.
182
+ // Exclude `direct` (STT/TTS) and `inference` — admin-owned, not user config on disk.
183
+ // Writing them to disk causes the file to permanently override npm package updates.
143
184
  if (!existsSync(CONFIG_FILE)) {
144
- const defaultYaml = stringify(DEFAULT_CONFIG);
145
- writeFileSync(CONFIG_FILE, defaultYaml, 'utf-8');
185
+ const { direct: _d, inference: _i, ...persistable } = DEFAULT_CONFIG;
186
+ writeFileSync(CONFIG_FILE, stringify(persistable), 'utf-8');
146
187
  console.log(`📝 Created default config: ${CONFIG_FILE}`);
147
188
  return DEFAULT_CONFIG;
148
189
  }
@@ -207,22 +248,33 @@ export function getVoiceMode(_config) {
207
248
  /**
208
249
  * Get pipeline mode config with defaults merged
209
250
  */
210
- export function getDirectConfig(config) {
251
+ export function getDirectConfig(_config) {
211
252
  const defaults = DEFAULT_CONFIG.direct;
212
- const userConfig = config.direct || {};
253
+ // STT/TTS settings are admin-owned defaults — they live in DEFAULT_CONFIG only.
254
+ // config.yaml is intentionally NOT consulted here: writing provider settings
255
+ // to the volume creates a stale file that permanently overrides npm updates.
256
+ // To override without a redeploy, set OSBORN_STT_PROVIDER / OSBORN_TTS_PROVIDER
257
+ // / OSBORN_TTS_VOICE / OSBORN_STT_MODEL / OSBORN_TTS_MODEL on the machine.
213
258
  return {
214
259
  stt: {
215
- provider: userConfig.stt?.provider || defaults.stt.provider,
216
- model: userConfig.stt?.model || defaults.stt.model,
217
- language: userConfig.stt?.language || 'en',
260
+ provider: (process.env.OSBORN_STT_PROVIDER || defaults.stt.provider),
261
+ model: process.env.OSBORN_STT_MODEL || defaults.stt.model,
262
+ language: process.env.OSBORN_STT_LANGUAGE || 'en',
218
263
  },
219
264
  tts: {
220
- provider: userConfig.tts?.provider || defaults.tts.provider,
221
- model: userConfig.tts?.model || defaults.tts.model,
222
- voice: userConfig.tts?.voice || defaults.tts.voice,
265
+ provider: (process.env.OSBORN_TTS_PROVIDER || defaults.tts.provider),
266
+ model: process.env.OSBORN_TTS_MODEL || defaults.tts.model,
267
+ voice: process.env.OSBORN_TTS_VOICE || defaults.tts.voice,
223
268
  },
224
269
  };
225
270
  }
271
+ export function getInferenceConfig(_config) {
272
+ const defaults = DEFAULT_CONFIG.inference;
273
+ return {
274
+ fastBrainModel: process.env.OSBORN_FAST_BRAIN_MODEL || defaults.fastBrainModel,
275
+ fastBrainProvider: 'openrouter',
276
+ };
277
+ }
226
278
  /**
227
279
  * Save config to file
228
280
  */
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ const __filename = fileURLToPath(import.meta.url);
30
30
  const __dirname = dirname(__filename);
31
31
  import { createPatch } from 'diff';
32
32
  import { loadConfig, getMcpServers, getEnabledMcpServerNames, getVoiceMode, getDirectConfig, listSessions, listAllClaudeSessions, invalidateSessionListCache, getMostRecentSessionId, sessionExists, getSessionSummary, getConversationHistory, ensureSessionWorkspace, getMcpServerStatusList, buildMcpServersForKeys, listWorkspaceArtifacts } from './config.js';
33
- import { createSTT, createTTS, DIRECT_MODE_STT, DIRECT_MODE_TTS } from './voice-io.js';
33
+ import { createSTT, createTTS } from './voice-io.js';
34
34
  import { createClaudeLLM, NAMED_AGENTS, applyTurbo } from './claude-llm.js';
35
35
  import { clearPipelineFastBrainSession, prewarmBM25Index } from './pipeline-fastbrain.js';
36
36
  import { getIndexPath, buildSummaryIndex } from './summary-index.js';
@@ -417,13 +417,11 @@ async function synthMp3(text) {
417
417
  console.warn('⚠️ synthMp3: no OPENAI_API_KEY — meeting has no voice');
418
418
  return null;
419
419
  }
420
- // Meeting voice = the SAME OpenAI model/voice as the website's regular TTS
421
- // (DIRECT_MODE_TTS), so the bot sounds IDENTICAL on both fronts (user directive
422
- // 2026-08-04: Deepgram aura sounded "cheap and inconsistent"). Deepgram removed
423
- // from the meeting path entirely. Pulls model/voice from DIRECT_MODE_TTS when
424
- // it's an OpenAI config so the two never drift.
425
- const model = DIRECT_MODE_TTS.provider === 'openai' ? (DIRECT_MODE_TTS.model || 'tts-1-hd') : 'tts-1-hd';
426
- const voice = DIRECT_MODE_TTS.provider === 'openai' ? (DIRECT_MODE_TTS.voice || 'fable') : 'fable';
420
+ // Meeting audio is always OpenAI TTS (HTTP MP3 required for Recall output_audio).
421
+ // Reads OSBORN_TTS_* env vars — same source as getDirectConfig — so voice is
422
+ // consistent between the pipeline and meeting canvas when TTS provider is openai.
423
+ const model = process.env.OSBORN_TTS_PROVIDER === 'openai' ? (process.env.OSBORN_TTS_MODEL || 'tts-1-hd') : 'tts-1-hd';
424
+ const voice = process.env.OSBORN_TTS_PROVIDER === 'openai' ? (process.env.OSBORN_TTS_VOICE || 'fable') : 'fable';
427
425
  try {
428
426
  const r = await fetch('https://api.openai.com/v1/audio/speech', {
429
427
  method: 'POST',
@@ -859,18 +857,16 @@ function startApiServer(workingDir, port) {
859
857
  return;
860
858
  }
861
859
  const t0 = Date.now();
862
- // Meeting voice = the SAME OpenAI model/voice as the website's regular TTS
863
- // (DIRECT_MODE_TTS) — user directive 2026-08-04: Deepgram aura removed, it
864
- // sounded cheap/inconsistent. Consistency over the ~2-4s latency Deepgram
865
- // saved. mp3 out (the canvas <audio> element plays it into the meeting).
860
+ // Meeting canvas TTS — always OpenAI HTTP (mp3 for <audio> element).
861
+ // Reads OSBORN_TTS_* env vars so voice stays in sync with pipeline when provider is openai.
866
862
  const key = process.env.OPENAI_API_KEY;
867
863
  if (!key) {
868
864
  res.writeHead(400, { 'Content-Type': 'application/json' });
869
865
  res.end(JSON.stringify({ error: 'no OPENAI_API_KEY' }));
870
866
  return;
871
867
  }
872
- const model = DIRECT_MODE_TTS.provider === 'openai' ? (DIRECT_MODE_TTS.model || 'tts-1-hd') : 'tts-1-hd';
873
- const voice = url.searchParams.get('voice') || (DIRECT_MODE_TTS.provider === 'openai' ? (DIRECT_MODE_TTS.voice || 'fable') : 'fable');
868
+ const model = process.env.OSBORN_TTS_PROVIDER === 'openai' ? (process.env.OSBORN_TTS_MODEL || 'tts-1-hd') : 'tts-1-hd';
869
+ const voice = url.searchParams.get('voice') || (process.env.OSBORN_TTS_PROVIDER === 'openai' ? (process.env.OSBORN_TTS_VOICE || 'fable') : 'fable');
874
870
  try {
875
871
  const tts = await fetch('https://api.openai.com/v1/audio/speech', {
876
872
  method: 'POST',
@@ -3173,8 +3169,8 @@ async function main() {
3173
3169
  // Create DIRECT session (STT + Claude Agent SDK + TTS)
3174
3170
  async function createDirectSession(resumeSessionId, llmOverride) {
3175
3171
  console.log('🎯 Creating direct session...');
3176
- const stt = createSTT(DIRECT_MODE_STT);
3177
- const tts = createTTS(DIRECT_MODE_TTS);
3172
+ const stt = createSTT(directConfig.stt);
3173
+ const tts = createTTS(directConfig.tts);
3178
3174
  // Create Claude LLM wrapper — direct mode uses speech-optimized system prompt
3179
3175
  // skipTTSQueue: bypass LiveKit's BufferedTokenStream, use session.say() instead
3180
3176
  // llmOverride: pipeline mode passes PipelineDirectLLM which wraps its own ClaudeLLM
@@ -15,7 +15,9 @@
15
15
  // ============================================================
16
16
  // CONSTANTS
17
17
  // ============================================================
18
- const OPENROUTER_MODEL = 'deepseek/deepseek-chat';
18
+ // Model reads from env var (set by main process from getInferenceConfig) or DEFAULT_CONFIG default.
19
+ // To switch model: update DEFAULT_CONFIG.inference.fastBrainModel in config.ts, or set OSBORN_FAST_BRAIN_MODEL env var.
20
+ const OPENROUTER_MODEL = process.env.OSBORN_FAST_BRAIN_MODEL || 'deepseek/deepseek-chat';
19
21
  const OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';
20
22
  const TIMEOUT_MS = 20_000;
21
23
  const MAX_TOOL_ROUNDS = 4;
@@ -42,9 +42,3 @@ export declare function createTTS(config: TTSConfig): any;
42
42
  * - False triggers from ambient noise
43
43
  */
44
44
  export declare function createVAD(): Promise<silero.VAD>;
45
- /**
46
- * Pipeline mode voice config — centralized here for easy provider swapping.
47
- * To switch providers: comment out the active line, uncomment the alternative.
48
- */
49
- export declare const DIRECT_MODE_STT: STTConfig;
50
- export declare const DIRECT_MODE_TTS: TTSConfig;
package/dist/voice-io.js CHANGED
@@ -154,21 +154,3 @@ export async function createVAD() {
154
154
  activationThreshold: 0.85, // default — balanced for interruptions only
155
155
  });
156
156
  }
157
- /**
158
- * Pipeline mode voice config — centralized here for easy provider swapping.
159
- * To switch providers: comment out the active line, uncomment the alternative.
160
- */
161
- export const DIRECT_MODE_STT = {
162
- // provider: 'groq-whisper', model: 'whisper-large-v3-turbo', // Batch — needs VAD
163
- // provider: 'openai-whisper', model: 'whisper-1', // Batch — needs VAD
164
- // provider: 'deepgram-flux', model: 'flux-general-en', language: 'en', // Streaming, ML-based turn detection — Flux V2 has silent keepalive timeout bug (~30s silence kills connection)
165
- provider: 'deepgram', model: 'nova-3', language: 'en', // Streaming, silence-based endpointing (550ms)
166
- };
167
- export const DIRECT_MODE_TTS = {
168
- // provider: 'deepgram', model: 'aura-2-asteria-en', // WebSocket-based: handles TTS abort cleanly — but quality rejected (run-on sentences)
169
- // provider: 'openai', model: 'tts-1', voice: 'fable', // HTTP streaming: throws APIUserAbortError on interrupt → unrecoverable session crash
170
- provider: 'openai', model: 'tts-1-hd', voice: 'fable', // 0.9.70: test tts-1-hd — tts-1 had chronic per-sentence HTTP hangs (40s SDK watchdog → APIUserAbortError mid-message)
171
- // provider: 'groq-orpheus', model: 'canopylabs/orpheus-v1-english', voice: 'autumn', // $22/M chars — voices: autumn, diana, hannah, austin, daniel, troy
172
- // provider: 'fishaudio', model: 's2-pro', voice: '<voice-id>', // $15/M chars — blind test winner, HTTP streaming (test abort behavior)
173
- // provider: 'rime', model: 'mistv3', voice: 'cove', // $30/M chars, 37ms TTFB — WebSocket (safe on interruption); voices: aurora, ember, cove
174
- };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.193",
3
+ "version": "0.9.195",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {