Required modules
Every VCI app has these four modules. Names and language are flexible; contracts are not.
storage
Persistence layer. Owns domain state.
readState()→ current domain state (typically an array of items).- One mutation function per state-changing action (
add,remove,update,setStatus, etc.). Each returns the mutated entity ornull. - Backend:
localStorage(default), IndexedDB, or a remote API.
ui
DOM rendering. No interactive controls for domain actions.
renderState(state)— repaint UI from state.appendLog(userText, assistantText, kind)— append a conversation entry so the user sees what was heard and what happened.kind ∈ {info, success, error}.setStatus(status, label)— see Session lifecycle for statuses.setMicListening(bool)andsetMicEnabled(bool).showKeyPanel(bool)— one-time API key entry UI.
realtime
OpenAI Realtime session over WebRTC. See the wire protocol and connection recipe for details.
connect({ apiKey, onTool, getContext, onEvent })— resolves when connected; throws on failure. Mic track starts muted.disconnect()— closes peer connection, data channel, mic tracks.isConnected()→ boolean.refreshContext()— resendsession.updatewith fresh state context (call after mutations so the model always sees current state).startTurn()— unmute the mic and clear the input audio buffer. Call on Push-to-Talk press.endTurn()— mute the mic, commit the buffered audio, request a response. Call on Push-to-Talk release.interruptResponse()— cancel the model's in-progress response so the user can barge in.
app
Orchestration layer.
- Mic button wiring: click → connect / disconnect.
- Tool-call handler that dispatches to
storagemutations, callsui.renderState, and returns fresh state to the model. - API-key entry flow (single
sk-input, stored inlocalStoragefor personal use; ephemeral-token backend for anything public — see Security).