Settings sheet — 電波 / 相手 / 局 / 本体 #14

Closed
opened 2026-07-29 17:32:07 +00:00 by aiko · 4 comments
Owner

What to build

Replaces the six upstream settings tabs with four, grouped by what you are adjusting (cards 3b, 3c, 5b, 5e, 7a). A sheet, not a page — she stays visible beside it and reacts to changes as you make them. On mobile it is a bottom sheet with a grab handle; parity is full, nothing is dropped for small screens.

  • 電波 — ASR, TTS, VAD, hands-free. しきい値 is the important control: three raw sliders with paragraphs of help become one live meter you drag two lines onto (発話 and 無音). Taller on mobile, not simpler, with a 44px drag puck.
  • 相手 — a costume room, not a character editor. Model cards ("changing model is a costume change — same conversation, same memory"), a scale slider that is the same value as the desktop pal's corner grip, voice chips from the TTS server with 試聴 that speaks one line through her mouth, and the persona as a read-only readout owned by Hermes. The client never edits the persona.
  • — go-between address + token with a 確認 button that reports what it found; upstream Hermes read-only as reported by the go-between; 会話を覚えておく (session id, which is also how interrupt reaches a running turn); 道具の実況 (tool transparency); 自発送信 as a read-and-mute list of cron jobs. Scheduling stays in Hermes — the client mutes, it does not edit jobs.
  • 本体 — three version rows (端末 / 中継局 / 上流; the client cannot check Hermes itself, so upstream reads "中継局が報告"), link chips, and やり直す reset at two levels. Both reset levels state that the log and her memory live on the server and are untouched. No affordance to delete her, ever — and log export is deliberately out of scope; do not add one.

Depth: common fields open, the rest behind a per-group 詳細 reveal that names what is inside it ("冗長フレーム, 圧縮率, 最大幅 ほか5件").

Design reference: docs/design/denpa-receiver/README.md (card ids 1a, 4a… index into Denpa Receiver.dc.html, openable in a browser).

Acceptance criteria

  • Four tabs; avatar stays visible and reacts live to changes
  • Threshold meter replaces the raw sliders and drives the real VAD values
  • Scale slider and pal corner grip read and write the same persisted value
  • 試聴 plays through the avatar, not a bare audio element
  • Persona and upstream Hermes rows are read-only
  • Cron job list can be muted but not edited
  • Reset levels behave as described; no delete-her affordance, no log export
  • 詳細 reveals name their contents
  • Full parity on mobile at 370px
  • Tests cover threshold drag → stored VAD values

Blocked by


Reality audit

Governing rule: build to reality, Hermes leads. Not doing something is acceptable if it does not break core functionality. Breaking the design is acceptable. Building UI that does nothing is not.

