Add text features: trusted triggers, context cache, response policy, MCP config tools

This commit is contained in:
Fisto
2026-09-11 15:31:23 +02:00
parent 23a1a8e593
commit 82f78bb2b4
20 changed files with 841 additions and 88 deletions
+65
View File
@@ -10,14 +10,23 @@ import {
MessageLog,
TRUSTED_LOG_LIMIT,
UNTRUSTED_LOG_LIMIT,
assembleContext,
compareSnowflakes,
extractMentionIds,
formatContextLine,
isTrusted,
parseCommand,
randomActionDelay,
stripMentions,
toCachedMessage,
type CachedMessage,
type PuppetMessage
} from "./communication.js";
function cached(id: number, content = `m${id}`): CachedMessage {
return { id: String(id), authorId: "1", authorName: "a", content, timestamp: "" };
}
function entry(id: number, trusted: boolean): PuppetMessage {
return {
id: String(id),
@@ -72,4 +81,60 @@ function entry(id: number, trusted: boolean): PuppetMessage {
}
}
// context assembly
{
const messages = Array.from({ length: 20 }, (_, i) => cached(100 + i));
const byCount = assembleContext(messages, { maxMessages: 15, maxChars: 1_000_000 });
assert.equal(byCount.messages.length, 15);
assert.equal(byCount.messages[0].id, "105");
assert.equal(byCount.truncated, true);
const long = Array.from({ length: 10 }, (_, i) => cached(i, "y".repeat(50)));
const byChars = assembleContext(long, { maxMessages: 15, maxChars: 120 });
assert.equal(byChars.messages.length, 2);
assert.ok(byChars.chars <= 120);
assert.equal(byChars.truncated, true);
// the newest message is always kept, even when it exceeds the budget
const single = assembleContext([cached(1, "z".repeat(5000))], { maxChars: 100 });
assert.equal(single.messages.length, 1);
// the trigger message can be excluded
const excluded = assembleContext([cached(1), cached(2), cached(3)], { excludeId: "3" });
assert.deepEqual(
excluded.messages.map(m => m.id),
["1", "2"]
);
}
// context formatting and snowflake compare
{
const withFile: CachedMessage = { ...cached(1, "look"), attachments: ["clip.mp4"] };
assert.equal(formatContextLine(withFile), "a: look [file: clip.mp4]");
assert.equal(formatContextLine({ ...cached(2, ""), attachments: ["a.png"] }), "a: [file: a.png]");
assert.equal(compareSnowflakes("10", "9"), 1);
assert.equal(compareSnowflakes("9", "10"), -1);
assert.equal(compareSnowflakes("5", "5"), 0);
}
// raw message conversion keeps file names only
{
const converted = toCachedMessage({
id: 5,
author: { id: 7, username: "u" },
content: "see this",
timestamp: "t",
attachments: [{ filename: "a.png" }, { filename: "b.mp4" }]
});
assert.deepEqual(converted, {
id: "5",
authorId: "7",
authorName: "u",
content: "see this",
timestamp: "t",
attachments: ["a.png", "b.mp4"]
});
}
console.log("all communication tests passed");