Add text features: trusted triggers, context cache, response policy, MCP config tools
This commit is contained in:
@@ -54,6 +54,7 @@ when the action list changes.
|
||||
| QR login | `poc/src/qr-login.ts` | Implemented and verified live |
|
||||
| Pacing | `poc/src/pacing.ts` | Implemented |
|
||||
| Trigger log and trust list | `poc/src/communication.ts`, `poc/src/puppet.ts` | Implemented and verified live |
|
||||
| Context cache and response policy | `poc/src/message-cache.ts`, `poc/src/puppet.ts` | Implemented and verified with the mock |
|
||||
| Voice, audio, video | Not started | Not started |
|
||||
|
||||
## Data flow
|
||||
|
||||
+67
-20
@@ -1,30 +1,32 @@
|
||||
# Communication
|
||||
|
||||
This document describes the trigger rules, the trust model, the commands, and the trigger log.
|
||||
This document describes the trigger rules, the trust model, the commands, the trigger log, the
|
||||
context cache, and the response policy.
|
||||
|
||||
## Trigger rules
|
||||
|
||||
A message is a trigger when one of these conditions is true:
|
||||
A message is a trigger when both conditions are true:
|
||||
|
||||
- The message is a direct message to the puppet. A direct message is a one-to-one DM, not a group.
|
||||
- The message mentions the puppet. This applies in guild channels and group DMs.
|
||||
- The author is trusted. The owner 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.
|
||||
|
||||
All other messages are ignored.
|
||||
All other messages are ignored. Untrusted messages do not enter the log at this time. The daemon
|
||||
ignores bots and its own messages.
|
||||
|
||||
## Trust model
|
||||
|
||||
The puppet has one owner and a trust list.
|
||||
|
||||
- The owner is always trusted. The default owner is `177117296140091393` (zzib).
|
||||
- The trust list holds additional user IDs.
|
||||
- A trusted user gets the 100-entry log. An untrusted user gets the 30-entry log.
|
||||
|
||||
The owner controls the trust list. Commands from other users do not change the list.
|
||||
- The trust list holds more user IDs.
|
||||
- The owner controls the trust list. Commands from other users do not change the list.
|
||||
|
||||
## Commands
|
||||
|
||||
The owner sends commands from Discord. In a direct message, the text is the command. In a guild or
|
||||
a group DM, mention the puppet first.
|
||||
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.
|
||||
|
||||
| Command | Effect |
|
||||
| --- | --- |
|
||||
@@ -36,7 +38,23 @@ Notes:
|
||||
|
||||
- The owner cannot be removed.
|
||||
- The command accepts mentions and raw user IDs.
|
||||
- The daemon replies with a confirmation. The reply uses the same pacing as all other sends.
|
||||
- The reply follows the response policy.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -45,7 +63,7 @@ The log has two ring buffers.
|
||||
| Buffer | Limit | Content |
|
||||
| --- | --- | --- |
|
||||
| Trusted | 100 entries | Triggers from the owner and trusted users |
|
||||
| Untrusted | 30 entries | Triggers from all other users |
|
||||
| Untrusted | 30 entries | Reserved. No triggers enter this buffer at this time. |
|
||||
|
||||
One entry holds these fields:
|
||||
|
||||
@@ -59,28 +77,57 @@ One entry holds these fields:
|
||||
| `content` | The raw message content |
|
||||
| `timestamp` | The message timestamp |
|
||||
| `trusted` | True for a trusted author |
|
||||
| `command` | The parsed command text, when the message is a command |
|
||||
| `command` | The parsed command text, when the message looks like a command |
|
||||
| `context` | Prior messages. Present on mention triggers. |
|
||||
|
||||
## 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.
|
||||
- The content is the message text and the 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.
|
||||
|
||||
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.
|
||||
|
||||
## Event flow
|
||||
|
||||
1. The client receives a `MESSAGE_CREATE` gateway event.
|
||||
2. The core renderer normalizes the event.
|
||||
2. The core normalizes the event.
|
||||
3. The core bridge pushes the event to the daemon.
|
||||
4. The daemon applies the trigger rules.
|
||||
5. The daemon adds a qualifying message to the log.
|
||||
6. The daemon runs a trusted command, when the message is a command.
|
||||
5. The daemon collects context for a mention trigger.
|
||||
6. The daemon adds the entry to the log.
|
||||
7. The daemon runs a trusted command and replies through the response policy.
|
||||
|
||||
## State file
|
||||
|
||||
The daemon stores the owner ID and the trust list in JSON.
|
||||
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 trigger log is in memory only. The log resets at restart.
|
||||
|
||||
## How to add a trigger rule
|
||||
## 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`.
|
||||
4. Run `pnpm test:comm` and `pnpm test:cache`.
|
||||
|
||||
@@ -59,6 +59,7 @@ Event line:
|
||||
| `state.get` | none | The current user, the guilds, and the channels. Cache-only. |
|
||||
| `message.send` | `channelId`, `content` | `{ channelId, sent: true }` |
|
||||
| `message.read` | `channelId`, `limit` | `{ cached, messages }`. Cache-only. |
|
||||
| `message.fetch` | `channelId`, `limit`, optional `before` or `after` | `{ fetched, messages }`. Fetches from Discord. |
|
||||
| `dm.send` | `userId`, `content` | `{ userId, channelId, sent: true }` |
|
||||
|
||||
## Validation rules
|
||||
@@ -73,6 +74,8 @@ The core validates all input. The daemon cannot bypass these rules.
|
||||
`GUILD_TEXT`, `DM`, `GROUP_DM`, `GUILD_ANNOUNCEMENT`, `ANNOUNCEMENT_THREAD`, `PUBLIC_THREAD`,
|
||||
and `PRIVATE_THREAD`.
|
||||
- `limit` must be an integer from 1 to 100.
|
||||
- `before` and `after` must be snowflakes. They are mutually exclusive. `message.fetch` uses them
|
||||
to walk history.
|
||||
- A request line must not be larger than 64 KiB.
|
||||
|
||||
## Core rate limit
|
||||
@@ -100,6 +103,7 @@ The core rate limit is a safety net. It does not replace the daemon pacing.
|
||||
| `NOT_SENDABLE` | The channel type cannot receive messages. |
|
||||
| `SEND_FAILED` | Discord rejected the message. |
|
||||
| `DM_UNAVAILABLE` | The client cannot create a DM channel. |
|
||||
| `NOT_SUPPORTED` | The client does not offer the internal action. |
|
||||
| `RATE_LIMITED` | The core rate limit rejected the action. |
|
||||
| `INTERNAL` | An unexpected error occurred. The details go to the local log only. |
|
||||
|
||||
|
||||
+18
-2
@@ -7,13 +7,15 @@ The daemon runs outside Vesktop. It holds all logic. It talks to the core bridge
|
||||
| File | Role | Status |
|
||||
| --- | --- | --- |
|
||||
| `poc/src/control-bridge.ts` | MCP server, tool list, health endpoint | Implemented |
|
||||
| `poc/src/puppet.ts` | Trust list, command handling, trigger log | Implemented |
|
||||
| `poc/src/communication.ts` | Pure log and parser logic | Implemented |
|
||||
| `poc/src/puppet.ts` | Trust list, command handling, trigger log, response policy | Implemented |
|
||||
| `poc/src/communication.ts` | Pure log, parser, and context logic | Implemented |
|
||||
| `poc/src/message-cache.ts` | Channel message cache in a tmp folder | Implemented |
|
||||
| `poc/src/pacing.ts` | Paced queue and query coalescing | Implemented |
|
||||
| `poc/src/core-client.ts` | Client actions over the core bridge | Implemented (primary transport) |
|
||||
| `poc/src/cdp-client.ts` | Chrome DevTools Protocol client | Development fallback |
|
||||
| `poc/src/qr-login.ts` | Terminal QR login | Implemented and verified |
|
||||
| `poc/src/test-communication.ts` | Unit tests for the pure logic | Implemented |
|
||||
| `poc/src/test-cache.ts` | Unit tests for the message cache | Implemented |
|
||||
| `poc/src/mock-core.ts` | Mock core bridge for daemon tests | Implemented |
|
||||
| `poc/src/test-trigger.ts` | Inject a trigger event into the mock core | Implemented |
|
||||
| `poc/src/cdp-probe.ts` | CDP feasibility probe | Development tool |
|
||||
@@ -47,6 +49,9 @@ The daemon exposes MCP over Streamable HTTP. The default address is `http://127.
|
||||
| `read_messages` | Read cached messages from a channel. |
|
||||
| `send_dm` | Send a DM to a user. |
|
||||
| `get_message_log` | Read the trigger log. |
|
||||
| `get_context` | Assemble conversation context for a channel. Fetches new messages by default. |
|
||||
| `get_puppet_config` | Read the response policy and context settings. |
|
||||
| `set_puppet_config` | Change the response policy and context settings. |
|
||||
| `whitelist_list` | List the owner and the trusted users. |
|
||||
| `whitelist_add` | Add users to the trust list. |
|
||||
| `whitelist_remove` | Remove users from the trust list. |
|
||||
@@ -68,6 +73,7 @@ pnpm poc:dm
|
||||
|
||||
# Run the pure-logic unit tests.
|
||||
pnpm test:comm
|
||||
pnpm test:cache
|
||||
|
||||
# Development tools for the CDP fallback.
|
||||
pnpm cdp:probe
|
||||
@@ -82,6 +88,16 @@ CDP_EXPRESSION='...' pnpm cdp:dispatch
|
||||
{ "ok": true, "cdpConnected": true, "ownerId": "177117296140091393", "queries": 4 }
|
||||
```
|
||||
|
||||
## Settings and paths
|
||||
|
||||
| Setting | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `VESKSLOP_SOCKET` | `$XDG_RUNTIME_DIR/vesktop-puppet.sock` | Core bridge socket |
|
||||
| `VESKSLOP_STATE_FILE` | `poc/.veskslop-state.json` | Owner, trust list, and config |
|
||||
| `VESKSLOP_CACHE_DIR` | `<tmp>/veskslop-cache` | Context cache folder |
|
||||
| `VESKSLOP_CACHE_MAX_BYTES` | 50 MiB | Context cache size limit |
|
||||
| `MCP_HOST` / `MCP_PORT` | `127.0.0.1` / `8788` | MCP listen address |
|
||||
|
||||
## How to add an MCP tool
|
||||
|
||||
1. Add the action to the core bridge. See [core-bridge.md](core-bridge.md).
|
||||
|
||||
Reference in New Issue
Block a user