92 lines
3.8 KiB
Markdown
92 lines
3.8 KiB
Markdown
# 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
|
|
|
|
1. **The local harness (veskslop agent).** A small Go process on the same host as the daemon. It
|
|
talks to a remote OpenAI-compatible `llama.cpp` endpoint and to the daemon over MCP. It owns
|
|
free-form messages. It is the only consumer that runs an LLM loop.
|
|
2. **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_state` for 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 will push them (not implemented yet; the
|
|
scaffold accepts manual pushes). The task body:
|
|
|
|
```json
|
|
{
|
|
"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_LIMITED` backoff 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. Daemon push, tests, and the remote notification tools are pending.
|
|
|
|
## Run the scaffold
|
|
|
|
```sh
|
|
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.
|