Audited against the actual wire surface:

  • Hermes (hermes-agent 0.19.0, verified running): /health, /v1/models, /v1/runs, /v1/runs/{id}, /v1/runs/{id}/events, /v1/runs/{id}/stop, /v1/runs/{id}/approval
  • Run events: message.delta, tool.started, tool.completed, run.completed, run.cancelled, run.failed/run.errored, reasoning.available, approval.request (carries a per-request choices array and sets run status waiting_for_approval; the go-between discards it today — see #19)
  • Go-between → client: full-text, audio, control, error, set-model-and-conf, config-switched, config-files, background-files, history-list, history-data, new-history-created, history-deleted, user-input-transcription, backend-synth-complete, force-new-message, tool_call_status, interrupt-signal, heartbeat-ack, group-update
  • Client → go-between: text-input, mic-audio-data, mic-audio-end, raw-audio-data, ai-speak-signal, interrupt-signal, fetch-history-list, fetch-and-set-history, create-new-history, delete-history, fetch-configs, switch-config, fetch-backgrounds, audio-play-start, request-init-config, heartbeat

Heavily cut — most of this settings sheet has no data behind it

Audited row by row. Keep only what has a wire behind it.

電波 — keep VAD thresholds and hands-free (client-side, real; the しきい値 drag-meter is the centrepiece and survives intact). Cut per-setting ASR and TTS controls: the only mechanism is switch-config, which swaps a whole config file. Offer config switching as what it is, not as granular engine settings.

相手 — keep the model/character list (config-files, switch-config, set-model-and-conf) and the scale slider (client-side, shared with the pal grip). Cut the voice bank and 試聴: no message lists TTS voices, so the chips would be empty and the preview button dead. Cut the persona readout: nothing carries Hermes'' persona to the client.

— keep the address and token fields, and the 確認 button (a real connect probe). Cut the upstream Hermes readout (no data). Cut the 会話を覚えておく toggle: the session id is managed inside the go-between''s agent, not switchable from the client. Cut the 道具の実況 toggle: tool transparency is Hermes-side configuration with no client control message. The 自発送信 cron list is NOT cut — the original call that nothing enumerates Hermes' jobs was wrong. GET /api/cron/jobs returns id, name, state (scheduled/paused), enabled, schedule_display, next_run_at, last_run_at, last_status, last_error, and POST /api/cron/jobs/{id}/pause|resume is the mute without touching the schedule — the read-and-mute list exactly as drawn. Caveat: these routes are served by hermes dashboard (default port 9119, X-Hermes-Session-Token auth, honours HERMES_DASHBOARD_SESSION_TOKEN), a different surface from the /v1/runs API the go-between uses today, so the go-between must reach it and relay.

本体 — keep the client version and the two reset levels (both client-side). Cut the 中継局 and 上流 version rows: no message reports either. The three-version story is the right idea and the wrong time.

What survives is a smaller, honest sheet: signal tuning, who you are looking at, where to connect, and reset. Do not leave placeholders, greyed-out rows, or "coming soon" affordances for the cut items — absent, not disabled.

The four-tab structure stays; two tabs are just thinner than drawn. The reset footer copy (彼女を消す方法はここにない) stays and is still non-negotiable.

## What to build Replaces the six upstream settings tabs with four, grouped **by what you are adjusting** (cards 3b, 3c, 5b, 5e, 7a). A sheet, not a page — she stays visible beside it and **reacts to changes as you make them**. On mobile it is a bottom sheet with a grab handle; parity is full, nothing is dropped for small screens. - **電波** — ASR, TTS, VAD, hands-free. **しきい値 is the important control:** three raw sliders with paragraphs of help become one live meter you drag two lines onto (発話 and 無音). Taller on mobile, not simpler, with a 44px drag puck. - **相手** — a costume room, not a character editor. Model cards ("changing model is a costume change — same conversation, same memory"), a scale slider that is the *same value* as the desktop pal's corner grip, voice chips from the TTS server with 試聴 that speaks one line **through her mouth**, and the persona as a read-only readout owned by Hermes. **The client never edits the persona.** - **局** — go-between address + token with a 確認 button that reports what it found; upstream Hermes read-only as reported by the go-between; 会話を覚えておく (session id, which is also how interrupt reaches a running turn); 道具の実況 (tool transparency); 自発送信 as a read-and-mute list of cron jobs. **Scheduling stays in Hermes — the client mutes, it does not edit jobs.** - **本体** — three version rows (端末 / 中継局 / 上流; the client cannot check Hermes itself, so upstream reads "中継局が報告"), link chips, and やり直す reset at two levels. Both reset levels state that the log and her memory live on the server and are untouched. **No affordance to delete her, ever — and log export is deliberately out of scope; do not add one.** Depth: common fields open, the rest behind a per-group 詳細 reveal that **names what is inside it** ("冗長フレーム, 圧縮率, 最大幅 ほか5件"). Design reference: `docs/design/denpa-receiver/README.md` (card ids `1a`, `4a`… index into `Denpa Receiver.dc.html`, openable in a browser). ## Acceptance criteria - [ ] Four tabs; avatar stays visible and reacts live to changes - [ ] Threshold meter replaces the raw sliders and drives the real VAD values - [ ] Scale slider and pal corner grip read and write the same persisted value - [ ] 試聴 plays through the avatar, not a bare audio element - [ ] Persona and upstream Hermes rows are read-only - [ ] Cron job list can be muted but not edited - [ ] Reset levels behave as described; no delete-her affordance, no log export - [ ] 詳細 reveals name their contents - [ ] Full parity on mobile at 370px - [ ] Tests cover threshold drag → stored VAD values ## Blocked by - #11 --- ## Reality audit Governing rule: **build to reality, Hermes leads. Not doing something is acceptable if it does not break core functionality. Breaking the design is acceptable. Building UI that does nothing is not.** Audited against the actual wire surface: - **Hermes** (`hermes-agent 0.19.0`, verified running): `/health`, `/v1/models`, `/v1/runs`, `/v1/runs/{id}`, `/v1/runs/{id}/events`, `/v1/runs/{id}/stop`, `/v1/runs/{id}/approval` - **Run events**: `message.delta`, `tool.started`, `tool.completed`, `run.completed`, `run.cancelled`, `run.failed`/`run.errored`, `reasoning.available`, `approval.request` (carries a per-request `choices` array and sets run status `waiting_for_approval`; the go-between discards it today — see #19) - **Go-between → client**: `full-text`, `audio`, `control`, `error`, `set-model-and-conf`, `config-switched`, `config-files`, `background-files`, `history-list`, `history-data`, `new-history-created`, `history-deleted`, `user-input-transcription`, `backend-synth-complete`, `force-new-message`, `tool_call_status`, `interrupt-signal`, `heartbeat-ack`, `group-update` - **Client → go-between**: `text-input`, `mic-audio-data`, `mic-audio-end`, `raw-audio-data`, `ai-speak-signal`, `interrupt-signal`, `fetch-history-list`, `fetch-and-set-history`, `create-new-history`, `delete-history`, `fetch-configs`, `switch-config`, `fetch-backgrounds`, `audio-play-start`, `request-init-config`, `heartbeat` ### Heavily cut — most of this settings sheet has no data behind it Audited row by row. **Keep only what has a wire behind it.** **電波** — keep VAD thresholds and hands-free (client-side, real; the しきい値 drag-meter is the centrepiece and survives intact). **Cut per-setting ASR and TTS controls**: the only mechanism is `switch-config`, which swaps a whole config file. Offer config switching as what it is, not as granular engine settings. **相手** — keep the model/character list (`config-files`, `switch-config`, `set-model-and-conf`) and the scale slider (client-side, shared with the pal grip). **Cut the voice bank and 試聴**: no message lists TTS voices, so the chips would be empty and the preview button dead. **Cut the persona readout**: nothing carries Hermes'' persona to the client. **局** — keep the address and token fields, and the 確認 button (a real connect probe). **Cut the upstream Hermes readout** (no data). **Cut the 会話を覚えておく toggle**: the session id is managed inside the go-between''s agent, not switchable from the client. **Cut the 道具の実況 toggle**: tool transparency is Hermes-side configuration with no client control message. **The 自発送信 cron list is NOT cut** — the original call that nothing enumerates Hermes' jobs was wrong. `GET /api/cron/jobs` returns `id`, `name`, `state` (`scheduled`/`paused`), `enabled`, `schedule_display`, `next_run_at`, `last_run_at`, `last_status`, `last_error`, and `POST /api/cron/jobs/{id}/pause|resume` is the mute without touching the schedule — the read-and-mute list exactly as drawn. Caveat: these routes are served by `hermes dashboard` (default port 9119, `X-Hermes-Session-Token` auth, honours `HERMES_DASHBOARD_SESSION_TOKEN`), a different surface from the `/v1/runs` API the go-between uses today, so the go-between must reach it and relay. **本体** — keep the client version and the two reset levels (both client-side). **Cut the 中継局 and 上流 version rows**: no message reports either. The three-version story is the right idea and the wrong time. What survives is a smaller, honest sheet: signal tuning, who you are looking at, where to connect, and reset. **Do not leave placeholders, greyed-out rows, or "coming soon" affordances for the cut items** — absent, not disabled. The four-tab structure stays; two tabs are just thinner than drawn. The reset footer copy (`彼女を消す方法はここにない`) stays and is still non-negotiable.
Author
Owner

Correction — 声 comes back, hardcoded

I cut the voice bank alongside the persona readout. Wrong grouping: TTS runs in the go-between, not Hermes, so this is our code to change, not upstream. Restoring it.

What is actually true today:

  • conf.yaml runs tts_model: fallback — primary vllmn_tts (http://127.0.0.1:8091/v1/audio/speech, model Qwen/Qwen3-TTS-12Hz-1.7B-Base, voice_id: mai), falling back to edge_tts (en-US-AvaMultilingualNeural) after 10s.
  • No enumeration exists. No list_voices on TTSInterface; every backend takes one voice string at construction. No websocket message carries voices.
  • mai is the only configured voice id in the stack. The vllmn server was not running when checked, so nothing could be enumerated live. The alloy/shimmer/さくら/つむぎ names in the design are marked illustrative in the handoff and must not be baked in as real ids.

Build it as a hardcoded client-side constant — one array of voice ids, extended by editing that array. No enumeration, no discovery, no capability negotiation.

Two open items before the row is more than a readout:

  1. The real voice ids. Only mai is known. Until the list is supplied, ship the row as a readout showing the active voice and the fallback engine — not chips, not a picker. A one-item picker is decoration.
  2. An apply path. Changing the selection needs a set-voice message in the go-between; voice_id is currently read from conf.yaml at engine construction. Small, and in our repo. Until it exists, the row does not offer selection.

試聴 stays cut for now. Previewing an unselected voice means synthesising with it, which needs the same apply path plus per-request voice override. It comes back with selection, not before.

Everything else in the reality audit above stands.

## Correction — 声 comes back, hardcoded I cut the voice bank alongside the persona readout. Wrong grouping: **TTS runs in the go-between, not Hermes**, so this is our code to change, not upstream. Restoring it. What is actually true today: - `conf.yaml` runs `tts_model: fallback` — primary `vllmn_tts` (`http://127.0.0.1:8091/v1/audio/speech`, model `Qwen/Qwen3-TTS-12Hz-1.7B-Base`, `voice_id: mai`), falling back to `edge_tts` (`en-US-AvaMultilingualNeural`) after 10s. - **No enumeration exists.** No `list_voices` on `TTSInterface`; every backend takes one `voice` string at construction. No websocket message carries voices. - **`mai` is the only configured voice id in the stack.** The vllmn server was not running when checked, so nothing could be enumerated live. The alloy/shimmer/さくら/つむぎ names in the design are marked illustrative in the handoff and must not be baked in as real ids. **Build it as a hardcoded client-side constant** — one array of voice ids, extended by editing that array. No enumeration, no discovery, no capability negotiation. Two open items before the row is more than a readout: 1. **The real voice ids.** Only `mai` is known. Until the list is supplied, ship the row as a **readout** showing the active voice and the fallback engine — not chips, not a picker. A one-item picker is decoration. 2. **An apply path.** Changing the selection needs a set-voice message in the go-between; `voice_id` is currently read from `conf.yaml` at engine construction. Small, and in our repo. Until it exists, the row does not offer selection. **試聴 stays cut for now.** Previewing an unselected voice means synthesising with it, which needs the same apply path plus per-request voice override. It comes back with selection, not before. Everything else in the reality audit above stands.
Author
Owner

Settled — 声 is a readout, no picker

Skipping voice selection for now. Final scope for this row:

  • Readout only. Show the active voice id and the engine behind it. No chips, no selection, no 試聴.
  • No apply path needed, so no go-between change is required for this issue.
  • Do not ship a disabled picker or a "coming soon" affordance — absent, not greyed, per the rule for every other cut in this sheet.

The hardcoded-constant approach is deferred with it: when selection is wanted, it arrives as the id list plus a set-voice message, and this row grows into a picker then.

## Settled — 声 is a readout, no picker Skipping voice selection for now. Final scope for this row: - **Readout only.** Show the active voice id and the engine behind it. No chips, no selection, no 試聴. - **No apply path needed**, so no go-between change is required for this issue. - **Do not ship a disabled picker** or a "coming soon" affordance — absent, not greyed, per the rule for every other cut in this sheet. The hardcoded-constant approach is deferred with it: when selection is wanted, it arrives as the id list plus a set-voice message, and this row grows into a picker then.
Author
Owner

局 gains back the cron list — the "nothing enumerates Hermes' jobs" call was wrong

This issue's reality audit cut the 自発送信 cron list on the grounds that nothing enumerates Hermes' jobs. That is false, and the design doc is corrected.

hermes dashboard (default port 9119) serves:

  • GET /api/cron/jobsid, name, state (scheduled/paused), enabled, schedule_display, next_run_at, last_run_at, last_status, last_error, deliver
  • GET /api/cron/jobs/{id} — the same for one job
  • POST /api/cron/jobs/{id}/pause and /resumethis is the mute, and it does not touch the schedule

Which is exactly the read-and-mute list drawn in 3c: 07:00 morning.brief · 毎日, muteable, not editable. The acceptance criterion "Cron job list can be muted but not edited" is satisfiable as written.

Two caveats that make this go-between work rather than client work:

  • Different surface. These routes live on the dashboard server, not the /v1/runs API the go-between uses today. The go-between has to reach it and relay; the client still talks only to the go-between.
  • Auth. A loopback bind skips the OAuth gate but still requires a session token, which is ephemeral and never written to disk (hermes_cli/web_server.py:298). It honours HERMES_DASHBOARD_SESSION_TOKEN, so launch the dashboard with a shared value and have the go-between send X-Hermes-Session-Token. Verified: without it 401, with it 200.

The other 3c cuts stand — upstream Hermes readout, 会話を覚えておく, 道具の実況.

## 局 gains back the cron list — the "nothing enumerates Hermes' jobs" call was wrong This issue's reality audit cut the 自発送信 cron list on the grounds that nothing enumerates Hermes' jobs. That is false, and the design doc is corrected. `hermes dashboard` (default port 9119) serves: - `GET /api/cron/jobs` — `id`, `name`, `state` (`scheduled`/`paused`), `enabled`, `schedule_display`, `next_run_at`, `last_run_at`, `last_status`, `last_error`, `deliver` - `GET /api/cron/jobs/{id}` — the same for one job - `POST /api/cron/jobs/{id}/pause` and `/resume` — **this is the mute**, and it does not touch the schedule Which is exactly the read-and-mute list drawn in 3c: `07:00 morning.brief · 毎日`, muteable, not editable. The acceptance criterion *"Cron job list can be muted but not edited"* is satisfiable as written. Two caveats that make this go-between work rather than client work: - **Different surface.** These routes live on the dashboard server, not the `/v1/runs` API the go-between uses today. The go-between has to reach it and relay; the client still talks only to the go-between. - **Auth.** A loopback bind skips the OAuth gate but still requires a session token, which is ephemeral and never written to disk (`hermes_cli/web_server.py:298`). It honours `HERMES_DASHBOARD_SESSION_TOKEN`, so launch the dashboard with a shared value and have the go-between send `X-Hermes-Session-Token`. Verified: without it `401`, with it `200`. The other 3c cuts stand — upstream Hermes readout, 会話を覚えておく, 道具の実況.
Author
Owner

Closing in favour of #29, rewritten against the ADRs.

This issue's premise changed with ADR-0001: Denpa is a Hermes gateway Platform adapter and the api_server HTTP surface is abandoned entirely. Patching the body would have left a trail of corrections on top of a stale audit block - the same pattern that got 承認 wrongly cut once already.

Decisions now live in docs/adr/ in the go-between repo (branch docs/hermes-platform-adrs). The replacement issue references them rather than restating them.

Closing in favour of #29, rewritten against the ADRs. This issue's premise changed with ADR-0001: Denpa is a Hermes gateway Platform adapter and the `api_server` HTTP surface is abandoned entirely. Patching the body would have left a trail of corrections on top of a stale audit block - the same pattern that got 承認 wrongly cut once already. Decisions now live in `docs/adr/` in the go-between repo (branch `docs/hermes-platform-adrs`). The replacement issue references them rather than restating them.
aiko closed this issue 2026-07-30 00:03:49 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiko/denpa#14
No description provided.