Files
Veskslop-t/CURRENT.md
T

177 lines
12 KiB
Markdown

# CURRENT.md — session state
Last updated: 2026-09-14
This file tracks the work state. Read it first. Update it before you stop.
## Current focus (broad)
The text layer is done. Voice actions and audio work. The local LLM harness (Veskslop agent, Go +
Eino) is scaffolded and smoke-tested; the daemon-side task push is next. The web UI has a first
mobile draft at 390px.
## In progress
- The Veskslop agent lives in `agent/` (Go 1.25+, Eino v0.9.19, remote llama.cpp over the
OpenAI-compatible API, daemon tools over MCP Streamable HTTP). It is disabled by default
(`VESKSLOP_AGENT_ENABLED=1`), takes tasks on `POST /task`, and logs every tool call. Smoke-tested
on 2026-09-14: 28 tools loaded, one ReAct round, final message returned. The daemon does not push
tasks to it yet. Plan and env vars: [docs/agent.md](docs/agent.md).
- Remote agents use the daemon's MCP endpoint directly (query state, send notifications). A
notification tool set and a documented LAN/Tailscale bind are planned.
- Mobile web UI first draft: four tabs (Users, Chat, Call, Activity), one pane at a time, 44px
targets, tab badge, call bar pinned, Activity becomes the full console. Verified headless at
390x844. Next: transcript and TTS box in the Call tab, per the 1c design.
- Fixed a pre-existing web UI bug: `loadDms` called the nonexistent `renderDirect`, so the DM rail
only rendered from other paths. It now calls `renderDmRail()`.
- Voice audio is implemented: daemon-managed virtual devices (`veskslop_tts`, `veskslop_mic`,
`veskslop_capture`), TTS providers (local command + HTTP API), queued playback through `ffplay`,
ducking on `VOICE_SPEAKING`, and WAV capture. The daemon starts `pulseaudio` when `pactl` cannot
reach a server and reports install hints. Verified live on 2026-09-13 after installing
`pulseaudio`/`ffmpeg`: engine ok, devices created, PCM streamed.
- The Web UI serves self-signed HTTPS (`openssl`, stored under the state dir) so browser media
APIs have a secure context. `WEBUI_TLS=0` disables it.
- Capture and transcript are implemented: ffmpeg reads the client output, a VAD cuts it into
utterances, each becomes a WAV plus a transcript entry with the dominant speaker, and a
pluggable STT hook (local command or HTTP API) fills the text. Live events feed the console
Transcript tab; `get_transcript` and `/api/transcript` read the store. `VESKSLOP_CAPTURE_AUTO=1`
records calls automatically. Verified end to end with real PulseAudio on 2026-09-13.
- STT uses Vosk (CPU, light): `poc/scripts/stt-vosk.py` plus a small model in `poc/models/`
(auto-detected). Verified with a TTS→capture→Vosk round trip. Other providers remain available
via `VESKSLOP_STT_CMD`/`_API_URL`.
- TTS defaults to `espeak-ng` when installed; engine and voice live in Settings → Client → Speech
(daemon preferences, so all browsers share them).
- Preferences moved to `<state dir>/preferences.json` and no longer depend on the core, so guild
visibility saves even while the client is down. API responses send `Cache-Control: no-store`.
- Browser audio has an **Enable browser audio** button that requests the microphone permission,
which browsers require before listing output devices.
- Browser microphone puppeting (talking through the bot) is next; it needs a mix sink, a `pacat`
input, and echo handling.
- The web UI follows the Nocturne design handoff (`poc/WebUIMockup.zip`). The 7a shell is
implemented and rendered headless for review: header, call strip, three-column body, composer,
and the agent console.
- Takeover is a client-side read-only gate. Off, the composer is locked. Manual sends are logged.
- Core process control is implemented and verified live: the daemon starts the client under
auto-Xvfb, the bridge connects, and stop exits cleanly.
- Web QR login is implemented. The session reached `pending` against the real gateway; a full
phone approval is pending.
## Done
- Validated core bridge: protocol, actions, validation, structured errors, hard outbound rate limit.
- Auth persistence: pinned `DATA_DIR`, token file, preload seeding, `VESKSLOP_HEADLESS`.
- Daemon on the core-bridge transport. CDP stays as a development fallback.
- Trusted-only triggers: DMs and direct mentions. Trust tiers: user, trusted, admin.
- Context: 15 messages or 4000 characters, time stamps, file names only, incremental fetch, tmp
cache with a size limit, fetch cooldown.
- Continuation window: a rushed follow-up joins the request context.
- Response policy: origin channel or DM. Sensitive commands default to DM.
- Persistent action log: triggers, commands, replies, errors, rotation, and secret redaction.
- `poll` is paused (`POLL_ENABLED = false`). The parser and its tests stay.
- Human send pacing: first send of a burst waits 30-200 ms, later sends 500-2800 ms, and the burst
resets after a random 5-20 s of quiet. Reads take 100-300 ms and do not reset the burst.
- `user.search`: cache and guild-member lookup. `voice.get` searches all guilds without a `guildId`.
- `voice.overview`: occupied voice channels and members for a guild.
- `VOICE_STATE_UPDATE` events: normalized and pushed. Mock-verified.
- Voice actions: `voice.join` (muted), `voice.leave`, and `voice.move` in the core, with daemon
routes (`/api/voice/*`), MCP tools, and Web UI wiring. Voice actions are paced 250-750 ms and
written to the action log. The internal lookups were probed on the live client.
- Web UI voice rail: every voice channel is clickable. Clicking joins (muted); clicking the active
channel leaves. Verified with the stateful mock.
- Web UI startup: a **Start server** button appears left of Takeover while the bridge is down and
hides when it connects. State reloads retry while the client boots, so guilds appear without a
manual refresh. Core requests now time out after `VESKSLOP_REQUEST_TIMEOUT_MS` instead of
hanging forever on a missing reply.
- Settings: "Visible guilds" stays top level; Audio sits under a collapsible "Client" group;
Core process, Account login, and Action log sit under a collapsible "Server" group.
- Web UI timestamps use the browser timezone by default; Settings → Client → Display can override
it with any IANA zone. The choice is per browser (`localStorage`).
- Web UI preferences: visible guilds persist server-side in the daemon state file
(`/api/preferences`), so every browser shares them. The Settings panel has an explicit **Save**
button; selections filter the UI live and persist only on Save.
- DM list: recipients resolve from IDs through the user store, so names and avatars load.
- Guild menu: fixed the light default button background.
- Web UI: Nocturne design tokens in `poc/public/nocturne.css`, the 7a debug shell, live guild and
channel rail, messages, DMs, voice membership, agent console with a live event tab, and a
Settings slide-over for core process, login, visible guilds, and the action log. Roadmap
surfaces show as placeholders.
- Web UI data: opening a chat fetches the latest 50 messages into the client cache; attachments
hotlink Discord CDN URLs and are never stored; avatars and guild icons load from the CDN with a
monogram fallback; the page reloads itself when the bridge connects.
- Web UI drafts: composer and TTS text persist per channel in `localStorage` and survive
re-renders, reconnects, and page reloads.
- Visible guilds: the Settings panel filters the guild switcher and picker.
- Takeover: client-side read-only switch with `localStorage` persistence; manual sends are written
to the action log with `type: "manual"`.
- `dm.list`: cached DM and group DM channels, exposed over MCP as `list_dms`.
- Core process control: the daemon starts, stops, and restarts the client, reconnects the bridge
after a restart, auto-starts Xvfb when `DISPLAY` is missing, and stops what it started on exit.
Override with `VESKSLOP_CORE_CMD`/`ARGS`; disable Xvfb with `VESKSLOP_XVFB=0`.
- Web QR login: the Settings panel runs the remote-auth session and renders the QR. The token file
is written on approval and a running managed core restarts with the new token.
- Unit tests cover the pacing policy. `pnpm lint` and `pnpm testTypes` pass at the root.
- Web UI navigation: Guild/DM scope tabs, a DM rail with unread badges (purple/orange, grey while
Takeover is off), last-message times, favourite stars, and a People list of users seen in voice.
Favourited users get presence and call tracking, grouped by call in **In voice**. Speaking shows
level bars and bold names (2c marks for mute/deaf/video/share). The body dims while the bridge
is down; the Start button becomes Refresh when the daemon is unreachable.
- TTS settings are split into language and voice, grouped by base language with the requested
languages pinned on top.
- Text notifications are parsed: edits update the context cache, deletes drop from it, trusted
reactions are logged, and replies are kept in the request context. A reply to a puppet message
counts as a trigger without a mention. The web UI marks edited messages and replies.
- Voice auto-leave is implemented: when the owner (zzib) leaves the bot's call, the bot leaves
after `voiceLeaveDelaySeconds` (default 30). A rejoin cancels it. Verified with the mock,
including cancellation. `stay @user` is deferred.
- Audio verified end to end on 2026-09-14: TTS played into the virtual mic (recording
`veskslop_mic` peaked at -4.1 dB) and the browser monitor streamed PCM over the TLS WebSocket.
- Next: per-user limits and the collector account. The agent attachment (remote llama.cpp, MCP
transport, event delivery) waits for a design discussion; the draft prompt has open issues to
settle first.
## Next (in order)
1. Live verification: `voice.join`/`voice.leave` end to end, `user.search`, `voice.overview`,
`VOICE_STATE_UPDATE`, and a full web QR login approval on the real client.
2. Agent: daemon task push for free-form triggers (commands stay local), tool allowlist,
per-conversation serialization, and tests against a fake model.
3. Collector account: read-only modules for voice membership fallback.
4. Per-user rate limit and command cooldowns.
5. Mobile web UI: transcript and TTS in the Call tab; verify on a phone.
## Open discussions (need the user)
1. **Web UI auth.** Accepted: no auth, LAN or Tailscale only. Revisit before any wider exposure.
2. **MCP event delivery.** Decided for the local harness: the daemon pushes tasks to the agent's
`POST /task`. The transport details (retry, dedupe IDs, queue) are still open. Remote agents
poll or call tools directly; they do not receive pushes.
3. **Untrusted logging.** Untrusted interactions are ignored at this time. Confirm whether to log
untrusted DMs and mentions again.
4. **Privacy pass.** Only token-shaped and `mfa.` strings are redacted, and only in the persistent
action log. A fuller policy (retention, cache clearing, message redaction) is deferred.
5. **Live checks.** `reaction.add`, `user.search`, `voice.overview`, and the voice state event
normalization are mock-verified only. The QR login session reached `pending` and `cancel`
against the real gateway; the approval path needs the phone. Confirm on the first real-client
run.
## Blocked / needs input
- The original repo `zib/Veskslop` has a broken server-side git setup. Fetches and clones fail with
`bad pack header` because a script (`/home/git/.sys_health_s3`) runs on the Veskslop read path.
The admin must fix it. Use `zib/Veskslop-t` (remote `veskslop-t`) until then.
- `main` on the original server is `68fc8bb` (zib, "Upload files to docs/images"). It diverged from
the local `main` (`24447f5`). Reconcile after the server is fixed.
## Where to look
- Backlog: `TODO.md`
- Design documents: `docs/INDEX.md`
- Design handoff: `poc/WebUIMockup.zip` (screen 7a is implemented)
- Agent rules: `AGENTS.md`
- Daemon code: `poc/src/`
- Web UI code: `poc/src/webui.ts`, `poc/public/`
- Core bridge code: `src/shared/puppetProtocol.ts`, `src/renderer/puppet/`,
`src/main/puppetBridge.ts`