# MVP architecture ## Boundary map ```mermaid flowchart LR Human[Human voice] <--> Web[Plugin Voice settings page] Web <--> RTC[Shengwang RTC + RTM] RTC --> ASR[Managed VAD + ASR] ASR --> CAI[Conversational AI Engine] CAI -->|HTTPS + Bearer\nOpenAI SSE| Tunnel[Path-restricted tunnel] Tunnel -->|only /v1/chat/completions| Bridge[Loopback bridge plugin] Bridge -->|followup text| Agent[Plugin-owned Harness agent] Agent -->|assistant text-delta| Bridge Bridge -->|SSE text deltas| CAI CAI --> TTS[Managed TTS] TTS --> RTC Web -. stream closes on barge-in .-> Bridge Bridge -. user cancellation .-> Agent ``` There are three separate runtime responsibilities in the flow: 1. The plugin browser client is the voice frontend. It owns microphone permission, RTC audio publishing/playback, mute and stop controls. 2. Shengwang Conversational AI is the media/voice orchestration service. It owns VAD, ASR, TTS and turn interruption. 3. This Cordis plugin is the LLM adapter. It owns one Harness agent/session and converts between OpenAI SSE and Harness events. ACP is absent. ACP would be appropriate if an editor or custom client wanted to drive an agent runtime directly. Here, Shengwang already defines the upstream custom-LLM contract, so a small host plugin is the direct seam. ## Lifecycle The agent is created lazily on the first admitted request. The plugin subscribes to `agent/inbox/claimed` to correlate the exact message with its Harness turn, then forwards only `assistant/chunk` events whose type is `text-delta` and whose turn matches. Reasoning, tool calls, plans and internal events never cross the voice wire. A newer request first settles the old HTTP response as cancelled, calls `agent.cancel({ kind: 'user' })`, waits for the agent to become idle within a bounded grace period, and only then admits the replacement prompt. This ordering is the epoch fence: an event from the old turn cannot match the new active request. Reset performs the same cancellation convergence and then disposes the owned `AgentHandle`. The next turn receives a new session identity. Plugin unload stops admission, cancels/drains current activity, disposes the owned agent and closes the dedicated listener. ## Read-only guarantee for the PoC Agent creation supplies a scoped `tools.restrict({ allow: [] })`. That removes every end-capability tool from this agent's model surface even if the surrounding Harness profile exposes tools to other sessions. The bridge also ignores any non-text output. This is intentionally stricter than hiding tool events from the stream: hiding would not stop a tool from executing. ## Settings and live-session lifecycle The plugin contributes a bilingual tab through `settings.plugins.tab`. Non-secret values live in the `shengwang-voice` settings namespace. App Certificate and bridge token are stored by the Harness credential provider under configurable references; GET responses expose status only and blank password inputs preserve stored values. Starting a live call resolves both secrets on the host, validates the public Custom LLM URL, creates a Shengwang Conversational AI session with managed Deepgram STT and MiniMax TTS, and returns a one-hour channel-scoped RTC token to the browser. The browser joins that channel, publishes exactly one owned microphone track and plays subscribed remote audio. Stop, unload and partial-start failures release the local track, RTC client and plugin-owned remote agent. The configured `enabled` value is live settings. Changing it starts or stops the dedicated loopback listener without editing the profile or restarting Harness. ## Network and credential boundary - The Node listener is hard-coded to `127.0.0.1` and is separate from the Harness web server. - The App Certificate and bridge token resolve from Harness credentials or their configured environment-variable references and never appear in browser responses or Cordis config. - Chat, cancel and reset use constant-time comparison of the complete Bearer value. - Request bodies and submitted text have independent upper bounds. - Health returns no session id, transcript, provider, model, cwd or credential facts. - The public tunnel should expose only the chat path. Cancellation for real voice barge-in is carried by closing that request; the explicit control endpoints remain local. ## Deliberate MVP exclusions - No transcript/RTM panel in the bundled UI; the PoC proves two-way audio first. - No tool/function calling and no approvals. - No multi-user routing; each browser controller owns one active microphone session. - No persisted mapping between Shengwang channel IDs and Harness session IDs. - No public TLS termination, rate limiting or abuse protection. - No audio passes through Harness or this plugin. The production follow-up should add per-user authorization, quotas, auditable session ownership, short-lived Custom LLM credentials, transcript/error events, and a managed path-restricted relay.