Rework core integration: validated capability layer, docs, and session state
This commit is contained in:
@@ -1,130 +1,65 @@
|
||||
# AGENTS.md — Veskslop
|
||||
|
||||
## What this is
|
||||
Veskslop controls Discord user accounts without a visible client window. It runs the real Vesktop
|
||||
client and drives it through a small, validated capability layer. All logic lives in the external
|
||||
daemon.
|
||||
|
||||
Veskslop is a **headless Discord client daemon** built on the Vesktop codebase. It runs the **real
|
||||
Discord web client** (Vencord-patched Vesktop) without a visible window and puppets the client's own
|
||||
interactions, so traffic and behavior stay as close to the official client as possible.
|
||||
**Read the docs first:** [docs/INDEX.md](docs/INDEX.md) and [CURRENT.md](CURRENT.md).
|
||||
|
||||
- an **MCP server** (Streamable HTTP) lets AI agents drive the account;
|
||||
- multiple user identities are planned (one read-only collector, one full puppet);
|
||||
- **audio in/out** and **video streaming** come later, using the client's native voice stack
|
||||
plus PipeWire virtual devices on Linux;
|
||||
- everything is **local-first**: clients reach it over LAN/Tailscale only.
|
||||
`CURRENT.md` tracks the work state: the current focus, the completed work, the current task, and the
|
||||
next steps. Read it before you start. Update it before you stop.
|
||||
|
||||
**Vesktop is a separate project with separate goals.** This repo shares history with it but not
|
||||
direction.
|
||||
- [architecture.md](docs/architecture.md) — system parts and boundaries
|
||||
- [core-bridge.md](docs/core-bridge.md) — the Vesktop-side capability layer
|
||||
- [daemon.md](docs/daemon.md) — the external MCP daemon
|
||||
- [communication.md](docs/communication.md) — trust model, commands, trigger log
|
||||
- [auth.md](docs/auth.md) — QR login and token persistence
|
||||
- [testing.md](docs/testing.md) — tests and live test setup
|
||||
- [decisions.md](docs/decisions.md) — design decisions
|
||||
|
||||
## Decisions (locked unless the user changes them)
|
||||
## Locked decisions
|
||||
|
||||
- **Core:** run the actual Vesktop/Discord client (Electron) headless. Do **not** build on
|
||||
third-party selfbot protocol libraries — the popular ones are archived/deprecated and stale
|
||||
- Use the real Vesktop client. Do not use selfbot protocol libraries. Most are archived, and stale
|
||||
protocol emulation is a ban risk.
|
||||
- **Control:** puppet Vesktop's own interactions through its IPC command bus:
|
||||
`vesktop.sock` (external) → `sendRendererCommand` (main) → renderer handlers that call
|
||||
Vencord/Discord internals (`sendMessage`, stores, voice actions).
|
||||
- **MCP:** an external local daemon speaks a JSON-lines protocol over `vesktop.sock` and exposes
|
||||
Streamable HTTP MCP. It must run without Electron so it is testable standalone.
|
||||
- **QR login:** our own terminal remote-auth tool (`poc/qr-login.ts`) acquires a user token.
|
||||
Feeding that token into the headless client is deferred until it can be tested.
|
||||
- **Accounts:** multiple user tokens. Expected setup is one read-only collector account plus one
|
||||
full-control puppet account; design for N identities behind one control surface.
|
||||
- **Network:** bind localhost by default. Remote access is always over LAN/Tailscale — never expose
|
||||
to the public internet. A client will later authenticate to the proxy for remote control.
|
||||
- **Audio/video:** use the client's native stack first; PipeWire virtual devices for system audio
|
||||
piping. ffmpeg is required for non-Opus media and video encoding.
|
||||
- **GUI:** optional minimal **web debug UI** later. The daemon must never require a visible window;
|
||||
on servers without a display, a virtual display (Xvfb) will be needed for Electron.
|
||||
- **Pacing/safety:** all outbound actions are queued and paced.
|
||||
- **Current policy:** every command sent to the client is spaced by a random **500–2800 ms**,
|
||||
serialized, with identical in-flight queries coalesced; prefer cached state over queries.
|
||||
- **Later policy:** human emulation — snappy first action (~30–80 ms), bulk actions randomized,
|
||||
opt-in risky fast mode, free-account limits by default, ability toggles.
|
||||
- The core validates. The daemon requests. The core rejects bad input with a structured error.
|
||||
The client must not crash, and the daemon must not send malformed data to Discord.
|
||||
- Keep core patches isolated. Add actions in `src/renderer/puppet/` and the bridge in
|
||||
`src/main/puppetBridge.ts`. Do not spread logic through upstream files.
|
||||
- The daemon holds the logic: triggers, trust list, command parsing, trigger log, and pacing.
|
||||
- The daemon binds to localhost. Remote access goes over LAN or Tailscale only.
|
||||
- Pacing: the daemon delays every command by 500 to 2800 ms. The core adds a hard limit of 500 ms
|
||||
between sends and 30 sends per minute.
|
||||
|
||||
## Auth persistence
|
||||
## Guardrails
|
||||
|
||||
- Durable credential: the token file, `VESKSLOP_TOKEN_FILE` (default `<DATA_DIR>/token`, mode 600,
|
||||
gitignored). It is the **puppet account's** token; zzib is only the DM test target.
|
||||
- `DATA_DIR` is pinned: `$VENCORD_USER_DATA_DIR`, else `$VESKSLOP_DATA_DIR`, else
|
||||
`<appData>/veskslop` (`src/main/constants.ts`). It does not depend on the app/product name, so
|
||||
renames and version bumps do not move the Electron profile where Discord stores the session.
|
||||
- On startup the preload asks main for the token (sync IPC) and seeds `localStorage.token` before
|
||||
the client boots, but only when no session exists. `VESKSLOP_FORCE_TOKEN=1` overrides.
|
||||
- Recovery: if Discord invalidates the session, run `pnpm poc:qr` for a fresh token and update the
|
||||
token file.
|
||||
- Discord deletes the stored token when it thinks devtools opened; keep Vesktop's devtools detector
|
||||
patch (`src/renderer/patches/devtoolsFixes.ts`) so this cannot cause random logouts.
|
||||
- Never commit tokens, credentials, or `.env` files.
|
||||
- Do not hardcode Discord limits from memory. Make the limits configurable.
|
||||
- All outbound actions go through the paced queue.
|
||||
- Test with throwaway accounts and private test guilds only.
|
||||
- Live send tests: only DMs to user **zzib** (`177117296140091393`).
|
||||
- Run `pnpm testTypes` and `pnpm lint` before you report done. Run `pnpm build` to compile.
|
||||
|
||||
## Headless run (verified 2026-09-11)
|
||||
## Status
|
||||
|
||||
The real client has been run with no visible window and successfully driven end-to-end:
|
||||
| Area | Status |
|
||||
| --- | --- |
|
||||
| Action protocol and renderer actions | Implemented |
|
||||
| Main bridge, event push, hooks | Pending |
|
||||
| Daemon on the CDP transport | Implemented and verified live |
|
||||
| Daemon on the core-bridge transport | Pending |
|
||||
| QR login, pacing, trigger log | Implemented |
|
||||
| Auth persistence in the core | Verified, then removed during the CDP pivot. To re-add. |
|
||||
| Voice, audio, video | Not started |
|
||||
|
||||
```sh
|
||||
Xvfb :99 -screen 0 1280x720x24 &
|
||||
DISPLAY=:99 VESKSLOP_HEADLESS=1 \
|
||||
VESKSLOP_TOKEN_FILE=~/.config/veskslop/token \
|
||||
electron . --no-sandbox --disable-gpu --disable-dev-shm-usage
|
||||
```
|
||||
## Work items
|
||||
|
||||
- A display server is still required; Xvfb works.
|
||||
- `VESKSLOP_HEADLESS=1` skips the first-launch tour so the main window is created directly.
|
||||
- The token file is seeded before the client boots (verified: logged in with the puppet account).
|
||||
- Outbound sends must pass `waitForChannelReady = false` (`sendMessage(channelId, data, false)` in
|
||||
`src/renderer/ipcCommands.ts`). Otherwise the client's MessageQueue waits forever for channels
|
||||
that are never opened in a headless session.
|
||||
- `message:read` is cache-only; channels the client never opened return zero messages. A
|
||||
fetch-based read (through the client) is still to be added.
|
||||
See the todo list in the current session. The main items are:
|
||||
|
||||
## How to proceed
|
||||
|
||||
**Current milestone — puppeting bridge (testable without Electron).**
|
||||
|
||||
1. Extend Vesktop's `vesktop.sock` server with a JSON-lines request/response protocol (keep the
|
||||
legacy `run-shortcut` line working).
|
||||
2. Add renderer handlers for `state:get`, `message:send`, `message:read` via Vencord internals.
|
||||
3. Build the external MCP daemon (`poc/src/control-bridge.ts`) with a mock Vesktop socket
|
||||
(`poc/src/mock-vesktop.ts`) so the whole path is testable in this container.
|
||||
4. Then: voice join/leave handlers, audio pipes, video streaming, token injection, client
|
||||
lightening, remote client auth.
|
||||
|
||||
Anything that needs a display, audio device, or a real client instance is **deferred** until it can
|
||||
be tested; note it instead of guessing.
|
||||
|
||||
## Architecture sketch
|
||||
|
||||
```
|
||||
MCP clients (agents) ──Streamable HTTP──▶ veskSlop control daemon
|
||||
│ JSON lines over vesktop.sock
|
||||
▼
|
||||
Vesktop main process
|
||||
│ sendRendererCommand()
|
||||
▼
|
||||
Discord renderer (Vencord patched)
|
||||
│ official client actions
|
||||
▼
|
||||
Discord
|
||||
```
|
||||
|
||||
## Guardrails for agents working here
|
||||
|
||||
- **Never commit tokens, credentials, or `.env` files.** Use environment variables / a local secret store.
|
||||
- Do not adopt archived/deprecated selfbot libraries. If a library is needed, verify it was pushed
|
||||
recently before proposing it.
|
||||
- Do not hardcode Discord limits from memory — verify current free-tier numbers (upload size, stream
|
||||
resolution, rate limits) at implementation time, and keep them configurable.
|
||||
- All outbound actions go through the pacing queue; no direct sends.
|
||||
- Default to conservative limits and opt-in risky features.
|
||||
- Test against throwaway accounts and private test guilds only.
|
||||
- **Live send tests:** DMs to user **zzib** (`177117296140091393`) are the only sanctioned real-send
|
||||
test target.
|
||||
- Run `pnpm testTypes` and `pnpm lint` before calling work done; `pnpm build` to compile.
|
||||
|
||||
## Open questions
|
||||
|
||||
- What to strip to lighten the client once the daemon path is stable.
|
||||
- Voice/video: what the client's native stack can be driven to do without UI interaction.
|
||||
- Scope and auth model of the future remote proxy client and web debug UI.
|
||||
- How much of the Vesktop Electron tree gets deleted once the daemon is self-sufficient.
|
||||
1. Wire the core bridge: `events.ts`, `index.ts`, `puppetBridge.ts`, and the upstream hooks.
|
||||
2. Re-add auth persistence: pinned `DATA_DIR`, token file, preload seeding, `VESKSLOP_HEADLESS`.
|
||||
3. Move the daemon to the core-bridge transport.
|
||||
4. Add a mock core for tests.
|
||||
5. Later: voice actions, audio, video, fetch-based read, client lightening, web debug UI.
|
||||
|
||||
## Dev commands
|
||||
|
||||
@@ -133,4 +68,8 @@ pnpm install
|
||||
pnpm build # esbuild bundle into dist/
|
||||
pnpm testTypes # tsc --noEmit
|
||||
pnpm lint
|
||||
|
||||
cd poc
|
||||
pnpm check # type check the daemon
|
||||
pnpm test:comm # unit tests
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user