* feat(voice): configure OpenAI STT and TTS endpoints separately
Replace providers.openai.voice (a single apiKey/baseUrl shared by
speech-to-text and text-to-speech) with independent providers.openai.stt
and providers.openai.tts, each carrying its own apiKey/baseUrl. STT and
TTS now resolve fully independently, so they can point at different
OpenAI-compatible endpoints. The env equivalents OPENAI_VOICE_API_KEY /
OPENAI_VOICE_BASE_URL split into OPENAI_STT_* and OPENAI_TTS_*.
No backcompat: the voice key is removed and no longer read. Each feature
still falls back to providers.openai.apiKey/baseUrl, then OPENAI_API_KEY/
OPENAI_BASE_URL. Composer dictation resolves from the STT endpoint.
* fix(voice): keep daemon bootable and respect global OpenAI key
Two issues from review of the STT/TTS endpoint split:
- A config from an older release that still sets providers.openai.voice
crashed daemon startup, because the strict schema rejects the now-unknown
key. Strip it before parsing (alongside the existing local.autoDownload
strip) so the daemon boots; the value is discarded, not migrated.
- An empty endpoint env var (e.g. a copied .env.example leaving
OPENAI_STT_API_KEY= blank) shadowed the OPENAI_API_KEY fallback, so
speech was reported as missing credentials despite a configured global
key. firstDefined now skips empty/whitespace strings.
* fix(voice): isolate STT and TTS option parsing per endpoint
An STT-only OpenAI setup could be broken by a stale or invalid TTS env
var (e.g. a leftover TTS_VOICE/TTS_MODEL), because the single resolution
schema validated both endpoints' option groups before the per-endpoint
gate. Split into endpoint-key, STT-option, and TTS-option schemas and
parse each option group only when that endpoint has credentials, so an
unused endpoint's bad env can no longer take down the configured one.
* fix(voice): tag voice-config shim and update direct-daemon test callers
- Mark the providers.openai.voice strip with a COMPAT(openaiVoiceConfig)
comment + removal date so it shows up in the back-compat cleanup
inventory, per repo convention.
- Update the tests that build the daemon directly with a resolved OpenAI
config (bootstrap smoke + the real-API voice/daemon e2e suites) to the
new { stt, tts } shape; the old top-level { apiKey } is no longer read,
and these files are excluded from typecheck so the break was silent.
* fix(voice): update voice-roundtrip debug script to new OpenAI config shape
Last direct daemon caller still passing the removed top-level
openai: { apiKey }; the debug script lives outside tsconfig.scripts.json
so the stale shape wasn't caught by typecheck. Use { stt, tts }.
514 lines
16 KiB
JSON
514 lines
16 KiB
JSON
{
|
|
"$ref": "#/definitions/PaseoConfigV1",
|
|
"definitions": {
|
|
"PaseoConfigV1": {
|
|
"type": "object",
|
|
"properties": {
|
|
"$schema": {
|
|
"type": "string"
|
|
},
|
|
"version": {
|
|
"type": "number",
|
|
"const": 1
|
|
},
|
|
"daemon": {
|
|
"type": "object",
|
|
"properties": {
|
|
"listen": {
|
|
"type": "string"
|
|
},
|
|
"hostnames": {
|
|
"anyOf": [
|
|
{
|
|
"type": "boolean",
|
|
"const": true
|
|
},
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"allowedHosts": {
|
|
"anyOf": [
|
|
{
|
|
"type": "boolean",
|
|
"const": true
|
|
},
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"trustedProxies": {
|
|
"description": "Express trusted proxy setting for X-Forwarded-* headers. Defaults to [\"loopback\"]. Use true only when the final trusted proxy overwrites client-supplied forwarded headers.",
|
|
"anyOf": [
|
|
{
|
|
"type": "boolean",
|
|
"const": true
|
|
},
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"mcp": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"injectIntoAgents": {
|
|
"type": "boolean"
|
|
}
|
|
},
|
|
"additionalProperties": true
|
|
},
|
|
"autoArchiveAfterMerge": {
|
|
"type": "boolean"
|
|
},
|
|
"enableTerminalAgentHooks": {
|
|
"type": "boolean"
|
|
},
|
|
"appendSystemPrompt": {
|
|
"type": "string"
|
|
},
|
|
"cors": {
|
|
"type": "object",
|
|
"properties": {
|
|
"allowedOrigins": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"relay": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"endpoint": {
|
|
"type": "string"
|
|
},
|
|
"publicEndpoint": {
|
|
"type": "string"
|
|
},
|
|
"useTls": {
|
|
"type": "boolean"
|
|
},
|
|
"publicUseTls": {
|
|
"type": "boolean"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"serviceProxy": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"description": "Compatibility shim. false suppresses optional service proxy listen/public layers only; localhost proxying remains enabled.",
|
|
"type": "boolean"
|
|
},
|
|
"listen": {
|
|
"description": "Optional service-only listener address. Presence enables the standalone service proxy listener.",
|
|
"type": "string"
|
|
},
|
|
"publicBaseUrl": {
|
|
"description": "Optional public base URL for service host aliases.",
|
|
"type": "string",
|
|
"format": "uri"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"auth": {
|
|
"type": "object",
|
|
"properties": {
|
|
"password": {
|
|
"type": "string",
|
|
"pattern": "^\\$2[aby]\\$\\d{2}\\$[./A-Za-z0-9]{53}$"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"app": {
|
|
"type": "object",
|
|
"properties": {
|
|
"baseUrl": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"worktrees": {
|
|
"type": "object",
|
|
"properties": {
|
|
"root": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"providers": {
|
|
"type": "object",
|
|
"properties": {
|
|
"openai": {
|
|
"type": "object",
|
|
"properties": {
|
|
"apiKey": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"baseUrl": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"stt": {
|
|
"type": "object",
|
|
"properties": {
|
|
"apiKey": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"baseUrl": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"tts": {
|
|
"type": "object",
|
|
"properties": {
|
|
"apiKey": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"baseUrl": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"local": {
|
|
"type": "object",
|
|
"properties": {
|
|
"modelsDir": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"agents": {
|
|
"type": "object",
|
|
"properties": {
|
|
"providers": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"extends": {
|
|
"type": "string"
|
|
},
|
|
"label": {
|
|
"type": "string"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"command": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"minItems": 1
|
|
},
|
|
"env": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"models": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"label": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"isDefault": {
|
|
"type": "boolean"
|
|
},
|
|
"thinkingOptions": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"label": {
|
|
"type": "string"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"isDefault": {
|
|
"type": "boolean"
|
|
}
|
|
},
|
|
"required": ["id", "label"],
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
},
|
|
"required": ["id", "label"],
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalModels": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/agents/properties/providers/additionalProperties/properties/models/items"
|
|
}
|
|
},
|
|
"disallowedTools": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"order": {
|
|
"type": "number"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"features": {
|
|
"type": "object",
|
|
"properties": {
|
|
"dictation": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"stt": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"allOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"enum": ["openai", "local"]
|
|
}
|
|
]
|
|
},
|
|
"model": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"language": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"confidenceThreshold": {
|
|
"type": "number"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"voiceMode": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"llm": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"type": "string"
|
|
},
|
|
"model": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"stt": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/features/properties/dictation/properties/stt/properties/provider"
|
|
},
|
|
"model": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"language": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"turnDetection": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/features/properties/dictation/properties/stt/properties/provider"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"tts": {
|
|
"type": "object",
|
|
"properties": {
|
|
"provider": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/features/properties/dictation/properties/stt/properties/provider"
|
|
},
|
|
"model": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"voice": {
|
|
"type": "string",
|
|
"enum": ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
|
|
},
|
|
"speakerId": {
|
|
"type": "integer"
|
|
},
|
|
"speed": {
|
|
"type": "number"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"webUi": {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean"
|
|
},
|
|
"distDir": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"log": {
|
|
"type": "object",
|
|
"properties": {
|
|
"level": {
|
|
"type": "string",
|
|
"enum": ["trace", "debug", "info", "warn", "error", "fatal"]
|
|
},
|
|
"format": {
|
|
"type": "string",
|
|
"enum": ["pretty", "json"]
|
|
},
|
|
"console": {
|
|
"type": "object",
|
|
"properties": {
|
|
"level": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/log/properties/level"
|
|
},
|
|
"format": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/log/properties/format"
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
},
|
|
"file": {
|
|
"type": "object",
|
|
"properties": {
|
|
"level": {
|
|
"$ref": "#/definitions/PaseoConfigV1/properties/log/properties/level"
|
|
},
|
|
"path": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"rotate": {
|
|
"type": "object",
|
|
"properties": {
|
|
"maxSize": {
|
|
"type": "string",
|
|
"minLength": 1
|
|
},
|
|
"maxFiles": {
|
|
"type": "integer",
|
|
"exclusiveMinimum": 0
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"additionalProperties": false
|
|
}
|
|
},
|
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
}
|