Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
8.3 KiB
EvOo Voice — Interaction Model
Gesamterfahrung (encounter)
A conversation with an Oo is a shared encounter, not a thread with a tiny avatar:
| Layer | Role |
|---|---|
| Oo Stage | Primary — large BotAvatar / persona presence, runtime pose, awareness strip |
| Transcript | Secondary protocol — compact caption trail, scrollable record |
| Sound | Calm procedural cues wired to real events (work finished, voice phases, handoffs) |
Text mode (OoStage in Shell) and Voice mode (VoiceStage overlay) share the same metaphor: eyes/presence + space + sound. Brand lock (asymmetric Kulleraugen) is unchanged.
Product model — conversation-centric (Grok-like)
Oos (agents) exist, but the product is organized around conversations (Unterhaltungen), not “pick an agent row = the whole app”:
| Layer | What it is |
|---|---|
| Team | Roster of your Oos — who you can talk to |
| 1:1 Unterhaltung | One Oo, one shared encounter (OoStage) |
| Gruppenunterhaltung | Multiple Oos (+ humans later) in one encounter (OoGroupStage) |
| Projekte | Group conversations organized in Ordner (BotSection folders in sidebar) |
| Mode | Shell route | Stage |
|---|---|---|
| Unterhaltung (1:1) | /app/:botId |
OoStage — one Oo hero |
| Gruppenunterhaltung | /app/g/:groupId?s=live |
OoGroupStage — active-speaker hero + ensemble row; Riffing when peers spark (docs/GROUP-CONVERSATIONS.md) |
| Project sessions | /app/g/:groupId (timeline) |
No stage — session list (ProjectSessionTimeline) |
Clicking an Oo in Team opens a 1:1 conversation, not “the agent product”. Clicking a project opens a group conversation or its session timeline. Gesamterfahrung (stage + sound + transcript record) applies to both conversation types.
Emergent ensemble: Das Ganze ist mehr als die Summe seiner Teile — power from distinct specialists + chemistry in groups, not one mega-agent (docs/PRODUCT.md).
Voice is the immersive primary mode for 1:1 bot conversations. Text chat uses the same stage-first layout; the transcript stays readable but no longer carries identity chrome.
Entry
| Surface | Action |
|---|---|
| Composer Voice pill | Opens Voice Stage when a provider is connected; otherwise opens Voice settings |
| Transcript “Speak this reply” | Reads one message aloud (text-first affordance, unchanged) |
The composer Voice control uses h-control-lg, cyan bloom when ready, and amber styling when setup is needed.
Voice Stage
Full-bleed immersive layer (VoiceStage + VoiceCanvas) replaces the modal call dialog.
Persona & consciousness layer
Persona is derived at runtime from existing bot fields (name, title, description) — aligned with templates/* archetypes, not a separate marketing field.
| System | Path |
|---|---|
| Archetype detection | apps/web/src/lib/bot-persona.ts |
| i18n status copy | apps/web/src/lib/voice-persona-copy.ts |
| Awareness strip | VoiceAwarenessStrip — subagent, handoff, delegation, peer reply |
| Visual identity | BotAvatar — persona shape + unified EvooMascot soul (eyes/mouth/hands) driven by voice phase |
| Voice delivery | speaker.speak(..., { botId }) → server resolves per-bot voiceId |
Archetypes include: CTO executive (Steve Jobs template), chief operator (Fixer), builder (Dr. Eggbot), curator (Webby), work/life inboxes, home steward, quant, engineer, specialist (narrow fallback — no Wunderwuzzi). See docs/PRODUCT.md and docs/PERSONA-SELECTION.md.
Companion tone: geek-peer (fachsimpeln when wanted) or quiet-doer (execute without condescension) — inferred per user, not one voice for everyone.
Accessibility of ambition: grandma-simple entry (Thermomix to hard math — no power-user gate); expert-grade ceiling per specialty. Same product, different crews; never a mediocre everything-bot.
Phase labels, idle prompts, and thinking lines are character-specific (DE/EN via Lingui). Collaboration narration uses persona-aware phrasing (“casting”, “routing”, “handing off”) instead of generic tool labels.
States
| Phase | Visual | Audio |
|---|---|---|
| Listening | Cyan dual-mind bloom, mic-reactive energy bars | Dictation active; silence endpoint sends |
| Thinking | Violet orbital ring, calm bars | TTS idle; awaiting bot reply |
| Speaking | Emerald bloom, conductor mascot pose | TTS playback; caption shown live |
Presence visuals sync to dictation.energy (0–1) while listening. Mascot uses EvooMascot poses: idle / thinking / wave.
Controls
| Input | Action |
|---|---|
| Space | Interrupt (stop TTS or cancel dictation, resume listening) |
| Esc | Hang up — exit Voice Stage |
| T | Toggle transcript drawer (secondary record) |
Transcript
Collapsible panel shows the last messages from the thread plus the live utterance. Chat scroll behind the stage is dimmed but unchanged; voice does not replace thread history in the database.
Accessibility
prefers-reduced-motion: reducedisables bloom drift, ring spin, and composer pulse; static gradients remain.- Dialog semantics:
role="dialog",aria-modal, labelled bot name. - Hang up exposes
aria-label="Hang up". - Keyboard exit paths documented in-stage.
Architecture
Shell (composer Voice)
└─ CallView (orchestration: dictation + speaker + thread send)
└─ VoiceStage (layout, controls, transcript drawer)
└─ VoiceCanvas (ambient bloom field)
Shared singletons: dictation (apps/web/src/lib/dictation.ts), speaker (apps/web/src/lib/tts.ts).
Design tokens
Voice chrome uses text-ui-*, size-icon-*, and h-control-* / size-control-lg — no sub-13px labels on primary controls. See DESIGN.md for the full scale.
Sound language
Procedural Web Audio cues (apps/web/src/lib/oo-sound.ts) — no sample packs:
| Cue | Trigger |
|---|---|
work-complete |
Run transitions from active → finished (text mode) |
listening-start / listening-stop |
Voice phase enters/leaves listening |
speaking-start / speaking-done |
TTS playback lifecycle |
attention |
Run error surfaced in composer |
message-sent |
Successful composer send |
handoff |
Handoff block appears in thread |
group-spark |
Peer Oo reply in a group thread — playful staccato |
Toggle: Settings → Voice → Sound feedback. Respects prefers-reduced-motion: reduce and hidden tabs.
Current slice (this PR)
Stage + sound + onboarding reassurance — shared encounter without requiring the user to sit in front of the screen. Voice Stage is opt-in per session; text mode uses the same Oo Stage metaphor.
Ambient & video presence (future roadmap — do not ship without consent UX)
Ingo capability vision — document only until explicit opt-in flows exist. Not part of the current Gesamterfahrung slice.
| Idea | Product rule |
|---|---|
| Video calls optional | Only when the user wants; a simpler audio-only path stays first-class for users who prefer it |
| "It's just an Oo" | User does not need to sit in front constantly — practical help can be ambient (notifications, background runs, crew handoffs) |
| Ambient presence | An Oo can carry a lot of practical presence without demanding attention — aligned with quiet-doer mode |
| Long-term metaphors (opt-in only) | Could eventually stand in for an alarm system or a home camera — strictly user-opt-in, never default, never surveillance-as-a-feature |
Hard gates (aligned with docs/OO-RELATIONSHIP.md)
- No always-on camera or mic without a dedicated, revocable consent UX — not buried in ToS
- Never creepy — warm competence, not uncanny stalking; earned context only
- Not yes-men — push back on unsafe or over-broad monitoring requests
- No Wunderwuzzi — home/security lanes are specialist Oos with narrow scope, not one omniscient watcher
When video or ambient sensing ships: same encounter grammar (Stage + sound + transcript), plus a visible presence mode indicator the user controls.
Out of scope (later)
- Dedicated
/voiceroute - Hold-to-talk in composer
- WebGL / real audio analyser on TTS output
- Always-on video / ambient camera without explicit consent UX (see roadmap above)