* 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 }.
Resolve OpenAI voice credentials and endpoints from voice-specific config before broader OpenAI fallbacks, and use the same REST STT provider for dictation and voice mode.
* Rework docs navigation and move alternatives to top-level pages
- Add nested docs nav with categories, collapsible groups, breadcrumbs,
and a right-hand page outline with heading slugs.
- Move alternatives out of /docs/alternatives into /alternatives/*
marketing pages with 301 redirects.
- Add github-slugger for heading IDs and require explicit width on
SiteShell so new pages can't accidentally pick the narrow prose layout.
- Remove the best-practices doc from public-docs.
* Make docs content column prose-width
Adds a new conceptual Workspaces doc that explains how Paseo organizes work around projects, workspaces, and sessions. Reframed the existing Git worktrees page as the technical deep-dive it always was. Updated glossary with agent session and tab terminology. Reordered docs nav so workspaces and worktrees come before providers.
v2 is English-only; v3 covers 25 European languages with automatic
language detection. The archive shares v2's filenames, architecture,
sample rate, and feature dim, so no runtime loader changes are needed.
Document that the `language` config field and PASEO_*_LANGUAGE vars
only steer the OpenAI STT provider — the local Parakeet models ignore
them, so multilingual local dictation means selecting the v3 model.
The documented parakeet-tdt-0.6b-v3-int8 doesn't exist in the model
catalog; only the v2 variant is accepted. Copying the doc value into
config produced an "invalid model id" error on daemon restart.
* docs: rename to lowercase + drop leftover plans
Rename docs in docs/ to lowercase kebab-case for consistency and update
all references in CLAUDE.md, CONTRIBUTING.md, CHANGELOG.md,
packages/server/CLAUDE.md, and inter-doc links.
Drop two leftover design plan docs:
- docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md
- docs/plan-approval-normalization.md
* docs: drop stale uppercase entries from case-insensitive rename
* feat(website): power /docs from public-docs/ markdown tree
Move website docs out of TSX route components and into a root-level
public-docs/ directory of plain markdown files with frontmatter
(title, description, nav, order).
- Add packages/website/src/docs.ts loader using import.meta.glob with
?raw to compile the markdown into the bundle at build time.
- Replace the 9 hand-written docs/*.tsx routes with a single $.tsx
catch-all that renders any slug via react-markdown.
- Drive the docs sidebar nav from frontmatter order/nav.
- Auto-discover docs routes in vite.config.ts so the sitemap stays in
sync without manual edits.
* fix(website): bind dev server to 0.0.0.0 so port collisions trigger fallback
`host: "127.0.0.1"` (or unset) lets macOS coexist with another process
holding an IPv6 dual-stack `*:8082` socket, so Vite never sees
EADDRINUSE and silently binds alongside it. Forcing IPv4 wildcard
makes the conflict real, and Vite's default `strictPort: false`
falls through to the next free port.
* fix(website): restore docs page styling after markdown migration
Add a .docs-prose class that mirrors the styling the original
docs/*.tsx components hand-rolled (h1/h2/h3 sizes, paragraph/list
spacing, link colors, code blocks, callout-style blockquotes).
ReactMarkdown was emitting unstyled HTML because the previous
wrapper class only had inline-code rules — headings and code
blocks fell back to user-agent defaults.