Files
Veskslop-t/docs/communication.md

239 lines
8.9 KiB
Markdown

# Communication
This document describes the trigger rules, trust tiers, commands, the trigger log, context, the
continuation window, and the response policy.
## Trigger rules
A message is a trigger when both conditions are true:
- The author is trusted. Each tier counts as trusted.
- One of these conditions is true:
- The message is a one-to-one DM to the puppet.
- The message mentions the puppet in a guild channel or a group DM.
- The message replies to a message the puppet sent.
All other messages are ignored. Untrusted messages do not enter the log at this time. Bots and the
puppet's own messages are ignored.
The daemon processes events one at a time. This order matters for continuation windows.
## Trust tiers
| Tier | Meaning |
| --- | --- |
| `user` | Can trigger the puppet. This is the default tier. |
| `trusted` | Can trigger the puppet. Future chat commands need this tier. |
| `admin` | Can manage the trust list and the config. The owner is always an admin. |
The owner controls the trust list. Commands from other users do not change the list.
## Commands
The owner sends commands from Discord. In a DM, the text is the command. In a guild or a group DM,
mention the puppet first.
Every command starts with the **wakeword** and a space: the default is `!s`, so
`!s whitelist list`. The wakeword must sit at the start of the message, and a space must follow it.
`whitelist list` alone, `!space`, and `hey!s whitelist list` are not commands. Settings → Commands
changes the wakeword: presets near the WASD keys plus `s`, or a custom string of 1-8 characters
with no spaces (the `!` prefill can become any symbol or letter). The match ignores letter case.
A trusted message without the wakeword goes to the reply-only agent as free chat.
| Command | Effect |
| --- | --- |
| `whitelist list` | Show the owner and the trusted users with their tiers. |
| `whitelist add @user [user\|trusted\|admin]` | Add one or more users. The default tier is `user`. |
| `whitelist remove @user` | Remove one or more users from the trust list. |
| `speak <text>` | Speak text into the bot's current voice call. `speak stop` stops it. |
| `poll ...` | Start a reaction poll. Paused at this time. |
| `stay @user` | Planned with the voice backend. Pin the bot to a voice user. See [voice.md](voice.md). |
Notes:
- The owner cannot be removed.
- The command accepts mentions and raw user IDs.
- `whitelist` commands need the admin tier.
- A bare `whitelist` or `whitelist help` shows the help.
- The reply follows the response policy.
## Polls
**Paused.** `POLL_ENABLED` is `false` in `poc/src/puppet.ts`. The parser, the tests, and the code
stay in place. The daemon does not run a `poll` command.
The `poll` command posts a message and adds one number emoji per option. Discord users vote by
reacting. The poll closes after `pollSeconds` (default 120) and the daemon posts the result.
Format:
```
poll "Question" Option 1 // Option 2 [// Option 3 [// Option 4]]
poll Question | Option 1 | Option 2
```
- The question can be in quotes. Then the first option follows after the closing quote.
- Separators are `|` or `//`. The `//` separator needs whitespace on both sides. This keeps URLs
such as `https://example.com` in one piece.
- A bare `poll` or `poll help` shows the help.
- Options: 2 to 4. Each option is at most 80 characters.
Voting:
- Emojis: `1️⃣`, `2️⃣`, `3️⃣`, `4️⃣`.
- Multiple reactions per user are allowed. Each reaction counts.
- Removing a reaction removes that one selection.
- The puppet's own reactions do not count.
- Votes do not need a trust tier. Friends can vote.
- The result message goes to the poll channel.
Examples:
```
@puppet poll "What country is best" Sweden // Denmark // Madagaskar
@puppet poll Lunch? | Pizza | Sushi
```
## Response policy
The policy decides where a reply goes. Sensitive replies go by DM.
| Setting | Meaning | Default |
| --- | --- | --- |
| `defaultMode` | The mode for normal replies: `channel` or `dm` | `channel` |
| `sensitiveCommands` | Commands that always reply by DM | `whitelist`, `config` |
| `overrides` | A per-command mode | empty |
Rules:
- An override wins over the sensitive list. The sensitive list wins over the default mode.
- A DM reply failure does not fall back to the origin channel. The daemon logs the error only.
- Read the policy with `get_puppet_config`. Change it with `set_puppet_config`.
## Trigger log
The log has two ring buffers.
| Buffer | Limit | Content |
| --- | --- | --- |
| Trusted | 100 entries | Triggers from trusted users |
| Untrusted | 30 entries | Reserved. No triggers enter this buffer at this time. |
One entry holds these fields:
| Field | Meaning |
| --- | --- |
| `id` | The message ID |
| `channelId` | The channel ID |
| `guildId` | The guild ID, or absent for a DM |
| `authorId` | The author user ID |
| `authorName` | The author username |
| `content` | The raw message content |
| `timestamp` | The message timestamp |
| `trusted` | True for a trusted author |
| `command` | The parsed command text, when the message looks like a command |
| `context` | Prior messages. Present on mention triggers. Follow-ups join this list. |
## Context
The daemon collects prior messages for a mention trigger. Direct messages do not need context.
- Message limit: `contextMessages`, default 15.
- Character limit: `contextChars`, default 4000.
- The lower limit stops the collection.
- Each line holds a time stamp, the author, the message text, and attachment file names.
- Media bodies stay on Discord.
- The trigger message is not part of its own context.
The cache makes the fetch small.
1. The daemon reads the newest cached message ID for the channel.
2. It fetches messages after that ID. On the first run, it fetches the newest messages.
3. It merges the new messages into the cache.
4. It assembles the context from the merged list.
The fetch cooldown stops a burst of queries. The setting `fetchCooldownSeconds` (default 5) is the
minimum gap between two fetches for one channel.
Cache settings:
| Setting | Default |
| --- | --- |
| `VESKSLOP_CACHE_DIR` | `<tmp>/veskslop-cache` |
| `VESKSLOP_CACHE_MAX_BYTES` | 50 MiB |
| Messages per channel | 200 |
Eviction removes the least recently written channel files until the folder fits the size limit.
## Continuation window
A user can send a second message right after a request. The second message often adds a missing
detail. The daemon appends such messages to the request context.
- Window: `continuationSeconds`, default 10.
- The window starts at the trigger and resets on each follow-up.
- Only messages from the same author in the same channel count.
- A follow-up does not need a mention.
- A follow-up needs a trusted author.
## Action log
The daemon writes a persistent log beside the state file: `actions.jsonl`. One JSON line holds one
event. The log rotates at the size limit.
| Event | Content |
| --- | --- |
| `trigger` | Message ID, channel, guild, author, tier, command, context count |
| `continuation` | Follow-up message ID and the parent entry ID |
| `command` | Command text, requester, result |
| `reply` | Command, mode, target, result, error |
| `voice` | Voice action, channel, mute |
| `media` | Scrape start or stop: scrape ID, channel, mode |
| `audio` | Audio action (speak, stop, devices, capture), provider, size |
| `message-edit` | Message ID, channel, author, content length, whether the cache had it |
| `message-delete` | Message ID, channel, whether the cache had it |
| `reaction` | Add or remove, message, channel, user, emoji (trusted users only) |
| `manual` | Web UI send: mode, channel or user, content length |
| `error` | Scope and message |
Secret-looking strings are redacted before the write. Discord token shapes and `mfa.` strings
become `[redacted]`.
Settings:
| Setting | Default |
| --- | --- |
| `VESKSLOP_ACTION_LOG` | `<state dir>/actions.jsonl` |
| `VESKSLOP_ACTION_LOG_MAX_BYTES` | 5 MiB |
Read the log with the `get_action_log` MCP tool.
## Event flow
1. The client receives a `MESSAGE_CREATE` gateway event.
2. The core normalizes the event.
3. The core bridge pushes the event to the daemon.
4. The daemon queues the event. Events run one at a time.
5. The daemon drops a replayed event.
6. The daemon applies the trigger rules.
7. The daemon collects context for a mention trigger.
8. The daemon adds the entry to the log and writes the action log.
9. The daemon runs a trusted command and replies through the response policy.
## State file
The daemon stores the owner ID, the trust list, and the config in JSON.
- Default path: `poc/.veskslop-state.json`.
- Override: `VESKSLOP_STATE_FILE`.
- The trust list is an object: user ID to tier.
- The trigger log is in memory only. The action log is persistent.
## How to add a trigger rule or a command
1. Add the rule in `poc/src/puppet.ts`, in `handleEvent`.
2. Keep the rule small and testable.
3. Add a unit test in `poc/src/test-communication.ts` when the rule is pure logic.
4. Run `pnpm test:comm` and `pnpm test:cache`.