4.9 KiB
The Veskslop agent
The agent is the local LLM harness. It lets a remote llama.cpp instance act through the daemon.
It is separate from the core and from the daemon, and it is optional.
Two consumers
- The local harness (veskslop agent). A small Go process on the same host as the daemon. It
talks to a remote OpenAI-compatible
llama.cppendpoint and to the daemon over MCP. It owns free-form messages. It is the only consumer that runs an LLM loop. - Remote agents. External systems connect to the daemon's MCP endpoint and use it as a sensor
and notifier. Example: a task runner wants to reboot a switch, calls
get_voice_statefor the owner, sees the owner is in a call, and reschedules. Remote agents do not run an LLM here and do not receive every message. They query state and send notifications.
Routing: commands versus free-form
The daemon keeps the fixed commands (whitelist, speak) and the poll parser. A trusted
free-form message becomes a task for the local harness. Commands never go to the LLM; this avoids
double replies and keeps privileged actions out of model reach.
Local harness design
| Piece | Choice |
|---|---|
| Language | Go |
| Agent framework | Eino (github.com/cloudwego/eino), ReAct agent |
| Model | eino-ext/components/model/openai against a remote llama.cpp /v1 endpoint |
| Tools | eino-ext/components/tool/mcp/officialmcp over the official MCP Go SDK, Streamable HTTP |
| Tool surface | The daemon's MCP tools. Nothing else. |
| Entry | agent/ |
| Kill switch | VESKSLOP_AGENT_ENABLED=1 is required. Default is off. |
| Auth | VESKSLOP_AGENT_TOKEN bearer token on the task endpoint. |
| Budget | VESKSLOP_AGENT_MAX_STEPS (default 16). Per-task wall clock: 5 minutes. |
The harness receives tasks on POST /task. The daemon pushes them when VESKSLOP_AGENT_ENABLED=1
is set: a trusted free-form message (no wakeword) becomes a task, and the harness returns the reply
text. The daemon sends that text through the normal paced path, so the action log and the response
policy still apply.
Respond-only mode. The daemon sends "mode": "respond" in the task body. The harness must
answer with one completion and no tool calls, and it must return {"reply": "..."} in the HTTP
response. Tool calling waits for a later stage.
| Variable | Meaning |
|---|---|
VESKSLOP_AGENT_ENABLED=1 |
Turn the daemon-side push on. Default off. |
VESKSLOP_AGENT_URL |
Harness base URL. Default http://127.0.0.1:8790. |
VESKSLOP_AGENT_TOKEN |
Optional bearer token for POST /task. |
VESKSLOP_AGENT_TIMEOUT_MS |
Request timeout. Default 120000. |
The task body:
{
"id": "trigger-id",
"guildId": "…",
"channelId": "…",
"author": "…",
"content": "the trigger text",
"sensitive": false,
"context": [{ "author": "…", "content": "…", "timestamp": "…" }]
}
The model replies through the daemon's own send tools, so pacing, the rate limit, and the action log apply as for any other sender. The harness never calls Discord directly.
Safety rules
- Tool results and message context are untrusted. The system prompt says so explicitly.
- The harness is stateless per task. Context comes from the daemon each time.
- The model cannot run fixed commands. It only has MCP tools.
- Planned, not implemented: a tool allowlist, per-conversation serialization with
cancel-and-replace, delivery IDs for dedupe, and
RATE_LIMITEDbackoff beyond one retry.
Remote agent path
The daemon already exposes its tools over MCP (Streamable HTTP). Remote clients can:
- read state (
get_voice_state,get_voice_overview,get_user_status,list_dms, …) - notify (
send_dm,send_message,speak)
Planned work: a small notification tool set for external systems (notify_owner), a documented
LAN or Tailscale bind, and optional shared-token auth on the MCP listener. The listener currently
binds localhost.
Status
Scaffold implemented in agent/ and smoke-tested on 2026-09-14: it loaded 28 tools from the
daemon, ran one ReAct tool round against a fake OpenAI-compatible endpoint, and produced the final
message.
The daemon side is in place (2026-09-22): the wakeword separates commands from chat, and
AgentClient posts free-form tasks in respond-only mode. The Go harness still needs the
respond-only mode branch and the reply response. The Go toolchain is not installed on the
current host, so the harness change waits for it. Remote notification tools are still pending.
Run the scaffold
cd agent
VESKSLOP_AGENT_ENABLED=1 \
VESKSLOP_AGENT_LLAMA_URL=http://llama-host:8080/v1 \
VESKSLOP_AGENT_MODEL=your-model \
VESKSLOP_AGENT_MCP_URL=http://127.0.0.1:8788/mcp \
go run . --task "Who is in the call right now?"
Build a binary with go build -o veskstop-agent .. The Go toolchain is not part of the root
pnpm workspace; run go build and go vet in agent/ directly.