diff --git a/package-lock.json b/package-lock.json
index 798b4f0..4b4957b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"": {
"name": "lettabot",
"version": "1.0.0",
+ "hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@clack/prompts": "^0.11.0",
diff --git a/src/core/bot.ts b/src/core/bot.ts
index 7f914fb..92d9903 100644
--- a/src/core/bot.ts
+++ b/src/core/bot.ts
@@ -443,6 +443,15 @@ export class LettaBot {
// Helper to finalize and send current accumulated response
const finalizeMessage = async () => {
+ // Check for silent marker - agent chose not to reply
+ if (response.trim() === '') {
+ console.log('[Bot] Agent chose not to reply (no-reply marker)');
+ sentAnyMessage = true;
+ response = '';
+ messageId = null;
+ lastUpdate = Date.now();
+ return;
+ }
if (response.trim()) {
try {
if (messageId) {
@@ -521,8 +530,10 @@ export class LettaBot {
response += streamMsg.content;
// Stream updates only for channels that support editing (Telegram, Slack)
+ // Hold back streaming edits while response could still become
const canEdit = adapter.supportsEditing?.() ?? true;
- if (canEdit && Date.now() - lastUpdate > 500 && response.length > 0) {
+ const mayBeNoReply = ''.startsWith(response.trim());
+ if (canEdit && !mayBeNoReply && Date.now() - lastUpdate > 500 && response.length > 0) {
try {
if (messageId) {
await adapter.editMessage(msg.chatId, messageId, response);
@@ -600,6 +611,13 @@ export class LettaBot {
clearInterval(typingInterval);
}
+ // Check for silent marker - agent chose not to reply
+ if (response.trim() === '') {
+ console.log('[Bot] Agent chose not to reply (no-reply marker)');
+ sentAnyMessage = true;
+ response = '';
+ }
+
// Send final response
if (response.trim()) {
try {
diff --git a/src/core/formatter.test.ts b/src/core/formatter.test.ts
index 21bdbab..793aea2 100644
--- a/src/core/formatter.test.ts
+++ b/src/core/formatter.test.ts
@@ -180,6 +180,18 @@ describe('formatMessageEnvelope', () => {
const result = formatMessageEnvelope(msg);
expect(result).toContain('**Mentioned**: yes');
});
+
+ it('includes no-reply hint for group chats', () => {
+ const msg = createMessage({ isGroup: true });
+ const result = formatMessageEnvelope(msg);
+ expect(result).toContain('');
+ });
+
+ it('omits no-reply hint for DMs', () => {
+ const msg = createMessage({ isGroup: false });
+ const result = formatMessageEnvelope(msg);
+ expect(result).not.toContain('no-reply');
+ });
});
describe('format hints', () => {
diff --git a/src/core/formatter.ts b/src/core/formatter.ts
index 1f110cb..2cdaf45 100644
--- a/src/core/formatter.ts
+++ b/src/core/formatter.ts
@@ -231,6 +231,7 @@ function buildChatContextLines(msg: InboundMessage, options: EnvelopeOptions): s
if (msg.wasMentioned) {
lines.push(`- **Mentioned**: yes`);
}
+ lines.push(`- **Hint**: To skip replying, respond with exactly: \`\``);
} else {
lines.push(`- **Type**: Direct message`);
}
diff --git a/src/core/system-prompt.ts b/src/core/system-prompt.ts
index f064232..c71883f 100644
--- a/src/core/system-prompt.ts
+++ b/src/core/system-prompt.ts
@@ -6,6 +6,8 @@ export const SYSTEM_PROMPT = `You are a self-improving AI agent with advanced me
You are connected to a multi-channel messaging system (LettaBot) that allows you to communicate with users across Telegram, Slack, Discord, WhatsApp, and Signal. You run on a remote server and can execute tools, manage files, and interact with various services.
+Not every message requires a response. Before replying, consider whether your response adds value. In group chats especially, avoid replying to messages not directed at you, simple acknowledgments, or conversations between other users. Quality over quantity — only reply when you have something meaningful to contribute.
+
# Communication System
You communicate through multiple channels and trigger types. Understanding when your messages are delivered is critical:
@@ -68,6 +70,21 @@ During heartbeats and background tasks:
You don't need to notify the user about everything. Use judgment about what's worth interrupting them for.
+## Choosing Not to Reply
+
+Not all messages warrant a response. If a message doesn't need a reply, respond with exactly:
+
+\`\`
+
+This suppresses the message so nothing is sent to the user. Use this for:
+- Messages in a group not directed at you
+- Simple acknowledgments (e.g., "ok", "thanks", thumbs up)
+- Conversations between other users you don't need to join
+- Notifications or updates that don't require a response
+- Messages you've already addressed
+
+When in doubt, prefer \`\` over a low-value response. Users appreciate an agent that knows when to stay quiet.
+
## Available Channels
- **telegram** - Telegram messenger