diff --git a/.gitignore b/.gitignore
index f2b3fda..4ea628b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,8 +38,8 @@ node_modules/
.npm/
package-lock.json
!speech_to_speech/package-lock.json
-!s2s-frontend/package-lock.json
-s2s-frontend/.eslintcache
+!vocero-frontend/package-lock.json
+vocero-frontend/.eslintcache
yarn-error.log
npm-debug.log*
yarn-debug.log*
@@ -125,10 +125,10 @@ CVS/
# ─────────────────────────────────────────────────────────────
# Client backend and speech-service runtime artifacts
# ─────────────────────────────────────────────────────────────
-s2s-client/.cache/
-s2s-client/uploads/
-s2s-client/sessions/
-s2s-client/*.log
+vocero-api/.cache/
+vocero-api/uploads/
+vocero-api/sessions/
+vocero-api/*.log
speech_to_speech/*.log
speech_to_speech/sessions/
speech_to_speech/.cache/
diff --git a/AGENTS.md b/AGENTS.md
index 405a325..7fce268 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3,12 +3,13 @@
## Scope and working conventions
This repository is Vocero: a browser voice client, its FastAPI integration
-server, and a speech pipeline. Use the code, commands,
-configuration, and documentation within this checkout. Do not assume other
-repositories or developer-specific directories are available.
+server, and a speech pipeline. Keep this guide self-contained and base its
+instructions on this checkout's code, configuration, documentation, and tools.
-All file and directory references in this guide are relative to the repository
-root. HTTP routes mentioned below are URLs, not filesystem paths.
+All file and directory references in this guide must point within the repository
+and be relative to its root. Commands may use the project's command-line tools,
+such as `rg`, `git`, `uv`, `npm`, Docker, and PM2. HTTP routes mentioned below are
+URLs, not filesystem paths.
- Check the current branch, worktree, and working-tree changes before editing.
Honor the checkout requested by the user, including direct work on `main`.
@@ -23,35 +24,34 @@ root. HTTP routes mentioned below are URLs, not filesystem paths.
assuming a tool is missing. Do not install system-wide packages unnecessarily.
- When commits are requested, make coherent, scoped Conventional Commits as work
progresses. Avoid excessive micro-commits and unrelated cleanup.
-- Invoke `work-on-feature` only when explicitly requested. For a requested PR,
- follow the available `pr-description` skill exactly and put verification
- details in the handoff when its format excludes them. Issues are optional;
- create native Gitea issues or post comments only when requested.
+- For a requested pull request, describe the problem, resulting behavior,
+ validation, and relevant limitations. Create issues or post comments only
+ when requested.
- When asked to merge locally, use the requested rebase/fast-forward flow;
preserve a recovery ref before rewriting and verify retained patches. A local
merge does not imply a push, deployment, or restart. Report those separately.
-- Treat old conversations and proposals as context. Verify claims against the
- current code and distinguish proposed authentication/deployment changes from
- implemented behavior.
+- Treat historical proposals in `docs/proposals/` as context. Verify their
+ claims against current code and distinguish proposed authentication or
+ deployment changes from implemented behavior.
## Project map
| Location | Responsibility |
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
-| `s2s-client/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package |
-| `s2s-client/s2s_client/main.py` | Application assembly, documentation mount, and retained redirects |
-| `s2s-client/s2s_client/api/` | HTTP routes, route registration, and response validation helper |
-| `s2s-client/s2s_client/models/` | Pydantic request and response contracts |
-| `s2s-client/s2s_client/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
-| `s2s-client/s2s_client/providers/` | ApiPilot and caller/profile/report HTTP integrations |
-| `s2s-client/s2s_client/services/` | Prompt/context construction, reasoning, and tool-context rendering |
-| `s2s-client/s2s_client/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
-| `s2s-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
+| `vocero-api/` | The complete uv project: manifests, environment, Python tooling, tests, and FastAPI package |
+| `vocero-api/vocero_api/main.py` | Application assembly, documentation mount, and retained redirects |
+| `vocero-api/vocero_api/api/` | HTTP routes, route registration, and response validation helper |
+| `vocero-api/vocero_api/models/` | Pydantic request and response contracts |
+| `vocero-api/vocero_api/core/` | Environment parsing, logging, public URLs, and startup diagnostics |
+| `vocero-api/vocero_api/providers/` | ApiPilot and caller/profile/report HTTP integrations |
+| `vocero-api/vocero_api/services/` | Prompt/context construction, reasoning, and tool-context rendering |
+| `vocero-api/vocero_api/realtime/` | WebSocket relay, event handling, marker filtering, and audio forwarding |
+| `vocero-frontend/` | React, TypeScript, Tailwind, and Vite browser application |
| `speech_to_speech/` | Separately run VAD → STT → LLM → TTS pipeline and realtime API |
| `start_ui.sh` | Dependency sync and supervision of FastAPI plus Vite |
| `docs/` | Maintained technical documentation, examples, and historical proposals |
-`s2s-client/server.py` is a compatibility shim for `server:app`; new backend
+`vocero-api/server.py` is a compatibility shim for `server:app`; new backend
implementation belongs in the package. Keep HTTP handlers focused on transport
and reuse providers/services for shared behavior.
@@ -62,37 +62,37 @@ Load-balancer allocation/queues and upstream caller authentication remain.
## Tooling and checks
-The uv project must stay inside `s2s-client/`, including
-`s2s-client/pyproject.toml`, `s2s-client/uv.lock`, `s2s-client/.python-version`,
-`s2s-client/.venv/`, `s2s-client/Makefile`, and
-`s2s-client/.pre-commit-config.yaml`.
+The uv project must stay inside `vocero-api/`, including
+`vocero-api/pyproject.toml`, `vocero-api/uv.lock`, `vocero-api/.python-version`,
+`vocero-api/.venv/`, `vocero-api/Makefile`, and
+`vocero-api/.pre-commit-config.yaml`.
Use Python 3.12 and the uv version required by `tool.uv.required-version`
(currently 0.12.5). Update dependencies through uv and commit the lockfile;
do not use pip to modify this environment or move it to the repository root.
-Keep uv pins in `s2s-client/Dockerfile` and setup documentation aligned when
+Keep uv pins in `vocero-api/Dockerfile` and setup documentation aligned when
changing the required version.
-The frontend uses npm and `s2s-frontend/package-lock.json`. Its Node requirement is
-`^22.18.0 || >=24.0.0`; consult `s2s-frontend/package.json` for current scripts.
+The frontend uses npm and `vocero-frontend/package-lock.json`. Its Node requirement is
+`^22.18.0 || >=24.0.0`; consult `vocero-frontend/package.json` for current scripts.
Keep application code and supported configuration files in TypeScript.
Commands below run from the repository root:
| Command | Purpose |
| ------------------------------------- | --------------------------------------------------------------------- |
-| `make -C s2s-client install` | Sync the locked Python environment and development tools |
-| `make -C s2s-client format` | Apply Ruff fixes and formatting |
-| `make -C s2s-client check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest |
-| `make -C s2s-client test-launcher` | Test readiness, failures, dependency sync, and process-group shutdown |
-| `make -C s2s-client test-image` | Build and smoke-test the actual backend Docker image |
-| `npm --prefix s2s-frontend ci` | Install locked frontend dependencies |
-| `npm --prefix s2s-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
-| `npm --prefix s2s-frontend run build` | Type-check and build frontend assets |
+| `make -C vocero-api install` | Sync the locked Python environment and development tools |
+| `make -C vocero-api format` | Apply Ruff fixes and formatting |
+| `make -C vocero-api check` | Check lock freshness, formatting, Ruff lint, mypy, and pytest |
+| `make -C vocero-api test-launcher` | Test readiness, failures, dependency sync, and process-group shutdown |
+| `make -C vocero-api test-image` | Build and smoke-test the actual backend Docker image |
+| `npm --prefix vocero-frontend ci` | Install locked frontend dependencies |
+| `npm --prefix vocero-frontend run check` | Check formatting, lint, TypeScript, Vitest, and launcher integration |
+| `npm --prefix vocero-frontend run build` | Type-check and build frontend assets |
Python uses Ruff with a 120-character line limit, double quotes, import ordering,
and typed application functions checked by mypy. Frontend formatting and lint
-rules live in `s2s-frontend/prettier.config.ts` and
-`s2s-frontend/eslint.config.ts`; use those tools rather than introducing
+rules live in `vocero-frontend/prettier.config.ts` and
+`vocero-frontend/eslint.config.ts`; use those tools rather than introducing
competing conventions.
Run checks appropriate to the change: backend checks for Python/API work,
@@ -126,7 +126,7 @@ evidence; do not bundle them into tooling or structural refactors.
- Response models validate fields constructed by this service. Upstream-owned
values remain permissive where the old parser was permissive: `Any`, optional
fields, open objects, and arbitrary profile JSON are deliberate boundaries.
-- `validated_json_response` in `s2s-client/s2s_client/api/responses.py` validates
+- `validated_json_response` in `vocero-api/vocero_api/api/responses.py` validates
with Pydantic, then serializes the original payload. Do not substitute
`model_dump()` or automatic response-model serialization: those can insert
defaults, filter unknown keys,
@@ -135,8 +135,8 @@ evidence; do not bundle them into tooling or structural refactors.
- Preserve environment/dotenv parsing, default flags, and import-time behavior
during refactors. Do not introduce stricter configuration validation or change
authentication behavior as part of a tooling change.
-- `s2s-client/tests/fixtures/legacy-contracts.json` captures the server before the
- migration at `3d42df5`. `s2s-client/tests/test_compatibility.py` compares HTTP
+- `vocero-api/tests/fixtures/legacy-contracts.json` captures the server before the
+ migration at `3d42df5`. `vocero-api/tests/test_compatibility.py` compares HTTP
response bytes, statuses, selected headers, outgoing requests, request schemas,
and realtime frames. Do not regenerate captures merely to make a refactor pass.
- Known reasoning failures, ApiPilot authentication/cache quirks, and relay
@@ -148,9 +148,9 @@ evidence; do not bundle them into tooling or structural refactors.
## Browser and realtime behavior
-- React renders the UI; `s2s-frontend/src/realtime/controller.ts` owns observable
- application state, `s2s-frontend/src/realtime/client.ts` owns protocol/session
- lifecycle, and `s2s-frontend/src/audio/` owns the audio graph and worklets.
+- React renders the UI; `vocero-frontend/src/realtime/controller.ts` owns observable
+ application state, `vocero-frontend/src/realtime/client.ts` owns protocol/session
+ lifecycle, and `vocero-frontend/src/audio/` owns the audio graph and worklets.
Keep high-frequency audio/WebSocket work outside React rendering and preserve
cleanup across unmounts and reconnects.
- Preserve the existing 16 kHz PCM16 audio path, worklet registration, interruption
@@ -164,11 +164,11 @@ evidence; do not bundle them into tooling or structural refactors.
server/provider calls; do not assume a vector database or a completed magic-link
authentication flow exists.
- Assistant Markdown is parsed with Marked and sanitized with DOMPurify in
- `s2s-frontend/src/lib/messages.ts`. User text stays literal, and exports retain
+ `vocero-frontend/src/lib/messages.ts`. User text stays literal, and exports retain
raw message text. Keep sanitization and unsafe-link regression coverage when
changing it.
- Reuse the existing design tokens, Tailwind integration, and
- `s2s-frontend/src/styles/legacy-theme.css`. Verify both light and dark themes,
+ `vocero-frontend/src/styles/legacy-theme.css`. Verify both light and dark themes,
narrow screens, keyboard focus, dialog dismissal, and voice-state feedback for
UI changes.
Preserve intentional `?debug=true` controls and browser audio-permission flows.
@@ -178,8 +178,8 @@ evidence; do not bundle them into tooling or structural refactors.
## Startup and deployment boundaries
-`./start_ui.sh` sources `.env`, syncs `s2s-client/.venv/` with
-`uv sync --project s2s-client --locked`, and installs frontend packages when the
+`./start_ui.sh` loads the optional root `.env`, syncs `vocero-api/.venv/` with
+`uv sync --project vocero-api --locked`, and installs frontend packages when the
manifests change.
It starts Vite on `0.0.0.0:8420` and FastAPI on `127.0.0.1:8421`, with
`UI_HOST`, `UI_PORT`, and `S2S_BACKEND_PORT` overrides. Use distinct ports for
@@ -207,7 +207,7 @@ the local relay. Either disables load-balancer mode. Reasoning supports `PROXY`
and `OPENAI`; unknown `S2S_REASONING_TYPE` values fall back to `PROXY`.
The backend Docker image serves APIs, WebSockets, and root `docs/`, not the React
-UI. Build with the repository root as context and `s2s-client/Dockerfile`; the
+UI. Build with the repository root as context and `vocero-api/Dockerfile`; the
image listens on 7860. FastAPI's root remains 404, `/docs` is the API explorer,
and project documentation is served below `/docs/` with retained legacy redirects.
@@ -223,10 +223,10 @@ and project documentation is served below `/docs/` with retained legacy redirect
without language suffixes. Historical proposals do not define current behavior.
- Reserve root Markdown for established files such as `README.md` and `AGENTS.md`.
Keep task summaries in the conversation unless a saved artifact is requested.
- Record notable changes under `Unreleased` in `s2s-client/CHANGELOG.md`.
+ Record notable changes under `Unreleased` in `vocero-api/CHANGELOG.md`.
- Keep credentials and deployment values out of commits and examples. Never copy
the root `.env` into the frontend or expose backend secrets through `VITE_*`.
- The launcher sources `.env` before startup, so it can override shell values;
+ The launcher loads `.env` when present, so its values can override shell values;
direct Python startup uses `load_dotenv(..., override=False)`. Preserve both
behaviors and avoid importing live configuration when a test can isolate it.
diff --git a/README.md b/README.md
index 702b693..48ba8b2 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ documentation, source identifiers, comments, and internal instructions use Engli
| Component | Purpose |
|---|---|
-| [s2s-frontend](s2s-frontend) | React, TypeScript, and Tailwind browser UI, served by Vite with hot reload |
-| [s2s-client](s2s-client) | FastAPI backend, realtime WebSocket proxy, search and ApiPilot bridges |
+| [vocero-frontend](vocero-frontend) | React, TypeScript, and Tailwind browser UI, served by Vite with hot reload |
+| [vocero-api](vocero-api) | FastAPI backend, realtime WebSocket proxy, search and ApiPilot bridges |
| [speech_to_speech](speech_to_speech) | Queue-driven VAD, speech recognition, language model, and speech synthesis pipeline |
Start with [client setup and usage](docs/client/usage.md), then read the
[architecture](docs/client/architecture.md) and [documentation index](docs/README.md).
The speech backend needs its own installed model and runtime dependencies;
-The uv project in `s2s-client/` installs only the UI server dependencies.
+The uv project in `vocero-api/` installs only the UI server dependencies.
## Run the browser client
@@ -24,7 +24,7 @@ S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime ./start_ui.sh
```
Open and allow microphone access. The launcher creates
-`s2s-client/.venv`, syncs locked Python and frontend dependencies,
+`vocero-api/.venv`, syncs locked Python and frontend dependencies,
and loads the root `.env` if present. Vite listens on `UI_PORT` (default `8420`)
and forwards HTTP/WebSocket backend routes to FastAPI on `127.0.0.1:8421`.
Both servers reload during development; stopping the launcher stops both.
@@ -33,16 +33,16 @@ Leave `SPEECH_TO_SPEECH_URL` unset to use the same-origin speech proxy.
The launcher requires uv 0.12.5 (which provisions Python 3.12), Node.js 22.18+ on the 22.x
line or Node.js 24+, npm, Bash 4.4+, `setsid`, and `curl`. For staging hostnames,
set `S2S_DEV_ALLOWED_HOSTS` and forward the Vite HMR WebSocket through the existing
-HTTPS reverse proxy. See [development setup](s2s-frontend/README.md).
+HTTPS reverse proxy. See [development setup](vocero-frontend/README.md).
The Docker image serves backend routes and shared documentation. The React
frontend runs separately through Vite. To build the backend image, use the
repository root as the Docker build context so documentation is included:
```bash
-docker build -f s2s-client/Dockerfile -t s2s-client .
+docker build -f vocero-api/Dockerfile -t vocero-api .
docker run --rm -p 7860:7860 \
- -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime s2s-client
+ -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime vocero-api
```
The direct WebSocket URL is resolved by the user's browser. For a server-side
@@ -50,7 +50,7 @@ proxy deployment, supply a backend address reachable from the container instead.
## Project references
-- [Change history](s2s-client/CHANGELOG.md)
+- [Change history](vocero-api/CHANGELOG.md)
- [Backend development and compatibility](docs/client/development.md)
- [Realtime protocol and pipeline](docs/speech/realtime-api.md)
- [Context injection](docs/client/context-injection.md)
diff --git a/docs/README.md b/docs/README.md
index c667713..234c10d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -15,7 +15,7 @@ do not define the current implementation.
- [Design language](client/design.md)
- [Glossary](client/glossary.md)
- [Troubleshooting](client/troubleshooting.md)
-- [Change history](../s2s-client/CHANGELOG.md)
+- [Change history](../vocero-api/CHANGELOG.md)
## Speech backend
diff --git a/docs/client/architecture.md b/docs/client/architecture.md
index b4a1d56..a573884 100644
--- a/docs/client/architecture.md
+++ b/docs/client/architecture.md
@@ -1,7 +1,7 @@
# Client architecture
-The [React browser client](../../s2s-frontend) uses the
-[FastAPI backend](../../s2s-client) for HTTP APIs and a speech WebSocket relay.
+The [React browser client](../../vocero-frontend) uses the
+[FastAPI backend](../../vocero-api) for HTTP APIs and a speech WebSocket relay.
`start_ui.sh` serves Vite on the existing public port `8420` and FastAPI on
loopback port `8421`. Vite forwards backend routes and provides hot reload;
Uvicorn reloads Python edits. The launcher supervises both processes.
@@ -42,22 +42,22 @@ audio. It is not an unconditional byte-for-byte tunnel.
| Source | Responsibility |
|---|---|
-| [server.py](../../s2s-client/server.py) | Compatibility entry point for existing `server:app` commands |
-| [main.py](../../s2s-client/s2s_client/main.py) | FastAPI assembly, documentation serving and redirects |
-| [api](../../s2s-client/s2s_client/api) | HTTP route registration, handlers and compatible response validation |
-| [models](../../s2s-client/s2s_client/models) | Pydantic request and response contracts |
-| [core](../../s2s-client/s2s_client/core) | Existing environment parsing, logging, URL helpers and diagnostics |
-| [providers](../../s2s-client/s2s_client/providers) | Caller context and ApiPilot HTTP integrations |
-| [services](../../s2s-client/s2s_client/services) | Context construction, reasoning and tool-context rendering |
-| [realtime](../../s2s-client/s2s_client/realtime) | WebSocket relay, events, markers and audio handling |
+| [server.py](../../vocero-api/server.py) | Compatibility entry point for existing `server:app` commands |
+| [main.py](../../vocero-api/vocero_api/main.py) | FastAPI assembly, documentation serving and redirects |
+| [api](../../vocero-api/vocero_api/api) | HTTP route registration, handlers and compatible response validation |
+| [models](../../vocero-api/vocero_api/models) | Pydantic request and response contracts |
+| [core](../../vocero-api/vocero_api/core) | Existing environment parsing, logging, URL helpers and diagnostics |
+| [providers](../../vocero-api/vocero_api/providers) | Caller context and ApiPilot HTTP integrations |
+| [services](../../vocero-api/vocero_api/services) | Context construction, reasoning and tool-context rendering |
+| [realtime](../../vocero-api/vocero_api/realtime) | WebSocket relay, events, markers and audio handling |
| [start_ui.sh](../../start_ui.sh) | Dependency setup, ports, readiness, reload processes, and shutdown |
-| [vite.config.ts](../../s2s-frontend/vite.config.ts) | Public development endpoint, HMR, allowed hosts, and backend routing |
-| [App.tsx](../../s2s-frontend/src/App.tsx) | React layout, voice controls, dialogs, profile and reports sidebar |
-| [controller.ts](../../s2s-frontend/src/realtime/controller.ts) | Application state, settings, context, queue, tools and external reasoning |
-| [client.ts](../../s2s-frontend/src/realtime/client.ts) | Realtime protocol, session lifecycle, audio and transcript events |
-| [components](../../s2s-frontend/src/components) | Chat, caller profile, settings and voice components |
-| [audio](../../s2s-frontend/src/audio) | TypeScript capture/playback worklets, PCM codec, and visualizer |
-| [legacy-theme.css](../../s2s-frontend/src/styles/legacy-theme.css) | Retained themes and responsive visual styles alongside Tailwind |
+| [vite.config.ts](../../vocero-frontend/vite.config.ts) | Public development endpoint, HMR, allowed hosts, and backend routing |
+| [App.tsx](../../vocero-frontend/src/App.tsx) | React layout, voice controls, dialogs, profile and reports sidebar |
+| [controller.ts](../../vocero-frontend/src/realtime/controller.ts) | Application state, settings, context, queue, tools and external reasoning |
+| [client.ts](../../vocero-frontend/src/realtime/client.ts) | Realtime protocol, session lifecycle, audio and transcript events |
+| [components](../../vocero-frontend/src/components) | Chat, caller profile, settings and voice components |
+| [audio](../../vocero-frontend/src/audio) | TypeScript capture/playback worklets, PCM codec, and visualizer |
+| [legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) | Retained themes and responsive visual styles alongside Tailwind |
## Server routes
@@ -79,9 +79,9 @@ explorer, while project documentation files are served in the `/docs/`
subdirectories. Markdown remains
source text, and directories without `index.html` are not browsable pages.
FastAPI serves backend and documentation routes only. The frontend source and
-assets live in `s2s-frontend` and are served by Vite.
+assets live in `vocero-frontend` and are served by Vite.
-The uv project and its Python tools are scoped to `s2s-client/`. Response models
+The uv project and its Python tools are scoped to `vocero-api/`. Response models
describe service-owned structure and retain loose upstream values. See
[development and compatibility](development.md) before tightening a contract.
@@ -89,11 +89,11 @@ describe service-owned structure and retain loose upstream values. See
1. The browser captures microphone audio with echo cancellation, noise
suppression and automatic gain-control settings.
-2. [mic-capture.worklet.ts](../../s2s-frontend/src/audio/mic-capture.worklet.ts) resamples to 16 kHz
+2. [mic-capture.worklet.ts](../../vocero-frontend/src/audio/mic-capture.worklet.ts) resamples to 16 kHz
mono PCM16. The browser sends base64 `input_audio_buffer.append` frames.
3. The backend processes VAD → STT → LLM → TTS and emits realtime events.
4. Assistant PCM16 audio at the current 16 kHz protocol rate is decoded and sent to
- [audio-playback.worklet.ts](../../s2s-frontend/src/audio/audio-playback.worklet.ts), which buffers,
+ [audio-playback.worklet.ts](../../vocero-frontend/src/audio/audio-playback.worklet.ts), which buffers,
resamples and fades playback edges. Input speech can clear playback for barge-in.
Audio worklets are loaded through `AudioWorklet.addModule`; keep their processor
diff --git a/docs/client/context-injection.md b/docs/client/context-injection.md
index 8a6e62e..a10bb08 100644
--- a/docs/client/context-injection.md
+++ b/docs/client/context-injection.md
@@ -2,10 +2,10 @@
Context reaches the assistant through browser-built instructions, server-patched
realtime session instructions, and HTTP reasoning context. The implementation is
-in the server's [context services](../../s2s-client/s2s_client/services/context.py),
-[realtime proxy](../../s2s-client/s2s_client/realtime/proxy.py),
-[context.ts](../../s2s-frontend/src/lib/context.ts), and the
-[WebSocket client](../../s2s-frontend/src/realtime/client.ts).
+in the server's [context services](../../vocero-api/vocero_api/services/context.py),
+[realtime proxy](../../vocero-api/vocero_api/realtime/proxy.py),
+[context.ts](../../vocero-frontend/src/lib/context.ts), and the
+[WebSocket client](../../vocero-frontend/src/realtime/client.ts).
## Independent controls
diff --git a/docs/client/design.md b/docs/client/design.md
index f2a6fb5..11e4944 100644
--- a/docs/client/design.md
+++ b/docs/client/design.md
@@ -1,8 +1,8 @@
# Design language
This guide describes the current browser UI. Use
-[legacy-theme.css](../../s2s-frontend/src/styles/legacy-theme.css) as the token/selector
-reference and [App.tsx](../../s2s-frontend/src/App.tsx) for the theme-specific structure.
+[legacy-theme.css](../../vocero-frontend/src/styles/legacy-theme.css) as the token/selector
+reference and [App.tsx](../../vocero-frontend/src/App.tsx) for the theme-specific structure.
Earlier documentation described a monochrome-only dark UI; the current design
also uses aurora gradients, glass surfaces, and a separate light theme.
diff --git a/docs/client/development.md b/docs/client/development.md
index f5068a7..b68a5d3 100644
--- a/docs/client/development.md
+++ b/docs/client/development.md
@@ -1,11 +1,18 @@
# Backend development and compatibility
-The FastAPI project lives entirely in `s2s-client/`. It uses uv 0.12.5, Python
+The FastAPI project lives entirely in `vocero-api/`. It uses uv 0.12.5, Python
3.12, and a committed `uv.lock`. The frontend and speech pipeline keep their
own tooling. Runtime dependency versions match the environment used to capture
the pre-migration server; framework upgrades require a separate compatibility
review.
+The Python distribution is named `vocero-api`, and its import package is
+`vocero_api`. The browser package and directory are both named `vocero-frontend`.
+`start_ui.sh` creates the API environment at its current path on the next start;
+do not move an existing virtual environment because its executables contain
+absolute paths. Reinstall optional Git hooks after a project-directory rename
+with `make -C vocero-api hooks-install`.
+
Pytest filters only the known `anyio.abc.BlockingPortal` deprecation emitted by
Starlette's test client ([upstream issue](https://github.com/Kludex/starlette/issues/3497)).
The filter matches the exact message, warning category, and module; other
@@ -17,24 +24,24 @@ the deprecated alias. Runtime dependencies and warning handling are unchanged.
From the repository root:
```bash
-make -C s2s-client install # uv sync --locked, including development tools
-make -C s2s-client format # Ruff safe fixes and formatting
-make -C s2s-client check # lock freshness, format, lint, mypy, pytest
-make -C s2s-client test-launcher # Bash supervisor integration fixtures
-make -C s2s-client test-image # Docker build and isolated runtime smoke test
+make -C vocero-api install # uv sync --locked, including development tools
+make -C vocero-api format # Ruff safe fixes and formatting
+make -C vocero-api check # lock freshness, format, lint, mypy, pytest
+make -C vocero-api test-launcher # Bash supervisor integration fixtures
+make -C vocero-api test-image # Docker build and isolated runtime smoke test
```
-Run `uv lock` inside `s2s-client/` when intentionally changing dependencies.
+Run `uv lock` inside `vocero-api/` when intentionally changing dependencies.
Do not install server packages into a shared root environment. The launcher
-syncs `s2s-client/.venv` and retains root `.env` loading, port defaults,
+syncs `vocero-api/.venv` and retains root `.env` loading, port defaults,
readiness checks, and process-group shutdown. Offline starts require the
locked packages and Python runtime to have been installed or cached already.
-Optional hooks use `make -C s2s-client hooks-install`; the configuration stays
-in `s2s-client/.pre-commit-config.yaml`. `make -C s2s-client hooks-run` runs the
+Optional hooks use `make -C vocero-api hooks-install`; the configuration stays
+in `vocero-api/.pre-commit-config.yaml`. `make -C vocero-api hooks-run` runs the
same hooks without installing them. Hooks and Python quality checks apply
-only to the client service. The server entry points are `s2s_client.main:app`
-and the retained `server:app` shim; use `--app-dir s2s-client` from the root.
+only to the client service. The server entry points are `vocero_api.main:app`
+and the retained `server:app` shim; use `--app-dir vocero-api` from the root.
## Schema boundaries
diff --git a/docs/client/reasoning.md b/docs/client/reasoning.md
index fe69a0b..46d727a 100644
--- a/docs/client/reasoning.md
+++ b/docs/client/reasoning.md
@@ -13,7 +13,7 @@ The mode is normalized case-insensitively. Unknown values resolve to `PROXY`.
## Shared instruction assembly
-In [context.ts](../../s2s-frontend/src/lib/context.ts), `buildInstructions()` starts
+In [context.ts](../../vocero-frontend/src/lib/context.ts), `buildInstructions()` starts
with `config.primaryPrompt || settings.instructions`. `/api/config` pins
`S2S_PRIMARY_PROMPT` when non-empty; otherwise the settings prompt or
`DEFAULT_INSTRUCTIONS` is used. The current server does not fetch a remote agent
@@ -50,7 +50,7 @@ OPENAI_REASONING_MODEL=gpt-4.1-mini
`OPENAI_ORGANIZATION` and `OPENAI_PROJECT` are optional headers. The model value
is a runtime default, not a recommendation about provider availability.
-`VoiceController.reason()` in [controller.ts](../../s2s-frontend/src/realtime/controller.ts)
+`VoiceController.reason()` in [controller.ts](../../vocero-frontend/src/realtime/controller.ts)
runs after a complete user transcript and posts a body with `transcript`,
`instructions`, `language`, `user_context`, `phoneNumber`, and `episode_id`.
The server combines instructions and context into model messages, requests a
@@ -76,7 +76,7 @@ realtime backend instead.
| Goal | Source |
|---|---|
| Change the base agent prompt | `S2S_PRIMARY_PROMPT` or browser settings when unpinned |
-| Change browser context assembly | `buildInstructions()` in `s2s-frontend/src/lib/context.ts` |
-| Change HTTP provider messages | `reasoning()` in `s2s-client/s2s_client/services/reasoning.py` |
+| Change browser context assembly | `buildInstructions()` in `vocero-frontend/src/lib/context.ts` |
+| Change HTTP provider messages | `reasoning()` in `vocero-api/vocero_api/services/reasoning.py` |
| Change external-answer rendering | `VoiceController.reason()` and `RealtimeClient.renderExternalAnswer()` |
| Change native model/tool generation | `speech_to_speech/LLM` and realtime handlers |
diff --git a/docs/client/tool-calling.md b/docs/client/tool-calling.md
index ec5767e..9d1300c 100644
--- a/docs/client/tool-calling.md
+++ b/docs/client/tool-calling.md
@@ -48,8 +48,8 @@ For a native call, the backend emits:
```
`arguments` is a JSON-encoded string. The
-[WebSocket client](../../s2s-frontend/src/realtime/client.ts) validates the event and
-dispatches `tool`; [controller.ts](../../s2s-frontend/src/realtime/controller.ts)
+[WebSocket client](../../vocero-frontend/src/realtime/client.ts) validates the event and
+dispatches `tool`; [controller.ts](../../vocero-frontend/src/realtime/controller.ts)
parses the arguments and runs the supported executor.
Web search uses `POST /api/search`, keeping the deployment's Serper key on the
server. Tool output returns to the model as:
diff --git a/docs/client/troubleshooting.md b/docs/client/troubleshooting.md
index 05bb4ed..aa46a92 100644
--- a/docs/client/troubleshooting.md
+++ b/docs/client/troubleshooting.md
@@ -37,7 +37,7 @@ the separate `/v1/realtime` WebSocket on the same public origin.
For a startup dependency error, check uv 0.12.5 and Node/npm on the process
manager's PATH, plus `setsid`/`curl`. uv manages Python 3.12 in
-`s2s-client/.venv` and checks the committed lock on every start. Frontend
+`vocero-api/.venv` and checks the committed lock on every start. Frontend
dependencies are reused until their manifests change. Changes to root
`.env` or launcher settings require a restart.
@@ -123,9 +123,9 @@ without evidence.
From the repository root, run the frontend checks and local backend fixtures:
```bash
-npm --prefix s2s-frontend run check
-npm --prefix s2s-frontend run build
-make -C s2s-client check
+npm --prefix vocero-frontend run check
+npm --prefix vocero-frontend run build
+make -C vocero-api check
```
These checks cover frontend contracts and local session/queue/WebSocket
diff --git a/docs/client/usage.md b/docs/client/usage.md
index 933a29d..12764ef 100644
--- a/docs/client/usage.md
+++ b/docs/client/usage.md
@@ -12,8 +12,8 @@ From the repository root:
S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime ./start_ui.sh
```
-The launcher loads the root `.env`, syncs `s2s-client/.venv` from the committed
-`s2s-client/uv.lock`, and installs the locked
+The launcher loads the root `.env`, syncs `vocero-api/.venv` from the committed
+`vocero-api/uv.lock`, and installs the locked
frontend dependencies when needed. Vite serves the React frontend on port
8420 and proxies the backend routes to Uvicorn on `127.0.0.1:8421`.
`UI_PORT` overrides the public port; `S2S_BACKEND_PORT` overrides the internal
@@ -24,21 +24,21 @@ the script, values in that file can replace variables supplied on the command li
Use Node.js 22.18+ on the 22.x line or Node.js 24+, npm, uv 0.12.5,
Bash 4.4+, `setsid`, and `curl`. Remote staging access also needs the exact
hostname in `S2S_DEV_ALLOWED_HOSTS`, HTTPS, and WebSocket forwarding for speech
-and Vite HMR. See [frontend development setup](../../s2s-frontend/README.md).
+and Vite HMR. See [frontend development setup](../../vocero-frontend/README.md).
To install and start the backend manually:
```bash
-uv sync --project s2s-client --locked
+uv sync --project vocero-api --locked
S2S_PROXY_TARGET=ws://localhost:8765/v1/realtime \
- uv run --project s2s-client --locked uvicorn --app-dir s2s-client s2s_client.main:app \
- --host 127.0.0.1 --port 8421 --reload --reload-dir s2s-client
+ uv run --project vocero-api --locked uvicorn --app-dir vocero-api vocero_api.main:app \
+ --host 127.0.0.1 --port 8421 --reload --reload-dir vocero-api
```
-Then, in a second terminal, run `cd s2s-frontend`, `npm ci`, and `npm run dev`.
+Then, in a second terminal, run `cd vocero-frontend`, `npm ci`, and `npm run dev`.
uv provisions Python 3.12 when needed. The Python environment belongs only to
-`s2s-client`; an existing root `.venv` for speech dependencies is left alone.
+`vocero-api`; an existing root `.venv` for speech dependencies is left alone.
See [development and compatibility](development.md) for checks and schema rules.
Open , tap the orb/microphone control, grant microphone
@@ -94,9 +94,9 @@ backend address. The image does not run the development launcher or serve a UI.
Use the project root as the build context:
```bash
-docker build -f s2s-client/Dockerfile -t s2s-client .
+docker build -f vocero-api/Dockerfile -t vocero-api .
docker run --rm -p 7860:7860 \
- -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime s2s-client
+ -e SPEECH_TO_SPEECH_URL=ws://localhost:8765/v1/realtime vocero-api
```
For the proxy path, replace the direct setting with a server-reachable
@@ -104,7 +104,7 @@ For the proxy path, replace the direct setting with a server-reachable
appropriate existing network and use that service's hostname. An upstream
bound only to host loopback is not reachable via an arbitrary container name.
-The image includes `s2s-client` and root `docs`. `/docs` is still the FastAPI API
+The image includes `vocero-api` and root `docs`. `/docs` is still the FastAPI API
explorer; documentation lives under `/docs/client/`, `/docs/speech/`,
`/docs/adr/`, `/docs/examples/`, and `/docs/proposals/`. Legacy `/project/` and
`/public/docs.html` redirect to the historical proposal. Markdown is served as a
diff --git a/start_ui.sh b/start_ui.sh
index 2554da4..71d2e90 100755
--- a/start_ui.sh
+++ b/start_ui.sh
@@ -2,9 +2,9 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-CLIENT_DIR="$ROOT_DIR/s2s-client"
-VENV_DIR="$CLIENT_DIR/.venv"
-FRONTEND_DIR="$ROOT_DIR/s2s-frontend"
+API_DIR="$ROOT_DIR/vocero-api"
+VENV_DIR="$API_DIR/.venv"
+FRONTEND_DIR="$ROOT_DIR/vocero-frontend"
if [ -f "$ROOT_DIR/.env" ]; then
set -a
source "$ROOT_DIR/.env"
@@ -28,7 +28,7 @@ for required_command in uv node npm setsid curl; do
done
if ! node -e 'const [major, minor] = process.versions.node.split(".").map(Number); process.exit((major === 22 && minor >= 18) || major >= 24 ? 0 : 1)'; then
- echo "Error: s2s-frontend requires Node.js 22.18+ on the 22.x line, or Node.js 24+."
+ echo "Error: vocero-frontend requires Node.js 22.18+ on the 22.x line, or Node.js 24+."
exit 1
fi
@@ -46,19 +46,19 @@ if [ "$UI_PORT" = "$S2S_BACKEND_PORT" ]; then
exit 1
fi
-if [ ! -f "$CLIENT_DIR/pyproject.toml" ] || [ ! -f "$CLIENT_DIR/uv.lock" ]; then
- echo "Error: s2s-client/pyproject.toml and s2s-client/uv.lock are required."
+if [ ! -f "$API_DIR/pyproject.toml" ] || [ ! -f "$API_DIR/uv.lock" ]; then
+ echo "Error: vocero-api/pyproject.toml and vocero-api/uv.lock are required."
exit 1
fi
-# uv manages only the client environment; an existing root speech venv is separate.
-UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$CLIENT_DIR" --locked
+# uv manages only the API environment; an existing root speech venv is separate.
+UV_PROJECT_ENVIRONMENT="$VENV_DIR" uv sync --project "$API_DIR" --locked
# Keep restarts offline when the installed frontend manifests have not changed.
if [ ! -f "$FRONTEND_DIR/node_modules/vite/bin/vite.js" ] || \
! cmp -s "$FRONTEND_DIR/package-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json" || \
! cmp -s "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.json"; then
- echo "Installing frontend dependencies from s2s-frontend/package-lock.json..."
+ echo "Installing frontend dependencies from vocero-frontend/package-lock.json..."
npm --prefix "$FRONTEND_DIR" ci --include=dev
cp "$FRONTEND_DIR/package-lock.json" "$FRONTEND_DIR/node_modules/.s2s-package-lock.json"
cp "$FRONTEND_DIR/package.json" "$FRONTEND_DIR/node_modules/.s2s-package.json"
@@ -109,10 +109,10 @@ echo " Press Ctrl+C in this terminal to stop both servers."
echo "----------------------------------------------------"
cd "$ROOT_DIR"
-setsid "$VENV_DIR/bin/python" -m uvicorn s2s_client.main:app \
- --app-dir "$CLIENT_DIR" \
+setsid "$VENV_DIR/bin/python" -m uvicorn vocero_api.main:app \
+ --app-dir "$API_DIR" \
--host 127.0.0.1 --port "$S2S_BACKEND_PORT" \
- --reload --reload-dir "$CLIENT_DIR" &
+ --reload --reload-dir "$API_DIR" &
backend_pid=$!
child_pids+=("$backend_pid")
diff --git a/s2s-client/.dockerignore b/vocero-api/.dockerignore
similarity index 100%
rename from s2s-client/.dockerignore
rename to vocero-api/.dockerignore
diff --git a/s2s-client/.gitignore b/vocero-api/.gitignore
similarity index 100%
rename from s2s-client/.gitignore
rename to vocero-api/.gitignore
diff --git a/s2s-client/.pre-commit-config.yaml b/vocero-api/.pre-commit-config.yaml
similarity index 65%
rename from s2s-client/.pre-commit-config.yaml
rename to vocero-api/.pre-commit-config.yaml
index 59ed36d..6663347 100644
--- a/s2s-client/.pre-commit-config.yaml
+++ b/vocero-api/.pre-commit-config.yaml
@@ -7,21 +7,21 @@ repos:
hooks:
- id: ruff-check
name: Ruff lint
- entry: uv run --project s2s-client --locked ruff check --force-exclude
+ entry: uv run --project vocero-api --locked ruff check --force-exclude
language: unsupported
types_or: [python, pyi]
- files: ^s2s-client/
+ files: ^vocero-api/
require_serial: true
- id: ruff-format
name: Ruff formatting
- entry: uv run --project s2s-client --locked ruff format --check --force-exclude
+ entry: uv run --project vocero-api --locked ruff format --check --force-exclude
language: unsupported
types_or: [python, pyi]
- files: ^s2s-client/
+ files: ^vocero-api/
require_serial: true
- id: mypy
name: Python types
- entry: uv run --directory s2s-client --locked mypy
+ entry: uv run --directory vocero-api --locked mypy
language: unsupported
pass_filenames: false
- files: ^s2s-client/(s2s_client/|server\.py|pyproject\.toml|uv\.lock)
+ files: ^vocero-api/(vocero_api/|server\.py|pyproject\.toml|uv\.lock)
diff --git a/s2s-client/.python-version b/vocero-api/.python-version
similarity index 100%
rename from s2s-client/.python-version
rename to vocero-api/.python-version
diff --git a/s2s-client/CHANGELOG.md b/vocero-api/CHANGELOG.md
similarity index 95%
rename from s2s-client/CHANGELOG.md
rename to vocero-api/CHANGELOG.md
index 3b97d9f..1f03219 100644
--- a/s2s-client/CHANGELOG.md
+++ b/vocero-api/CHANGELOG.md
@@ -1,4 +1,4 @@
-# Changelog — s2s-client (Azurian Vocero)
+# Changelog — vocero-api (Azurian Vocero)
All significant client changes are recorded here. This English history
supersedes the former Italian and Spanish versions. Historical entries describe
@@ -9,20 +9,25 @@ the implementation at the recorded date; current operating instructions live in
### Changed
+- Renamed the API and browser projects to `vocero-api/` and `vocero-frontend/`.
+ Python uses the `vocero-api` distribution and `vocero_api` import package;
+ npm uses `vocero-frontend`. Updated the launcher, Docker setup, tooling,
+ tests, and documentation while preserving HTTP/WebSocket contracts and
+ existing environment variables, browser preferences, and PM2 process names.
- Removed the optional workflow reasoning service, its configuration, transcript
forwarding, and browser integration. Reasoning supports `PROXY` and `OPENAI`;
unsupported mode values fall back to `PROXY`.
- Filter the specific Starlette TestClient / AnyIO `BlockingPortal` deprecation
in pytest while retaining the runtime versions used for compatibility captures.
- Added a uv-managed Python 3.12 environment and Ruff, mypy, and pytest quality
- checks, all contained in `s2s-client/`. Startup and Docker use the committed
+ checks, all contained in `vocero-api/`. Startup and Docker use the committed
lockfile. Split the FastAPI service into configuration, route, provider,
context/reasoning, and realtime modules while retaining `server:app` startup.
- Added Pydantic response contracts with permissive upstream fields and original
payload serialization. Captured HTTP, request-schema, and realtime regression
fixtures preserve existing API behavior, including known failures.
- Removed the legacy browser site in favor of the React application in
- `s2s-frontend`. FastAPI now serves only backend and documentation routes.
+ `vocero-frontend`. FastAPI now serves only backend and documentation routes.
- Retired daily talk-time accounting and the associated Hugging Face account
UI. Load-balancer allocation, queue lifecycle, caller profiles, upstream
authentication, and voice-session cleanup remain available.
diff --git a/s2s-client/Dockerfile b/vocero-api/Dockerfile
similarity index 57%
rename from s2s-client/Dockerfile
rename to vocero-api/Dockerfile
index 4d43a41..c633983 100644
--- a/s2s-client/Dockerfile
+++ b/vocero-api/Dockerfile
@@ -1,4 +1,4 @@
-# Build from the project root: docker build -f s2s-client/Dockerfile -t s2s-client .
+# Build from the project root: docker build -f vocero-api/Dockerfile -t vocero-api .
# Serve backend APIs, the WebSocket proxy, and shared project documentation.
# Run the React/Vite frontend separately; this image contains no browser UI.
# HF Spaces (sdk: docker) routes traffic to $PORT, default 7860.
@@ -8,18 +8,18 @@ COPY --from=ghcr.io/astral-sh/uv:0.12.5 /uv /uvx /bin/
ENV UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=never \
- PATH="/app/s2s-client/.venv/bin:$PATH"
+ PATH="/app/vocero-api/.venv/bin:$PATH"
-WORKDIR /app/s2s-client
+WORKDIR /app/vocero-api
-COPY s2s-client/pyproject.toml s2s-client/uv.lock ./
+COPY vocero-api/pyproject.toml vocero-api/uv.lock ./
RUN uv sync --locked --no-dev --no-install-project
-COPY s2s-client/README.md s2s-client/server.py ./
-COPY s2s-client/s2s_client/ ./s2s_client/
+COPY vocero-api/README.md vocero-api/server.py ./
+COPY vocero-api/vocero_api/ ./vocero_api/
COPY docs/ /app/docs/
RUN uv sync --locked --no-dev
EXPOSE 7860
-CMD ["uvicorn", "s2s_client.main:app", "--host", "0.0.0.0", "--port", "7860"]
+CMD ["uvicorn", "vocero_api.main:app", "--host", "0.0.0.0", "--port", "7860"]
diff --git a/s2s-client/Makefile b/vocero-api/Makefile
similarity index 86%
rename from s2s-client/Makefile
rename to vocero-api/Makefile
index 87a0d0c..b342ef4 100644
--- a/s2s-client/Makefile
+++ b/vocero-api/Makefile
@@ -48,13 +48,13 @@ dev:
../start_ui.sh
hooks-install:
- $(UV_RUN) pre-commit install --install-hooks --config s2s-client/.pre-commit-config.yaml
+ $(UV_RUN) pre-commit install --install-hooks --config vocero-api/.pre-commit-config.yaml
hooks-run:
- $(UV_RUN) pre-commit run --all-files --config s2s-client/.pre-commit-config.yaml
+ $(UV_RUN) pre-commit run --all-files --config vocero-api/.pre-commit-config.yaml
test-launcher:
- npm --prefix ../s2s-frontend run test:launcher
+ npm --prefix ../vocero-frontend run test:launcher
test-image:
bash scripts/test_docker_image.sh
diff --git a/s2s-client/README.md b/vocero-api/README.md
similarity index 81%
rename from s2s-client/README.md
rename to vocero-api/README.md
index 2d7cf86..ece8538 100644
--- a/s2s-client/README.md
+++ b/vocero-api/README.md
@@ -9,15 +9,15 @@ pinned: false
short_description: Voice chat over WebSocket against a HF speech-to-speech
---
-# Realtime voice backend
+# Vocero API
-FastAPI APIs and WebSocket relay for the [React voice frontend](../s2s-frontend).
+FastAPI APIs and WebSocket relay for the [React voice frontend](../vocero-frontend).
This service handles configuration, caller context, upstream authentication,
search, reasoning, ApiPilot tools, and optional load-balancer allocation.
-The root `start_ui.sh` now serves the [React frontend](../s2s-frontend) with
+The root `start_ui.sh` now serves the [React frontend](../vocero-frontend) with
Vite on public port `8420`, proxying backend requests here on loopback port
-`8421`. The browser UI lives entirely in `s2s-frontend`; running FastAPI directly
+`8421`. The browser UI lives entirely in `vocero-frontend`; running FastAPI directly
or through Docker exposes only backend routes and shared documentation.
The complete [usage guide](../docs/client/usage.md) covers local startup,
@@ -28,7 +28,7 @@ and [change history](CHANGELOG.md) for implementation details and history.
Build from the repository root so the container includes the documentation:
```bash
-docker build -f s2s-client/Dockerfile -t s2s-client .
+docker build -f vocero-api/Dockerfile -t vocero-api .
```
The Python project is contained in this directory: `pyproject.toml`, `uv.lock`,
@@ -36,8 +36,8 @@ The Python project is contained in this directory: `pyproject.toml`, `uv.lock`,
3.12 (uv can provision Python). From the repository root:
```bash
-make -C s2s-client install
-make -C s2s-client check
+make -C vocero-api install
+make -C vocero-api check
```
Tests isolate environment configuration, use HTTP fixtures for allocation and
@@ -46,8 +46,9 @@ frames, keepalive, and teardown. No deployed services or database are needed.
See [backend development and compatibility](../docs/client/development.md) for
formatting, typing, schema policy, optional Git hooks, and deployment checks.
-`uvicorn server:app --app-dir s2s-client` remains a supported entry point;
-the implementation now lives in `s2s_client/`.
+`uvicorn server:app --app-dir vocero-api` remains a supported entry point;
+the implementation lives in the `vocero_api/` package. Its Python distribution
+name is `vocero-api`.
FastAPI's API explorer remains at `/docs`. Project documents are served as files below
`/docs/client/`, `/docs/speech/`, `/docs/adr/`, `/docs/examples/`, and
diff --git a/s2s-client/pyproject.toml b/vocero-api/pyproject.toml
similarity index 91%
rename from s2s-client/pyproject.toml
rename to vocero-api/pyproject.toml
index 295d6e3..b8dcebc 100644
--- a/s2s-client/pyproject.toml
+++ b/vocero-api/pyproject.toml
@@ -3,7 +3,7 @@ requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
-name = "aquaportal-s2s-client"
+name = "vocero-api"
version = "0.1.0"
description = "FastAPI integration and realtime proxy for the Vocero frontend"
readme = "README.md"
@@ -29,12 +29,12 @@ dev = [
required-version = "==0.12.5"
[tool.setuptools.packages.find]
-include = ["s2s_client*"]
+include = ["vocero_api*"]
[tool.ruff]
target-version = "py312"
line-length = 120
-include = ["pyproject.toml", "s2s_client/**/*.py", "server.py", "tests/**/*.py", "scripts/**/*.py"]
+include = ["pyproject.toml", "vocero_api/**/*.py", "server.py", "tests/**/*.py", "scripts/**/*.py"]
[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "B", "UP"]
@@ -48,7 +48,7 @@ line-ending = "auto"
[tool.mypy]
python_version = "3.12"
plugins = ["pydantic.mypy"]
-files = ["s2s_client", "server.py"]
+files = ["vocero_api", "server.py"]
check_untyped_defs = true
disallow_untyped_defs = true
no_implicit_optional = true
diff --git a/s2s-client/scripts/test_docker_image.sh b/vocero-api/scripts/test_docker_image.sh
similarity index 94%
rename from s2s-client/scripts/test_docker_image.sh
rename to vocero-api/scripts/test_docker_image.sh
index ec0e598..b41e7f0 100644
--- a/s2s-client/scripts/test_docker_image.sh
+++ b/vocero-api/scripts/test_docker_image.sh
@@ -3,7 +3,7 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
-IMAGE="s2s-client-smoke:$(date +%s)-$$"
+IMAGE="vocero-api-smoke:$(date +%s)-$$"
container_id=""
cleanup() {
if [ -n "$container_id" ]; then
@@ -13,7 +13,7 @@ cleanup() {
}
trap cleanup EXIT
-docker build --file "$ROOT_DIR/s2s-client/Dockerfile" --tag "$IMAGE" "$ROOT_DIR"
+docker build --file "$ROOT_DIR/vocero-api/Dockerfile" --tag "$IMAGE" "$ROOT_DIR"
container_id="$(docker run --detach --publish 127.0.0.1::7860 "$IMAGE")"
port="$(docker port "$container_id" 7860/tcp)"
ready=false
diff --git a/s2s-client/server.py b/vocero-api/server.py
similarity index 62%
rename from s2s-client/server.py
rename to vocero-api/server.py
index d35bdba..c98494c 100644
--- a/s2s-client/server.py
+++ b/vocero-api/server.py
@@ -3,7 +3,7 @@
import sys
from pathlib import Path
-# Also support --app-dir /path/to/s2s-client when launched outside the checkout.
+# Also support --app-dir /path/to/vocero-api when launched outside the checkout.
sys.path.insert(0, str(Path(__file__).resolve().parent))
-from s2s_client.main import app as app # noqa: E402
+from vocero_api.main import app as app # noqa: E402
diff --git a/s2s-client/tests/fixtures/legacy-contracts.json b/vocero-api/tests/fixtures/legacy-contracts.json
similarity index 100%
rename from s2s-client/tests/fixtures/legacy-contracts.json
rename to vocero-api/tests/fixtures/legacy-contracts.json
diff --git a/s2s-client/tests/test_compatibility.py b/vocero-api/tests/test_compatibility.py
similarity index 98%
rename from s2s-client/tests/test_compatibility.py
rename to vocero-api/tests/test_compatibility.py
index b329c7b..76bdc41 100644
--- a/s2s-client/tests/test_compatibility.py
+++ b/vocero-api/tests/test_compatibility.py
@@ -17,11 +17,11 @@ import httpx
from fastapi.testclient import TestClient
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
- from s2s_client import main as server
- from s2s_client.core import config as settings
- from s2s_client.providers import apipilot
- from s2s_client.realtime import proxy
- from s2s_client.services import context
+ from vocero_api import main as server
+ from vocero_api.core import config as settings
+ from vocero_api.providers import apipilot
+ from vocero_api.realtime import proxy
+ from vocero_api.services import context
ASYNC_CLIENT = httpx.AsyncClient
FIXTURE = Path(__file__).parent / "fixtures" / "legacy-contracts.json"
diff --git a/s2s-client/tests/test_models.py b/vocero-api/tests/test_models.py
similarity index 89%
rename from s2s-client/tests/test_models.py
rename to vocero-api/tests/test_models.py
index 35f2b8d..8cfa411 100644
--- a/s2s-client/tests/test_models.py
+++ b/vocero-api/tests/test_models.py
@@ -4,9 +4,9 @@ import pytest
from fastapi.responses import JSONResponse
from pydantic import ValidationError
-from s2s_client.api.responses import validated_json_response
-from s2s_client.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest
-from s2s_client.models.responses import (
+from vocero_api.api.responses import validated_json_response
+from vocero_api.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest
+from vocero_api.models.responses import (
AllocationResponse,
QueueCancellationResponse,
ReasoningResponse,
@@ -58,7 +58,7 @@ def test_request_models_keep_legacy_extra_field_and_argument_behavior():
def test_http_routes_publish_response_schemas():
# Import the app only after the compatibility suite's isolated configuration.
- from s2s_client.main import app
+ from vocero_api.main import app
spec = app.openapi()
for path, operations in spec["paths"].items():
@@ -73,6 +73,13 @@ def test_http_routes_publish_response_schemas():
assert config["properties"]["reasoningType"]["enum"] == ["PROXY", "OPENAI"]
+def test_legacy_entry_point_exports_the_renamed_application():
+ import server
+ from vocero_api.main import app
+
+ assert server.app is app
+
+
@pytest.mark.parametrize(
("value", "expected"),
[
@@ -85,6 +92,6 @@ def test_http_routes_publish_response_schemas():
],
)
def test_reasoning_configuration_preserves_normalization_and_native_fallback(value, expected):
- from s2s_client.core.config import _normalize_reasoning_type
+ from vocero_api.core.config import _normalize_reasoning_type
assert _normalize_reasoning_type(value) == expected
diff --git a/s2s-client/tests/test_server.py b/vocero-api/tests/test_server.py
similarity index 98%
rename from s2s-client/tests/test_server.py
rename to vocero-api/tests/test_server.py
index 3c9fe3b..4fe0a24 100644
--- a/s2s-client/tests/test_server.py
+++ b/vocero-api/tests/test_server.py
@@ -1,6 +1,6 @@
"""Backend contract checks with local HTTP fixtures and a loopback voice server.
-Run from s2s-client: uv run --locked pytest tests
+Run from vocero-api: uv run --locked pytest tests
"""
import json
@@ -16,10 +16,10 @@ from websockets.sync.server import serve
# Never consume deployment credentials or contact services from a developer's .env.
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
- from s2s_client import main as server
- from s2s_client.core import config as settings
- from s2s_client.providers import apipilot, caller
- from s2s_client.services import tool_context
+ from vocero_api import main as server
+ from vocero_api.core import config as settings
+ from vocero_api.providers import apipilot, caller
+ from vocero_api.services import tool_context
GRANT = {
diff --git a/s2s-client/uv.lock b/vocero-api/uv.lock
similarity index 99%
rename from s2s-client/uv.lock
rename to vocero-api/uv.lock
index 2bd99b4..5b37630 100644
--- a/s2s-client/uv.lock
+++ b/vocero-api/uv.lock
@@ -24,45 +24,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
]
-[[package]]
-name = "aquaportal-s2s-client"
-version = "0.1.0"
-source = { editable = "." }
-dependencies = [
- { name = "fastapi" },
- { name = "httpx" },
- { name = "pydantic" },
- { name = "python-dotenv" },
- { name = "uvicorn", extra = ["standard"] },
- { name = "websockets" },
-]
-
-[package.dev-dependencies]
-dev = [
- { name = "mypy" },
- { name = "pre-commit" },
- { name = "pytest" },
- { name = "ruff" },
-]
-
-[package.metadata]
-requires-dist = [
- { name = "fastapi", specifier = "==0.115.6" },
- { name = "httpx", specifier = "==0.28.1" },
- { name = "pydantic", specifier = "==2.13.5" },
- { name = "python-dotenv", specifier = "==1.2.2" },
- { name = "uvicorn", extras = ["standard"], specifier = "==0.34.0" },
- { name = "websockets", specifier = "==17.2" },
-]
-
-[package.metadata.requires-dev]
-dev = [
- { name = "mypy", specifier = ">=1.16,<2" },
- { name = "pre-commit", specifier = ">=4.4,<5" },
- { name = "pytest", specifier = ">=9,<10" },
- { name = "ruff", specifier = ">=0.12,<1" },
-]
-
[[package]]
name = "certifi"
version = "2026.7.22"
@@ -546,6 +507,45 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/df/93/084f10ebbecdedf7902ed68fbecc903f0e9c5742155bfffb05eccbc071da/virtualenv-21.14.5-py3-none-any.whl", hash = "sha256:b0651e0174982bba17cc6f0aaa2ef496730cb85a3f35eb5d1119d1c904f7d1da", size = 5487482, upload-time = "2026-10-02T17:42:23.38Z" },
]
+[[package]]
+name = "vocero-api"
+version = "0.1.0"
+source = { editable = "." }
+dependencies = [
+ { name = "fastapi" },
+ { name = "httpx" },
+ { name = "pydantic" },
+ { name = "python-dotenv" },
+ { name = "uvicorn", extra = ["standard"] },
+ { name = "websockets" },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "mypy" },
+ { name = "pre-commit" },
+ { name = "pytest" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "fastapi", specifier = "==0.115.6" },
+ { name = "httpx", specifier = "==0.28.1" },
+ { name = "pydantic", specifier = "==2.13.5" },
+ { name = "python-dotenv", specifier = "==1.2.2" },
+ { name = "uvicorn", extras = ["standard"], specifier = "==0.34.0" },
+ { name = "websockets", specifier = "==17.2" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "mypy", specifier = ">=1.16,<2" },
+ { name = "pre-commit", specifier = ">=4.4,<5" },
+ { name = "pytest", specifier = ">=9,<10" },
+ { name = "ruff", specifier = ">=0.12,<1" },
+]
+
[[package]]
name = "watchfiles"
version = "1.3.0"
diff --git a/s2s-client/s2s_client/__init__.py b/vocero-api/vocero_api/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/__init__.py
rename to vocero-api/vocero_api/__init__.py
diff --git a/s2s-client/s2s_client/api/__init__.py b/vocero-api/vocero_api/api/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/api/__init__.py
rename to vocero-api/vocero_api/api/__init__.py
diff --git a/s2s-client/s2s_client/api/responses.py b/vocero-api/vocero_api/api/responses.py
similarity index 100%
rename from s2s-client/s2s_client/api/responses.py
rename to vocero-api/vocero_api/api/responses.py
diff --git a/s2s-client/s2s_client/api/router.py b/vocero-api/vocero_api/api/router.py
similarity index 68%
rename from s2s-client/s2s_client/api/router.py
rename to vocero-api/vocero_api/api/router.py
index 20d4881..e1f1157 100644
--- a/s2s-client/s2s_client/api/router.py
+++ b/vocero-api/vocero_api/api/router.py
@@ -2,10 +2,10 @@
from fastapi import APIRouter
-from s2s_client.api.routes import apipilot, caller, config, search, sessions
-from s2s_client.models.responses import ReasoningResponse
-from s2s_client.realtime.proxy import realtime_proxy
-from s2s_client.services.reasoning import reasoning
+from vocero_api.api.routes import apipilot, caller, config, search, sessions
+from vocero_api.models.responses import ReasoningResponse
+from vocero_api.realtime.proxy import realtime_proxy
+from vocero_api.services.reasoning import reasoning
router = APIRouter()
router.include_router(config.router)
diff --git a/s2s-client/s2s_client/api/routes/__init__.py b/vocero-api/vocero_api/api/routes/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/api/routes/__init__.py
rename to vocero-api/vocero_api/api/routes/__init__.py
diff --git a/s2s-client/s2s_client/api/routes/apipilot.py b/vocero-api/vocero_api/api/routes/apipilot.py
similarity index 94%
rename from s2s-client/s2s_client/api/routes/apipilot.py
rename to vocero-api/vocero_api/api/routes/apipilot.py
index e704da1..a918006 100644
--- a/s2s-client/s2s_client/api/routes/apipilot.py
+++ b/vocero-api/vocero_api/api/routes/apipilot.py
@@ -3,12 +3,12 @@ from __future__ import annotations
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.models import requests as models_requests
-from s2s_client.models.responses import ToolCatalogResponse, ToolExecutionResponse
-from s2s_client.providers import apipilot as providers_apipilot
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.models import requests as models_requests
+from vocero_api.models.responses import ToolCatalogResponse, ToolExecutionResponse
+from vocero_api.providers import apipilot as providers_apipilot
router = APIRouter()
diff --git a/s2s-client/s2s_client/api/routes/caller.py b/vocero-api/vocero_api/api/routes/caller.py
similarity index 97%
rename from s2s-client/s2s_client/api/routes/caller.py
rename to vocero-api/vocero_api/api/routes/caller.py
index 0f96047..a8cbc0f 100644
--- a/s2s-client/s2s_client/api/routes/caller.py
+++ b/vocero-api/vocero_api/api/routes/caller.py
@@ -6,12 +6,12 @@ import httpx
from fastapi import APIRouter, HTTPException
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.models.responses import ReportsResponse, UserProfileResponse
-from s2s_client.providers import caller as providers_caller
-from s2s_client.services import context as services_context
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.models.responses import ReportsResponse, UserProfileResponse
+from vocero_api.providers import caller as providers_caller
+from vocero_api.services import context as services_context
router = APIRouter()
diff --git a/s2s-client/s2s_client/api/routes/config.py b/vocero-api/vocero_api/api/routes/config.py
similarity index 88%
rename from s2s-client/s2s_client/api/routes/config.py
rename to vocero-api/vocero_api/api/routes/config.py
index 576fb56..57d973e 100644
--- a/s2s-client/s2s_client/api/routes/config.py
+++ b/vocero-api/vocero_api/api/routes/config.py
@@ -3,11 +3,11 @@ from __future__ import annotations
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.core import urls as core_urls
-from s2s_client.models.responses import ConfigResponse
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.core import urls as core_urls
+from vocero_api.models.responses import ConfigResponse
router = APIRouter()
diff --git a/s2s-client/s2s_client/api/routes/search.py b/vocero-api/vocero_api/api/routes/search.py
similarity index 92%
rename from s2s-client/s2s_client/api/routes/search.py
rename to vocero-api/vocero_api/api/routes/search.py
index bb7ae6e..5097b42 100644
--- a/s2s-client/s2s_client/api/routes/search.py
+++ b/vocero-api/vocero_api/api/routes/search.py
@@ -6,11 +6,11 @@ import httpx
from fastapi import APIRouter, HTTPException
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.models import requests as models_requests
-from s2s_client.models.responses import SearchResponse
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.models import requests as models_requests
+from vocero_api.models.responses import SearchResponse
router = APIRouter()
diff --git a/s2s-client/s2s_client/api/routes/sessions.py b/vocero-api/vocero_api/api/routes/sessions.py
similarity index 96%
rename from s2s-client/s2s_client/api/routes/sessions.py
rename to vocero-api/vocero_api/api/routes/sessions.py
index a401359..e4f0a9c 100644
--- a/s2s-client/s2s_client/api/routes/sessions.py
+++ b/vocero-api/vocero_api/api/routes/sessions.py
@@ -6,11 +6,11 @@ import httpx
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.models.requests import QueueEndPayload
-from s2s_client.models.responses import AllocationResponse, QueueCancellationResponse
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.models.requests import QueueEndPayload
+from vocero_api.models.responses import AllocationResponse, QueueCancellationResponse
router = APIRouter()
diff --git a/s2s-client/s2s_client/core/__init__.py b/vocero-api/vocero_api/core/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/core/__init__.py
rename to vocero-api/vocero_api/core/__init__.py
diff --git a/s2s-client/s2s_client/core/config.py b/vocero-api/vocero_api/core/config.py
similarity index 99%
rename from s2s-client/s2s_client/core/config.py
rename to vocero-api/vocero_api/core/config.py
index 26e2e64..3f13be6 100644
--- a/s2s-client/s2s_client/core/config.py
+++ b/vocero-api/vocero_api/core/config.py
@@ -4,7 +4,7 @@ import os
from dotenv import load_dotenv
-from s2s_client.core import logging as app_logging
+from vocero_api.core import logging as app_logging
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
load_dotenv(os.path.join(ROOT_DIR, ".env"), override=False)
diff --git a/s2s-client/s2s_client/core/diagnostics.py b/vocero-api/vocero_api/core/diagnostics.py
similarity index 96%
rename from s2s-client/s2s_client/core/diagnostics.py
rename to vocero-api/vocero_api/core/diagnostics.py
index 846650f..042c89e 100644
--- a/s2s-client/s2s_client/core/diagnostics.py
+++ b/vocero-api/vocero_api/core/diagnostics.py
@@ -1,7 +1,7 @@
from __future__ import annotations
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
# Resolve the execution URL for the startup banner using the same normalization
diff --git a/s2s-client/s2s_client/core/logging.py b/vocero-api/vocero_api/core/logging.py
similarity index 100%
rename from s2s-client/s2s_client/core/logging.py
rename to vocero-api/vocero_api/core/logging.py
diff --git a/s2s-client/s2s_client/core/urls.py b/vocero-api/vocero_api/core/urls.py
similarity index 97%
rename from s2s-client/s2s_client/core/urls.py
rename to vocero-api/vocero_api/core/urls.py
index 9cff9ec..74bbee3 100644
--- a/s2s-client/s2s_client/core/urls.py
+++ b/vocero-api/vocero_api/core/urls.py
@@ -4,7 +4,7 @@ from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from fastapi import Request, WebSocket
-from s2s_client.core import config as settings
+from vocero_api.core import config as settings
def _url_with_query(url: str, **params: str) -> str:
diff --git a/s2s-client/s2s_client/main.py b/vocero-api/vocero_api/main.py
similarity index 88%
rename from s2s-client/s2s_client/main.py
rename to vocero-api/vocero_api/main.py
index 3ea5866..25bfc9b 100644
--- a/s2s-client/s2s_client/main.py
+++ b/vocero-api/vocero_api/main.py
@@ -6,8 +6,8 @@ from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
-from s2s_client.api.router import router
-from s2s_client.core import config, diagnostics # noqa: F401 - retain the startup banner
+from vocero_api.api.router import router
+from vocero_api.core import config, diagnostics # noqa: F401 - retain the startup banner
app = FastAPI(title="s2s-demo")
app.include_router(router)
diff --git a/s2s-client/s2s_client/models/__init__.py b/vocero-api/vocero_api/models/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/models/__init__.py
rename to vocero-api/vocero_api/models/__init__.py
diff --git a/s2s-client/s2s_client/models/requests.py b/vocero-api/vocero_api/models/requests.py
similarity index 100%
rename from s2s-client/s2s_client/models/requests.py
rename to vocero-api/vocero_api/models/requests.py
diff --git a/s2s-client/s2s_client/models/responses.py b/vocero-api/vocero_api/models/responses.py
similarity index 100%
rename from s2s-client/s2s_client/models/responses.py
rename to vocero-api/vocero_api/models/responses.py
diff --git a/s2s-client/s2s_client/providers/__init__.py b/vocero-api/vocero_api/providers/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/providers/__init__.py
rename to vocero-api/vocero_api/providers/__init__.py
diff --git a/s2s-client/s2s_client/providers/apipilot.py b/vocero-api/vocero_api/providers/apipilot.py
similarity index 99%
rename from s2s-client/s2s_client/providers/apipilot.py
rename to vocero-api/vocero_api/providers/apipilot.py
index 50e1494..16c801f 100644
--- a/s2s-client/s2s_client/providers/apipilot.py
+++ b/vocero-api/vocero_api/providers/apipilot.py
@@ -9,8 +9,8 @@ from typing import Any, cast
import httpx
from fastapi import HTTPException, Request
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
# In-process cache and asynchronous lock: one caller refreshes while others
# read the stale entry. `_APIPILOT_CACHE[auth_fingerprint] = (fetched_at_monotonic, tools_list)`.
diff --git a/s2s-client/s2s_client/providers/caller.py b/vocero-api/vocero_api/providers/caller.py
similarity index 99%
rename from s2s-client/s2s_client/providers/caller.py
rename to vocero-api/vocero_api/providers/caller.py
index 453abd4..b3bc689 100644
--- a/s2s-client/s2s_client/providers/caller.py
+++ b/vocero-api/vocero_api/providers/caller.py
@@ -6,8 +6,8 @@ from urllib.parse import urlencode
import httpx
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
async def _fetch_reports_list(phone_number: str | None = None, auth_token: str | None = None) -> list | dict | None:
diff --git a/s2s-client/s2s_client/realtime/__init__.py b/vocero-api/vocero_api/realtime/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/realtime/__init__.py
rename to vocero-api/vocero_api/realtime/__init__.py
diff --git a/s2s-client/s2s_client/realtime/events.py b/vocero-api/vocero_api/realtime/events.py
similarity index 99%
rename from s2s-client/s2s_client/realtime/events.py
rename to vocero-api/vocero_api/realtime/events.py
index 2da301b..e4a57ff 100644
--- a/s2s-client/s2s_client/realtime/events.py
+++ b/vocero-api/vocero_api/realtime/events.py
@@ -4,9 +4,9 @@ import json
import re
from typing import Any, cast
-from s2s_client.core import logging as app_logging
-from s2s_client.services import context as services_context
-from s2s_client.services import tool_context as services_tool_context
+from vocero_api.core import logging as app_logging
+from vocero_api.services import context as services_context
+from vocero_api.services import tool_context as services_tool_context
async def _report_server_debug(hypothesis_id: str, location: str, msg: str, data: dict | None = None) -> None:
diff --git a/s2s-client/s2s_client/realtime/proxy.py b/vocero-api/vocero_api/realtime/proxy.py
similarity index 99%
rename from s2s-client/s2s_client/realtime/proxy.py
rename to vocero-api/vocero_api/realtime/proxy.py
index 05afea7..2fdeaed 100644
--- a/s2s-client/s2s_client/realtime/proxy.py
+++ b/vocero-api/vocero_api/realtime/proxy.py
@@ -10,14 +10,14 @@ from fastapi import WebSocket, WebSocketDisconnect
from websockets.asyncio.client import ClientConnection
from websockets.typing import Subprotocol
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.core import urls as core_urls
-from s2s_client.providers import apipilot as providers_apipilot
-from s2s_client.providers import caller as providers_caller
-from s2s_client.realtime import events as realtime_events
-from s2s_client.services import context as services_context
-from s2s_client.services import tool_context as services_tool_context
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.core import urls as core_urls
+from vocero_api.providers import apipilot as providers_apipilot
+from vocero_api.providers import caller as providers_caller
+from vocero_api.realtime import events as realtime_events
+from vocero_api.services import context as services_context
+from vocero_api.services import tool_context as services_tool_context
async def realtime_proxy(ws: WebSocket) -> None:
diff --git a/s2s-client/s2s_client/services/__init__.py b/vocero-api/vocero_api/services/__init__.py
similarity index 100%
rename from s2s-client/s2s_client/services/__init__.py
rename to vocero-api/vocero_api/services/__init__.py
diff --git a/s2s-client/s2s_client/services/context.py b/vocero-api/vocero_api/services/context.py
similarity index 99%
rename from s2s-client/s2s_client/services/context.py
rename to vocero-api/vocero_api/services/context.py
index 853392b..3bb603f 100644
--- a/s2s-client/s2s_client/services/context.py
+++ b/vocero-api/vocero_api/services/context.py
@@ -4,9 +4,9 @@ import datetime as _dt_module
import json
from typing import Any, cast
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.services import tool_context as services_tool_context
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.services import tool_context as services_tool_context
VOCERO_USER_CONTEXT_HINT = " Caller profile resolved from the page phoneNumber: "
REPORTS_LIST_HINT = (
diff --git a/s2s-client/s2s_client/services/reasoning.py b/vocero-api/vocero_api/services/reasoning.py
similarity index 98%
rename from s2s-client/s2s_client/services/reasoning.py
rename to vocero-api/vocero_api/services/reasoning.py
index 288d63b..43b67c2 100644
--- a/s2s-client/s2s_client/services/reasoning.py
+++ b/vocero-api/vocero_api/services/reasoning.py
@@ -6,15 +6,15 @@ import httpx
from fastapi import HTTPException
from fastapi.responses import JSONResponse
-from s2s_client.api.responses import validated_json_response
-from s2s_client.core import config as settings
-from s2s_client.core import logging as app_logging
-from s2s_client.models import requests as models_requests
-from s2s_client.models.responses import ReasoningResponse
-from s2s_client.providers import apipilot as providers_apipilot
-from s2s_client.providers import caller as providers_caller
-from s2s_client.services import context as services_context
-from s2s_client.services import tool_context as services_tool_context
+from vocero_api.api.responses import validated_json_response
+from vocero_api.core import config as settings
+from vocero_api.core import logging as app_logging
+from vocero_api.models import requests as models_requests
+from vocero_api.models.responses import ReasoningResponse
+from vocero_api.providers import apipilot as providers_apipilot
+from vocero_api.providers import caller as providers_caller
+from vocero_api.services import context as services_context
+from vocero_api.services import tool_context as services_tool_context
def _openai_headers() -> dict[str, str]:
diff --git a/s2s-client/s2s_client/services/tool_context.py b/vocero-api/vocero_api/services/tool_context.py
similarity index 99%
rename from s2s-client/s2s_client/services/tool_context.py
rename to vocero-api/vocero_api/services/tool_context.py
index 2b0fb79..3011332 100644
--- a/s2s-client/s2s_client/services/tool_context.py
+++ b/vocero-api/vocero_api/services/tool_context.py
@@ -4,7 +4,7 @@ import json
import re
from typing import cast
-from s2s_client.core import logging as app_logging
+from vocero_api.core import logging as app_logging
# ---------------------------------------------------------------------------
# APIPILOT — Reasoning engine integration (two passes)
diff --git a/s2s-frontend/.editorconfig b/vocero-frontend/.editorconfig
similarity index 100%
rename from s2s-frontend/.editorconfig
rename to vocero-frontend/.editorconfig
diff --git a/s2s-frontend/.env.example b/vocero-frontend/.env.example
similarity index 100%
rename from s2s-frontend/.env.example
rename to vocero-frontend/.env.example
diff --git a/s2s-frontend/.gitignore b/vocero-frontend/.gitignore
similarity index 100%
rename from s2s-frontend/.gitignore
rename to vocero-frontend/.gitignore
diff --git a/s2s-frontend/.prettierignore b/vocero-frontend/.prettierignore
similarity index 100%
rename from s2s-frontend/.prettierignore
rename to vocero-frontend/.prettierignore
diff --git a/s2s-frontend/README.md b/vocero-frontend/README.md
similarity index 94%
rename from s2s-frontend/README.md
rename to vocero-frontend/README.md
index 03bafef..4ad7d9a 100644
--- a/s2s-frontend/README.md
+++ b/vocero-frontend/README.md
@@ -1,7 +1,7 @@
# Vocero React frontend
React 19, TypeScript, Tailwind CSS 4, and Vite 8 client for the existing
-`s2s-client` FastAPI backend. This is a client-side application with React-owned
+`vocero-api` FastAPI backend. This is a client-side application with React-owned
rendering and state, including the realtime WebSocket and audio integration.
## Run during development
@@ -10,9 +10,9 @@ From the repository root, run `./start_ui.sh`. Open `http://localhost:8420`.
Add `?debug=true` to access settings, tools, and custom RAG context. Existing
`phoneNumber` and `episode_id` query parameters are supported.
-The launcher requires Python 3 with venv support, Node.js 22.18 or later on the
-22.x line or Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`.
-It creates `.venv` and installs missing Python dependencies. It runs
+The launcher requires uv 0.12.5, Node.js 22.18 or later on the 22.x line or
+Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`.
+It provisions Python 3.12 and syncs `vocero-api/.venv` from the API lockfile. It runs
`npm ci --include=dev` on first start and when either frontend package manifest
changes. Subsequent restarts reuse the installed dependencies.
@@ -26,7 +26,7 @@ changes. Subsequent restarts reuse the installed dependencies.
must differ. Occupied ports fail startup rather than silently selecting a new
endpoint. The launcher waits for FastAPI before starting Vite, stops both
process groups on Ctrl+C/SIGTERM, and exits with a failure if either server exits.
-Uvicorn watches only `s2s-client`, avoiding frontend and dependency directories.
+Uvicorn watches only `vocero-api`, avoiding frontend and dependency directories.
The Vite child explicitly uses `NODE_ENV=development`, including when a process
manager sets `NODE_ENV=production`, so React Fast Refresh remains enabled.
@@ -49,8 +49,8 @@ variables, which are exposed to browser code.
## Staging with hot reload
PM2 can continue running the root `start_ui.sh` as its existing `s2s_ui` process.
-Ensure the Node/npm versions above are on PM2's PATH and deploy `s2s-frontend`
-alongside `s2s-client` and `docs`. The reverse proxy still targets port `8420`.
+Ensure uv and the Node/npm versions above are on PM2's PATH and deploy `vocero-frontend`
+alongside `vocero-api` and `docs`. The reverse proxy still targets port `8420`.
Set the exact public hostname in the root `.env`, without scheme or port:
@@ -74,7 +74,7 @@ Start FastAPI on `127.0.0.1:8421` as described in the
[client usage guide](../docs/client/usage.md), then run:
```sh
-cd s2s-frontend
+cd vocero-frontend
npm ci
npm run dev
```
diff --git a/s2s-frontend/config/backend-proxy.ts b/vocero-frontend/config/backend-proxy.ts
similarity index 100%
rename from s2s-frontend/config/backend-proxy.ts
rename to vocero-frontend/config/backend-proxy.ts
diff --git a/s2s-frontend/config/launcher.integration.ts b/vocero-frontend/config/launcher.integration.ts
similarity index 89%
rename from s2s-frontend/config/launcher.integration.ts
rename to vocero-frontend/config/launcher.integration.ts
index 314e1b1..972d9ef 100644
--- a/s2s-frontend/config/launcher.integration.ts
+++ b/vocero-frontend/config/launcher.integration.ts
@@ -64,21 +64,21 @@ async function waitForFile(filename: string) {
async function launcher(context: TestContext, env: Record = {}) {
const root = await mkdtemp(join(tmpdir(), "s2s-launcher-"));
- const frontend = join(root, "s2s-frontend");
+ const frontend = join(root, "vocero-frontend");
const modules = join(frontend, "node_modules");
- await mkdir(join(root, "s2s-client/.venv/bin"), { recursive: true });
+ await mkdir(join(root, "vocero-api/.venv/bin"), { recursive: true });
await mkdir(join(root, "bin"));
await mkdir(join(modules, "vite/bin"), { recursive: true });
await copyFile(new URL("../../start_ui.sh", import.meta.url), join(root, "start_ui.sh"));
- await writeFile(join(root, "s2s-client/pyproject.toml"), "");
- await writeFile(join(root, "s2s-client/uv.lock"), "");
+ await writeFile(join(root, "vocero-api/pyproject.toml"), "");
+ await writeFile(join(root, "vocero-api/uv.lock"), "");
await writeFile(
join(root, "bin/uv"),
'#!/bin/bash\nprintf "%s\\n" "$@" > "$S2S_TEST_ROOT/uv-args"\nprintf "%s" "$UV_PROJECT_ENVIRONMENT" > "$S2S_TEST_ROOT/uv-environment"\nexit "${S2S_TEST_UV_EXIT:-0}"\n',
{ mode: 0o755 },
);
await writeFile(
- join(root, "s2s-client/.venv/bin/python"),
+ join(root, "vocero-api/.venv/bin/python"),
'#!/bin/bash\nif [ "$1" = "-c" ]; then exit 0; fi\nexec node "$S2S_TEST_ROOT/child.mjs" backend "$@"\n',
{ mode: 0o755 },
);
@@ -146,9 +146,30 @@ for (const signal of ["SIGINT", "SIGTERM"] as const) {
assert.equal(run.output().includes("Installing"), false);
assert.equal(
await readFile(join(run.root, "uv-args"), "utf8"),
- `sync\n--project\n${run.root}/s2s-client\n--locked\n`,
+ `sync\n--project\n${run.root}/vocero-api\n--locked\n`,
);
- assert.equal(await readFile(join(run.root, "uv-environment"), "utf8"), `${run.root}/s2s-client/.venv`);
+ assert.equal(await readFile(join(run.root, "uv-environment"), "utf8"), `${run.root}/vocero-api/.venv`);
+ const backendMetadata: unknown = JSON.parse(await readFile(join(run.root, "backend.json"), "utf8"));
+ assert.deepEqual(backendMetadata, {
+ args: [
+ "backend",
+ "-m",
+ "uvicorn",
+ "vocero_api.main:app",
+ "--app-dir",
+ `${run.root}/vocero-api`,
+ "--host",
+ "127.0.0.1",
+ "--port",
+ String(run.backendPort),
+ "--reload",
+ "--reload-dir",
+ `${run.root}/vocero-api`,
+ ],
+ target: `http://127.0.0.1:${run.backendPort}`,
+ hosts: "s2s.test",
+ nodeEnv: "production",
+ });
run.child.kill(signal);
assert.equal(await run.exited, signal === "SIGINT" ? 130 : 143);
for (const role of ["backend", "frontend"]) {
diff --git a/s2s-frontend/eslint.config.ts b/vocero-frontend/eslint.config.ts
similarity index 100%
rename from s2s-frontend/eslint.config.ts
rename to vocero-frontend/eslint.config.ts
diff --git a/s2s-frontend/index.html b/vocero-frontend/index.html
similarity index 100%
rename from s2s-frontend/index.html
rename to vocero-frontend/index.html
diff --git a/s2s-frontend/package-lock.json b/vocero-frontend/package-lock.json
similarity index 99%
rename from s2s-frontend/package-lock.json
rename to vocero-frontend/package-lock.json
index e6de408..cb07230 100644
--- a/s2s-frontend/package-lock.json
+++ b/vocero-frontend/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "s2s-frontend",
+ "name": "vocero-frontend",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "s2s-frontend",
+ "name": "vocero-frontend",
"version": "0.0.0",
"dependencies": {
"dompurify": "^3.4.16",
diff --git a/s2s-frontend/package.json b/vocero-frontend/package.json
similarity index 98%
rename from s2s-frontend/package.json
rename to vocero-frontend/package.json
index 795f737..eee75c0 100644
--- a/s2s-frontend/package.json
+++ b/vocero-frontend/package.json
@@ -1,5 +1,5 @@
{
- "name": "s2s-frontend",
+ "name": "vocero-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
diff --git a/s2s-frontend/prettier.config.ts b/vocero-frontend/prettier.config.ts
similarity index 100%
rename from s2s-frontend/prettier.config.ts
rename to vocero-frontend/prettier.config.ts
diff --git a/s2s-frontend/src/App.test.tsx b/vocero-frontend/src/App.test.tsx
similarity index 100%
rename from s2s-frontend/src/App.test.tsx
rename to vocero-frontend/src/App.test.tsx
diff --git a/s2s-frontend/src/App.tsx b/vocero-frontend/src/App.tsx
similarity index 100%
rename from s2s-frontend/src/App.tsx
rename to vocero-frontend/src/App.tsx
diff --git a/s2s-frontend/src/audio/audio-playback.worklet.ts b/vocero-frontend/src/audio/audio-playback.worklet.ts
similarity index 100%
rename from s2s-frontend/src/audio/audio-playback.worklet.ts
rename to vocero-frontend/src/audio/audio-playback.worklet.ts
diff --git a/s2s-frontend/src/audio/codec.ts b/vocero-frontend/src/audio/codec.ts
similarity index 100%
rename from s2s-frontend/src/audio/codec.ts
rename to vocero-frontend/src/audio/codec.ts
diff --git a/s2s-frontend/src/audio/engine.ts b/vocero-frontend/src/audio/engine.ts
similarity index 100%
rename from s2s-frontend/src/audio/engine.ts
rename to vocero-frontend/src/audio/engine.ts
diff --git a/s2s-frontend/src/audio/mic-capture.worklet.ts b/vocero-frontend/src/audio/mic-capture.worklet.ts
similarity index 100%
rename from s2s-frontend/src/audio/mic-capture.worklet.ts
rename to vocero-frontend/src/audio/mic-capture.worklet.ts
diff --git a/s2s-frontend/src/audio/worklet-globals.d.ts b/vocero-frontend/src/audio/worklet-globals.d.ts
similarity index 100%
rename from s2s-frontend/src/audio/worklet-globals.d.ts
rename to vocero-frontend/src/audio/worklet-globals.d.ts
diff --git a/s2s-frontend/src/components/Bubbles.tsx b/vocero-frontend/src/components/Bubbles.tsx
similarity index 100%
rename from s2s-frontend/src/components/Bubbles.tsx
rename to vocero-frontend/src/components/Bubbles.tsx
diff --git a/s2s-frontend/src/components/Conversation.tsx b/vocero-frontend/src/components/Conversation.tsx
similarity index 100%
rename from s2s-frontend/src/components/Conversation.tsx
rename to vocero-frontend/src/components/Conversation.tsx
diff --git a/s2s-frontend/src/components/Dialog.tsx b/vocero-frontend/src/components/Dialog.tsx
similarity index 100%
rename from s2s-frontend/src/components/Dialog.tsx
rename to vocero-frontend/src/components/Dialog.tsx
diff --git a/s2s-frontend/src/components/SettingsDialogs.tsx b/vocero-frontend/src/components/SettingsDialogs.tsx
similarity index 100%
rename from s2s-frontend/src/components/SettingsDialogs.tsx
rename to vocero-frontend/src/components/SettingsDialogs.tsx
diff --git a/s2s-frontend/src/components/VoiceStage.tsx b/vocero-frontend/src/components/VoiceStage.tsx
similarity index 100%
rename from s2s-frontend/src/components/VoiceStage.tsx
rename to vocero-frontend/src/components/VoiceStage.tsx
diff --git a/s2s-frontend/src/hooks/use-wake-word.ts b/vocero-frontend/src/hooks/use-wake-word.ts
similarity index 100%
rename from s2s-frontend/src/hooks/use-wake-word.ts
rename to vocero-frontend/src/hooks/use-wake-word.ts
diff --git a/s2s-frontend/src/lib/api.ts b/vocero-frontend/src/lib/api.ts
similarity index 100%
rename from s2s-frontend/src/lib/api.ts
rename to vocero-frontend/src/lib/api.ts
diff --git a/s2s-frontend/src/lib/context.ts b/vocero-frontend/src/lib/context.ts
similarity index 100%
rename from s2s-frontend/src/lib/context.ts
rename to vocero-frontend/src/lib/context.ts
diff --git a/s2s-frontend/src/lib/contracts.ts b/vocero-frontend/src/lib/contracts.ts
similarity index 100%
rename from s2s-frontend/src/lib/contracts.ts
rename to vocero-frontend/src/lib/contracts.ts
diff --git a/s2s-frontend/src/lib/messages.test.ts b/vocero-frontend/src/lib/messages.test.ts
similarity index 100%
rename from s2s-frontend/src/lib/messages.test.ts
rename to vocero-frontend/src/lib/messages.test.ts
diff --git a/s2s-frontend/src/lib/messages.ts b/vocero-frontend/src/lib/messages.ts
similarity index 100%
rename from s2s-frontend/src/lib/messages.ts
rename to vocero-frontend/src/lib/messages.ts
diff --git a/s2s-frontend/src/lib/profile.ts b/vocero-frontend/src/lib/profile.ts
similarity index 100%
rename from s2s-frontend/src/lib/profile.ts
rename to vocero-frontend/src/lib/profile.ts
diff --git a/s2s-frontend/src/lib/reports.ts b/vocero-frontend/src/lib/reports.ts
similarity index 100%
rename from s2s-frontend/src/lib/reports.ts
rename to vocero-frontend/src/lib/reports.ts
diff --git a/s2s-frontend/src/lib/settings.ts b/vocero-frontend/src/lib/settings.ts
similarity index 100%
rename from s2s-frontend/src/lib/settings.ts
rename to vocero-frontend/src/lib/settings.ts
diff --git a/s2s-frontend/src/main.tsx b/vocero-frontend/src/main.tsx
similarity index 100%
rename from s2s-frontend/src/main.tsx
rename to vocero-frontend/src/main.tsx
diff --git a/s2s-frontend/src/realtime/client.test.ts b/vocero-frontend/src/realtime/client.test.ts
similarity index 100%
rename from s2s-frontend/src/realtime/client.test.ts
rename to vocero-frontend/src/realtime/client.test.ts
diff --git a/s2s-frontend/src/realtime/client.ts b/vocero-frontend/src/realtime/client.ts
similarity index 100%
rename from s2s-frontend/src/realtime/client.ts
rename to vocero-frontend/src/realtime/client.ts
diff --git a/s2s-frontend/src/realtime/controller.test.ts b/vocero-frontend/src/realtime/controller.test.ts
similarity index 100%
rename from s2s-frontend/src/realtime/controller.test.ts
rename to vocero-frontend/src/realtime/controller.test.ts
diff --git a/s2s-frontend/src/realtime/controller.ts b/vocero-frontend/src/realtime/controller.ts
similarity index 100%
rename from s2s-frontend/src/realtime/controller.ts
rename to vocero-frontend/src/realtime/controller.ts
diff --git a/s2s-frontend/src/styles.css b/vocero-frontend/src/styles.css
similarity index 100%
rename from s2s-frontend/src/styles.css
rename to vocero-frontend/src/styles.css
diff --git a/s2s-frontend/src/styles/legacy-theme.css b/vocero-frontend/src/styles/legacy-theme.css
similarity index 100%
rename from s2s-frontend/src/styles/legacy-theme.css
rename to vocero-frontend/src/styles/legacy-theme.css
diff --git a/s2s-frontend/src/test/backend-proxy.test.ts b/vocero-frontend/src/test/backend-proxy.test.ts
similarity index 100%
rename from s2s-frontend/src/test/backend-proxy.test.ts
rename to vocero-frontend/src/test/backend-proxy.test.ts
diff --git a/s2s-frontend/src/test/setup.ts b/vocero-frontend/src/test/setup.ts
similarity index 100%
rename from s2s-frontend/src/test/setup.ts
rename to vocero-frontend/src/test/setup.ts
diff --git a/s2s-frontend/tsconfig.app.json b/vocero-frontend/tsconfig.app.json
similarity index 100%
rename from s2s-frontend/tsconfig.app.json
rename to vocero-frontend/tsconfig.app.json
diff --git a/s2s-frontend/tsconfig.json b/vocero-frontend/tsconfig.json
similarity index 100%
rename from s2s-frontend/tsconfig.json
rename to vocero-frontend/tsconfig.json
diff --git a/s2s-frontend/tsconfig.node.json b/vocero-frontend/tsconfig.node.json
similarity index 100%
rename from s2s-frontend/tsconfig.node.json
rename to vocero-frontend/tsconfig.node.json
diff --git a/s2s-frontend/vite.config.ts b/vocero-frontend/vite.config.ts
similarity index 100%
rename from s2s-frontend/vite.config.ts
rename to vocero-frontend/vite.config.ts
diff --git a/s2s-frontend/vitest.config.ts b/vocero-frontend/vitest.config.ts
similarity index 100%
rename from s2s-frontend/vitest.config.ts
rename to vocero-frontend/vitest.config.ts