@fortressllm/sybil 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env copy +91 -0
- package/.env.example +139 -0
- package/BROWSER_CONTROL.md +354 -0
- package/CLI_CHAT_FEATURE.md +224 -0
- package/CLI_GUIDE.md +359 -0
- package/DYNAMIC_SKILLS.md +345 -0
- package/DockerFile.sandbox +14 -0
- package/PROGRESS.md +249 -0
- package/README.md +281 -0
- package/RENAME_LOG.md +62 -0
- package/SIMPLIFIED_TELEGRAM_UX.md +273 -0
- package/SYBIL_SUMMARY.md +360 -0
- package/TASK11_NETWORK.md +202 -0
- package/TASK14_CLI.md +432 -0
- package/TASK8_SAFETY.md +317 -0
- package/TASK9_COMPLETION.md +186 -0
- package/TASK9_SUMMARY.md +201 -0
- package/TELEGRAM_OTP_AUTH.md +359 -0
- package/VECTOR_MEMORY.md +163 -0
- package/assets/logo.png +0 -0
- package/cypfq_code_search.md +287 -0
- package/cypfq_driver_search.md +297 -0
- package/cypfq_github_search.md +297 -0
- package/cypfq_repo_search.md +370 -0
- package/dist/agents/autonomous-agent.d.ts +61 -0
- package/dist/agents/autonomous-agent.d.ts.map +1 -0
- package/dist/agents/autonomous-agent.js +536 -0
- package/dist/agents/autonomous-agent.js.map +1 -0
- package/dist/agents/network.d.ts +1006 -0
- package/dist/agents/network.d.ts.map +1 -0
- package/dist/agents/network.js +1266 -0
- package/dist/agents/network.js.map +1 -0
- package/dist/cli/commands/backup.d.ts +3 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +63 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +163 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +107 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +138 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/logs.d.ts +3 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +81 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/otp.d.ts +3 -0
- package/dist/cli/commands/otp.d.ts.map +1 -0
- package/dist/cli/commands/otp.js +142 -0
- package/dist/cli/commands/otp.js.map +1 -0
- package/dist/cli/commands/restore.d.ts +3 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +99 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/start.d.ts +3 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +65 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +68 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +62 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/update.d.ts +3 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +49 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/commands/whatsapp.d.ts +3 -0
- package/dist/cli/commands/whatsapp.d.ts.map +1 -0
- package/dist/cli/commands/whatsapp.js +281 -0
- package/dist/cli/commands/whatsapp.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +58 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +750 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/mastra/index.d.ts +4 -0
- package/dist/mastra/index.d.ts.map +1 -0
- package/dist/mastra/index.js +37 -0
- package/dist/mastra/index.js.map +1 -0
- package/dist/mastra/memory.d.ts +9 -0
- package/dist/mastra/memory.d.ts.map +1 -0
- package/dist/mastra/memory.js +92 -0
- package/dist/mastra/memory.js.map +1 -0
- package/dist/processors/index.d.ts +74 -0
- package/dist/processors/index.d.ts.map +1 -0
- package/dist/processors/index.js +153 -0
- package/dist/processors/index.js.map +1 -0
- package/dist/processors/semantic-recall.d.ts +63 -0
- package/dist/processors/semantic-recall.d.ts.map +1 -0
- package/dist/processors/semantic-recall.js +216 -0
- package/dist/processors/semantic-recall.js.map +1 -0
- package/dist/processors/tool-search.d.ts +26 -0
- package/dist/processors/tool-search.d.ts.map +1 -0
- package/dist/processors/tool-search.js +41 -0
- package/dist/processors/tool-search.js.map +1 -0
- package/dist/skills/dynamic/skill-generator.d.ts +169 -0
- package/dist/skills/dynamic/skill-generator.d.ts.map +1 -0
- package/dist/skills/dynamic/skill-generator.js +488 -0
- package/dist/skills/dynamic/skill-generator.js.map +1 -0
- package/dist/tools/agent-delegation-tools.d.ts +142 -0
- package/dist/tools/agent-delegation-tools.d.ts.map +1 -0
- package/dist/tools/agent-delegation-tools.js +263 -0
- package/dist/tools/agent-delegation-tools.js.map +1 -0
- package/dist/tools/browser-tools.d.ts +374 -0
- package/dist/tools/browser-tools.d.ts.map +1 -0
- package/dist/tools/browser-tools.js +752 -0
- package/dist/tools/browser-tools.js.map +1 -0
- package/dist/tools/dynamic/registry.d.ts +61 -0
- package/dist/tools/dynamic/registry.d.ts.map +1 -0
- package/dist/tools/dynamic/registry.js +121 -0
- package/dist/tools/dynamic/registry.js.map +1 -0
- package/dist/tools/dynamic/tool-generator.d.ts +99 -0
- package/dist/tools/dynamic/tool-generator.d.ts.map +1 -0
- package/dist/tools/dynamic/tool-generator.js +367 -0
- package/dist/tools/dynamic/tool-generator.js.map +1 -0
- package/dist/tools/extended-tools.d.ts +176 -0
- package/dist/tools/extended-tools.d.ts.map +1 -0
- package/dist/tools/extended-tools.js +464 -0
- package/dist/tools/extended-tools.js.map +1 -0
- package/dist/tools/library/calendar/index.d.ts +134 -0
- package/dist/tools/library/calendar/index.d.ts.map +1 -0
- package/dist/tools/library/calendar/index.js +160 -0
- package/dist/tools/library/calendar/index.js.map +1 -0
- package/dist/tools/podman-workspace-mcp-cli.d.ts +3 -0
- package/dist/tools/podman-workspace-mcp-cli.d.ts.map +1 -0
- package/dist/tools/podman-workspace-mcp-cli.js +12 -0
- package/dist/tools/podman-workspace-mcp-cli.js.map +1 -0
- package/dist/tools/podman-workspace-mcp.d.ts +247 -0
- package/dist/tools/podman-workspace-mcp.d.ts.map +1 -0
- package/dist/tools/podman-workspace-mcp.js +1093 -0
- package/dist/tools/podman-workspace-mcp.js.map +1 -0
- package/dist/tools/podman-workspace.d.ts +148 -0
- package/dist/tools/podman-workspace.d.ts.map +1 -0
- package/dist/tools/podman-workspace.js +682 -0
- package/dist/tools/podman-workspace.js.map +1 -0
- package/dist/tools/telegram-file-tools.d.ts +78 -0
- package/dist/tools/telegram-file-tools.d.ts.map +1 -0
- package/dist/tools/telegram-file-tools.js +294 -0
- package/dist/tools/telegram-file-tools.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +467 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +156 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/web-tools.d.ts +77 -0
- package/dist/tools/web-tools.d.ts.map +1 -0
- package/dist/tools/web-tools.js +416 -0
- package/dist/tools/web-tools.js.map +1 -0
- package/dist/tools/whatsapp-autoreply-tools.d.ts +118 -0
- package/dist/tools/whatsapp-autoreply-tools.d.ts.map +1 -0
- package/dist/tools/whatsapp-autoreply-tools.js +503 -0
- package/dist/tools/whatsapp-autoreply-tools.js.map +1 -0
- package/dist/tools/whatsapp-tools.d.ts +175 -0
- package/dist/tools/whatsapp-tools.d.ts.map +1 -0
- package/dist/tools/whatsapp-tools.js +566 -0
- package/dist/tools/whatsapp-tools.js.map +1 -0
- package/dist/utils/logger.d.ts +65 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +307 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/model-config.d.ts +73 -0
- package/dist/utils/model-config.d.ts.map +1 -0
- package/dist/utils/model-config.js +366 -0
- package/dist/utils/model-config.js.map +1 -0
- package/dist/utils/semantic-memory.d.ts +82 -0
- package/dist/utils/semantic-memory.d.ts.map +1 -0
- package/dist/utils/semantic-memory.js +189 -0
- package/dist/utils/semantic-memory.js.map +1 -0
- package/dist/utils/system.d.ts +2 -0
- package/dist/utils/system.d.ts.map +1 -0
- package/dist/utils/system.js +24 -0
- package/dist/utils/system.js.map +1 -0
- package/dist/utils/telegram-auth.d.ts +54 -0
- package/dist/utils/telegram-auth.d.ts.map +1 -0
- package/dist/utils/telegram-auth.js +146 -0
- package/dist/utils/telegram-auth.js.map +1 -0
- package/dist/utils/telegram.d.ts +7 -0
- package/dist/utils/telegram.d.ts.map +1 -0
- package/dist/utils/telegram.js +1494 -0
- package/dist/utils/telegram.js.map +1 -0
- package/dist/utils/whatsapp-client.d.ts +166 -0
- package/dist/utils/whatsapp-client.d.ts.map +1 -0
- package/dist/utils/whatsapp-client.js +722 -0
- package/dist/utils/whatsapp-client.js.map +1 -0
- package/dist/workflows/planner-workflow.d.ts +39 -0
- package/dist/workflows/planner-workflow.d.ts.map +1 -0
- package/dist/workflows/planner-workflow.js +165 -0
- package/dist/workflows/planner-workflow.js.map +1 -0
- package/dist/workflows/skill-builder-workflow.d.ts +16 -0
- package/dist/workflows/skill-builder-workflow.d.ts.map +1 -0
- package/dist/workflows/skill-builder-workflow.js +157 -0
- package/dist/workflows/skill-builder-workflow.js.map +1 -0
- package/dist/workspace/index.d.ts +23 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +64 -0
- package/dist/workspace/index.js.map +1 -0
- package/docs/README.md +140 -0
- package/docs/api/agents.md +481 -0
- package/docs/api/browser-tools.md +469 -0
- package/docs/api/memory.md +629 -0
- package/docs/architecture/agent-networks.md +586 -0
- package/docs/architecture/memory.md +579 -0
- package/docs/architecture/overview.md +436 -0
- package/docs/architecture/tools.md +637 -0
- package/docs/cli-tui.md +367 -0
- package/docs/guides/environment-variables.md +502 -0
- package/docs/guides/troubleshooting.md +882 -0
- package/docs/tutorials/agent-networks.md +432 -0
- package/docs/tutorials/dynamic-tools.md +469 -0
- package/docs/tutorials/getting-started.md +263 -0
- package/docs/tutorials/skills.md +561 -0
- package/docs/tutorials/web-browsing.md +329 -0
- package/mastra.db-shm +0 -0
- package/mastra.db-wal +0 -0
- package/package.json +71 -0
- package/plan.md +601 -0
- package/skills/code-review/SKILL.md +48 -0
- package/skills/task-planning/SKILL.md +55 -0
- package/skills/web-research/SKILL.md +79 -0
- package/skills/whatsapp-management/SKILL.md +78 -0
- package/src/agents/autonomous-agent.ts +626 -0
- package/src/agents/network.ts +1307 -0
- package/src/cli/commands/backup.ts +78 -0
- package/src/cli/commands/config.ts +176 -0
- package/src/cli/commands/doctor.ts +111 -0
- package/src/cli/commands/init.ts +150 -0
- package/src/cli/commands/logs.ts +94 -0
- package/src/cli/commands/otp.ts +162 -0
- package/src/cli/commands/restore.ts +118 -0
- package/src/cli/commands/start.ts +76 -0
- package/src/cli/commands/status.ts +81 -0
- package/src/cli/commands/stop.ts +68 -0
- package/src/cli/commands/update.ts +61 -0
- package/src/cli/commands/whatsapp.ts +322 -0
- package/src/cli/index.ts +69 -0
- package/src/cli.ts +830 -0
- package/src/index.ts +124 -0
- package/src/mastra/index.ts +49 -0
- package/src/mastra/memory.ts +99 -0
- package/src/mastra/public/workspace/plan.md +115 -0
- package/src/mastra/public/workspace/research/react-tailwind/skill.md +47 -0
- package/src/processors/index.ts +170 -0
- package/src/processors/semantic-recall.ts +277 -0
- package/src/processors/tool-search.ts +46 -0
- package/src/skills/dynamic/skill-generator.ts +568 -0
- package/src/tools/agent-delegation-tools.ts +301 -0
- package/src/tools/browser-tools.ts +792 -0
- package/src/tools/dynamic/registry.ts +144 -0
- package/src/tools/dynamic/tool-generator.ts +406 -0
- package/src/tools/extended-tools.ts +498 -0
- package/src/tools/library/calendar/index.ts +172 -0
- package/src/tools/podman-workspace-mcp-cli.ts +14 -0
- package/src/tools/podman-workspace-mcp.ts +1290 -0
- package/src/tools/podman-workspace.ts +858 -0
- package/src/tools/telegram-file-tools.ts +320 -0
- package/src/tools/tool-registry.ts +233 -0
- package/src/tools/web-tools.ts +461 -0
- package/src/tools/whatsapp-autoreply-tools.ts +616 -0
- package/src/tools/whatsapp-tools.ts +602 -0
- package/src/utils/logger.ts +368 -0
- package/src/utils/model-config.ts +437 -0
- package/src/utils/semantic-memory.ts +230 -0
- package/src/utils/system.ts +25 -0
- package/src/utils/telegram-auth.ts +201 -0
- package/src/utils/telegram.ts +1847 -0
- package/src/utils/whatsapp-client.ts +808 -0
- package/src/workflows/planner-workflow.ts +178 -0
- package/src/workflows/skill-builder-workflow.ts +175 -0
- package/src/workspace/index.ts +69 -0
- package/tsconfig.json +22 -0
- package/view-logs.sh +116 -0
- package/whatsapp-session.sh +197 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import { createTool } from "@mastra/core/tools";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import "dotenv/config";
|
|
4
|
+
|
|
5
|
+
// NOTE: whatsappManager and mastra are imported lazily (via dynamic import)
|
|
6
|
+
// inside the functions that need them to avoid circular dependencies.
|
|
7
|
+
// whatsapp-client.ts imports from this file, and mastra/index.ts imports
|
|
8
|
+
// autonomous-agent.ts which transitively imports from this file.
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Interface for queued messages
|
|
12
|
+
*/
|
|
13
|
+
interface QueuedMessage {
|
|
14
|
+
id: string;
|
|
15
|
+
body: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
from: string;
|
|
18
|
+
senderName: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Interface for message queue per contact
|
|
23
|
+
*/
|
|
24
|
+
interface MessageQueue {
|
|
25
|
+
messages: QueuedMessage[];
|
|
26
|
+
timeoutId: NodeJS.Timeout | null;
|
|
27
|
+
lastActivity: number;
|
|
28
|
+
isProcessing: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Storage for auto-reply configuration
|
|
33
|
+
*/
|
|
34
|
+
interface AutoReplyConfig {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
mode: "manual" | "auto" | "smart";
|
|
37
|
+
whitelist: string[];
|
|
38
|
+
blacklist: string[];
|
|
39
|
+
maxRepliesPerHour: number;
|
|
40
|
+
replyDelayMs: number;
|
|
41
|
+
debounceMs: number; // Time to wait for inactivity (default: 2 minutes = 120000ms)
|
|
42
|
+
userContext: string;
|
|
43
|
+
customInstructions: string;
|
|
44
|
+
lastReplies: Map<string, number>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const defaultConfig: AutoReplyConfig = {
|
|
48
|
+
enabled: false,
|
|
49
|
+
mode: "manual",
|
|
50
|
+
whitelist: [],
|
|
51
|
+
blacklist: [],
|
|
52
|
+
maxRepliesPerHour: 10,
|
|
53
|
+
replyDelayMs: 3000,
|
|
54
|
+
debounceMs: 120000, // 2 minutes
|
|
55
|
+
userContext: "",
|
|
56
|
+
customInstructions: "",
|
|
57
|
+
lastReplies: new Map(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// In-memory config storage
|
|
61
|
+
let autoReplyConfig: AutoReplyConfig = { ...defaultConfig };
|
|
62
|
+
|
|
63
|
+
// Message queues per contact (phone number -> queue)
|
|
64
|
+
const messageQueues = new Map<string, MessageQueue>();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Add a message to the queue for debouncing
|
|
68
|
+
*/
|
|
69
|
+
export function queueMessage(
|
|
70
|
+
from: string,
|
|
71
|
+
message: QueuedMessage,
|
|
72
|
+
onDebounceComplete: (phoneNumber: string, messages: QueuedMessage[]) => void
|
|
73
|
+
): void {
|
|
74
|
+
const phoneNumber = from.replace(/\D/g, "").replace(/@c\.us$/, "");
|
|
75
|
+
|
|
76
|
+
// Get or create queue for this contact
|
|
77
|
+
let queue = messageQueues.get(phoneNumber);
|
|
78
|
+
if (!queue) {
|
|
79
|
+
queue = {
|
|
80
|
+
messages: [],
|
|
81
|
+
timeoutId: null,
|
|
82
|
+
lastActivity: Date.now(),
|
|
83
|
+
isProcessing: false,
|
|
84
|
+
};
|
|
85
|
+
messageQueues.set(phoneNumber, queue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add message to queue
|
|
89
|
+
queue.messages.push(message);
|
|
90
|
+
queue.lastActivity = Date.now();
|
|
91
|
+
|
|
92
|
+
console.log(`[WhatsApp Debouncer] Queued message from ${message.senderName}. Total: ${queue.messages.length} messages`);
|
|
93
|
+
|
|
94
|
+
// Clear existing timeout
|
|
95
|
+
if (queue.timeoutId) {
|
|
96
|
+
clearTimeout(queue.timeoutId);
|
|
97
|
+
queue.timeoutId = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Set new timeout for debouncing
|
|
101
|
+
queue.timeoutId = setTimeout(() => {
|
|
102
|
+
console.log(`[WhatsApp Debouncer] Debounce complete for ${message.senderName}. Processing ${queue!.messages.length} messages...`);
|
|
103
|
+
|
|
104
|
+
// Only process if not already processing and auto-reply is enabled
|
|
105
|
+
if (!queue!.isProcessing && autoReplyConfig.enabled) {
|
|
106
|
+
queue!.isProcessing = true;
|
|
107
|
+
onDebounceComplete(phoneNumber, [...queue!.messages]);
|
|
108
|
+
|
|
109
|
+
// Clear the queue after processing
|
|
110
|
+
queue!.messages = [];
|
|
111
|
+
queue!.isProcessing = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
queue!.timeoutId = null;
|
|
115
|
+
}, autoReplyConfig.debounceMs);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Cancel pending messages for a contact
|
|
120
|
+
*/
|
|
121
|
+
export function cancelPendingMessages(phoneNumber: string): void {
|
|
122
|
+
const queue = messageQueues.get(phoneNumber);
|
|
123
|
+
if (queue && queue.timeoutId) {
|
|
124
|
+
clearTimeout(queue.timeoutId);
|
|
125
|
+
queue.timeoutId = null;
|
|
126
|
+
queue.messages = [];
|
|
127
|
+
console.log(`[WhatsApp Debouncer] Cancelled pending messages for ${phoneNumber}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get pending messages for a contact
|
|
133
|
+
*/
|
|
134
|
+
export function getPendingMessages(phoneNumber: string): QueuedMessage[] {
|
|
135
|
+
const queue = messageQueues.get(phoneNumber);
|
|
136
|
+
return queue ? [...queue.messages] : [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if we should auto-reply to a contact
|
|
141
|
+
*/
|
|
142
|
+
export function shouldAutoReply(from: string): { shouldReply: boolean; reason?: string } {
|
|
143
|
+
if (!autoReplyConfig.enabled) {
|
|
144
|
+
return { shouldReply: false, reason: "Auto-reply is disabled" };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const phoneNumber = from.replace(/\D/g, "").replace(/@c\.us$/, "");
|
|
148
|
+
|
|
149
|
+
// Check blacklist
|
|
150
|
+
if (autoReplyConfig.blacklist.includes(phoneNumber)) {
|
|
151
|
+
return { shouldReply: false, reason: "Number is blacklisted" };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check whitelist (if whitelist is not empty, only reply to whitelisted numbers)
|
|
155
|
+
if (autoReplyConfig.whitelist.length > 0 && !autoReplyConfig.whitelist.includes(phoneNumber)) {
|
|
156
|
+
return { shouldReply: false, reason: "Number not in whitelist" };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check rate limit
|
|
160
|
+
const lastReply = autoReplyConfig.lastReplies.get(phoneNumber);
|
|
161
|
+
if (lastReply) {
|
|
162
|
+
const hoursSinceLastReply = (Date.now() - lastReply) / (1000 * 60 * 60);
|
|
163
|
+
if (hoursSinceLastReply < 1) {
|
|
164
|
+
const repliesThisHour = Array.from(autoReplyConfig.lastReplies.entries())
|
|
165
|
+
.filter(([num, time]) => num === phoneNumber && (Date.now() - time) < 3600000)
|
|
166
|
+
.length;
|
|
167
|
+
|
|
168
|
+
if (repliesThisHour >= autoReplyConfig.maxRepliesPerHour) {
|
|
169
|
+
return { shouldReply: false, reason: "Rate limit exceeded" };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { shouldReply: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Generate a smart reply using the agent based on batched messages
|
|
179
|
+
*/
|
|
180
|
+
export async function generateSmartReplyForBatchedMessages(
|
|
181
|
+
chatId: string,
|
|
182
|
+
messages: QueuedMessage[],
|
|
183
|
+
chatHistory: Array<{ body: string; fromMe: boolean }>
|
|
184
|
+
): Promise<{ reply: string; confidence: number; shouldSend: boolean; summary: string }> {
|
|
185
|
+
try {
|
|
186
|
+
// Lazy import to avoid circular dependency: this file → mastra/index → autonomous-agent → ... → this file
|
|
187
|
+
const { mastra } = await import("../mastra/index.js");
|
|
188
|
+
const agent = mastra.getAgent("autonomousAgent");
|
|
189
|
+
|
|
190
|
+
// Combine all batched messages into a single context
|
|
191
|
+
const batchedMessagesText = messages.map((msg, index) =>
|
|
192
|
+
`[${new Date(msg.timestamp).toLocaleTimeString()}] ${msg.senderName}: ${msg.body}`
|
|
193
|
+
).join("\n");
|
|
194
|
+
|
|
195
|
+
// Get the latest sender name
|
|
196
|
+
const senderName = messages[messages.length - 1]?.senderName || "Unknown";
|
|
197
|
+
|
|
198
|
+
// Build context from chat history
|
|
199
|
+
const recentHistory = chatHistory.slice(-10);
|
|
200
|
+
const conversationContext = recentHistory
|
|
201
|
+
.map((msg) => `${msg.fromMe ? "Me" : senderName}: ${msg.body}`)
|
|
202
|
+
.join("\n");
|
|
203
|
+
|
|
204
|
+
const prompt = `Generate a reply to these WhatsApp messages on my behalf. The sender may have sent multiple messages in sequence.
|
|
205
|
+
|
|
206
|
+
My Context:
|
|
207
|
+
${autoReplyConfig.userContext || "I'm a busy professional who values concise communication."}
|
|
208
|
+
|
|
209
|
+
Custom Instructions:
|
|
210
|
+
${autoReplyConfig.customInstructions || "Reply naturally and helpfully. Keep it brief unless detailed explanation is needed."}
|
|
211
|
+
|
|
212
|
+
Previous Conversation History:
|
|
213
|
+
${conversationContext}
|
|
214
|
+
|
|
215
|
+
Incoming Messages from ${senderName} (${messages.length} message${messages.length > 1 ? 's' : ''}):
|
|
216
|
+
${batchedMessagesText}
|
|
217
|
+
|
|
218
|
+
Generate a single comprehensive reply that addresses all the messages appropriately. The reply should:
|
|
219
|
+
1. Sound like me (based on my communication style)
|
|
220
|
+
2. Address all the key points from the messages
|
|
221
|
+
3. Be concise but helpful
|
|
222
|
+
4. Match the tone of the conversation
|
|
223
|
+
|
|
224
|
+
Provide your response in this format:
|
|
225
|
+
SUMMARY: [brief summary of what the sender is asking/saying]
|
|
226
|
+
REPLY: [your suggested reply]
|
|
227
|
+
CONFIDENCE: [0-100, how confident you are this is appropriate]
|
|
228
|
+
SHOULD_SEND: [yes/no, whether this should be sent without human review]`;
|
|
229
|
+
|
|
230
|
+
const response = await agent.generate(prompt);
|
|
231
|
+
|
|
232
|
+
// Parse the response
|
|
233
|
+
const summaryMatch = response.text.match(/SUMMARY:\s*(.+?)(?=\nREPLY:|$)/s);
|
|
234
|
+
const replyMatch = response.text.match(/REPLY:\s*(.+?)(?=\nCONFIDENCE:|$)/s);
|
|
235
|
+
const confidenceMatch = response.text.match(/CONFIDENCE:\s*(\d+)/);
|
|
236
|
+
const shouldSendMatch = response.text.match(/SHOULD_SEND:\s*(yes|no)/i);
|
|
237
|
+
|
|
238
|
+
const summary = summaryMatch ? summaryMatch[1].trim() : "New message received";
|
|
239
|
+
const reply = replyMatch ? replyMatch[1].trim() : "Thanks for your message! I'll get back to you soon.";
|
|
240
|
+
const confidence = confidenceMatch ? parseInt(confidenceMatch[1]) : 50;
|
|
241
|
+
const shouldSend = shouldSendMatch ? shouldSendMatch[1].toLowerCase() === "yes" : false;
|
|
242
|
+
|
|
243
|
+
return { reply, confidence, shouldSend, summary };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error("Error generating smart reply for batched messages:", error);
|
|
246
|
+
return {
|
|
247
|
+
reply: "Thanks for your message! I'll get back to you soon.",
|
|
248
|
+
confidence: 0,
|
|
249
|
+
shouldSend: false,
|
|
250
|
+
summary: "Error processing messages",
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Record that we replied to someone
|
|
257
|
+
*/
|
|
258
|
+
export function recordReply(phoneNumber: string): void {
|
|
259
|
+
autoReplyConfig.lastReplies.set(phoneNumber.replace(/\D/g, "").replace(/@c\.us$/, ""), Date.now());
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Tool: Configure Auto-Reply Settings
|
|
264
|
+
*/
|
|
265
|
+
export const configureAutoReplyTool = createTool({
|
|
266
|
+
id: "configure-auto-reply",
|
|
267
|
+
description: `
|
|
268
|
+
Configure WhatsApp auto-reply settings.
|
|
269
|
+
Use this to enable/disable auto-replies, set mode (manual/auto/smart),
|
|
270
|
+
manage whitelist/blacklist, set custom instructions, and configure debounce time.
|
|
271
|
+
Messages are batched and only processed after 2 minutes of inactivity from the sender.
|
|
272
|
+
`,
|
|
273
|
+
inputSchema: z.object({
|
|
274
|
+
action: z.enum(["enable", "disable", "set-mode", "set-debounce", "add-to-whitelist", "remove-from-whitelist", "add-to-blacklist", "remove-from-blacklist", "set-context", "set-instructions", "get-status", "clear-queue"]),
|
|
275
|
+
value: z.string().optional().describe("Value for the action (e.g., phone number, mode, instructions, debounce time in minutes)"),
|
|
276
|
+
}),
|
|
277
|
+
outputSchema: z.object({
|
|
278
|
+
success: z.boolean(),
|
|
279
|
+
message: z.string(),
|
|
280
|
+
currentConfig: z.object({
|
|
281
|
+
enabled: z.boolean(),
|
|
282
|
+
mode: z.string(),
|
|
283
|
+
debounceMinutes: z.number(),
|
|
284
|
+
whitelistCount: z.number(),
|
|
285
|
+
blacklistCount: z.number(),
|
|
286
|
+
maxRepliesPerHour: z.number(),
|
|
287
|
+
pendingQueues: z.number(),
|
|
288
|
+
}),
|
|
289
|
+
}),
|
|
290
|
+
execute: async (inputData) => {
|
|
291
|
+
const { action, value } = inputData;
|
|
292
|
+
|
|
293
|
+
switch (action) {
|
|
294
|
+
case "enable":
|
|
295
|
+
autoReplyConfig.enabled = true;
|
|
296
|
+
return {
|
|
297
|
+
success: true,
|
|
298
|
+
message: "✅ Auto-reply enabled. Messages will be batched and processed after 2 minutes of inactivity.",
|
|
299
|
+
currentConfig: getConfigSummary(),
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
case "disable":
|
|
303
|
+
autoReplyConfig.enabled = false;
|
|
304
|
+
return {
|
|
305
|
+
success: true,
|
|
306
|
+
message: "✅ Auto-reply disabled",
|
|
307
|
+
currentConfig: getConfigSummary(),
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
case "set-mode":
|
|
311
|
+
if (value && ["manual", "auto", "smart"].includes(value)) {
|
|
312
|
+
autoReplyConfig.mode = value as "manual" | "auto" | "smart";
|
|
313
|
+
return {
|
|
314
|
+
success: true,
|
|
315
|
+
message: `✅ Auto-reply mode set to: ${value}`,
|
|
316
|
+
currentConfig: getConfigSummary(),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
success: false,
|
|
321
|
+
message: "❌ Invalid mode. Use: manual, auto, or smart",
|
|
322
|
+
currentConfig: getConfigSummary(),
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
case "set-debounce":
|
|
326
|
+
if (value) {
|
|
327
|
+
const minutes = parseInt(value);
|
|
328
|
+
if (!isNaN(minutes) && minutes >= 1 && minutes <= 30) {
|
|
329
|
+
autoReplyConfig.debounceMs = minutes * 60 * 1000;
|
|
330
|
+
return {
|
|
331
|
+
success: true,
|
|
332
|
+
message: `✅ Debounce time set to ${minutes} minutes. Messages will be processed after ${minutes} minutes of inactivity.`,
|
|
333
|
+
currentConfig: getConfigSummary(),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
message: "❌ Invalid debounce time. Please provide a number between 1 and 30 minutes.",
|
|
340
|
+
currentConfig: getConfigSummary(),
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
case "add-to-whitelist":
|
|
344
|
+
if (value) {
|
|
345
|
+
const cleanNumber = value.replace(/\D/g, "");
|
|
346
|
+
if (!autoReplyConfig.whitelist.includes(cleanNumber)) {
|
|
347
|
+
autoReplyConfig.whitelist.push(cleanNumber);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
success: true,
|
|
351
|
+
message: `✅ Added ${cleanNumber} to whitelist`,
|
|
352
|
+
currentConfig: getConfigSummary(),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
success: false,
|
|
357
|
+
message: "❌ Please provide a phone number",
|
|
358
|
+
currentConfig: getConfigSummary(),
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
case "remove-from-whitelist":
|
|
362
|
+
if (value) {
|
|
363
|
+
const cleanNumber = value.replace(/\D/g, "");
|
|
364
|
+
autoReplyConfig.whitelist = autoReplyConfig.whitelist.filter(n => n !== cleanNumber);
|
|
365
|
+
return {
|
|
366
|
+
success: true,
|
|
367
|
+
message: `✅ Removed ${cleanNumber} from whitelist`,
|
|
368
|
+
currentConfig: getConfigSummary(),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
success: false,
|
|
373
|
+
message: "❌ Please provide a phone number",
|
|
374
|
+
currentConfig: getConfigSummary(),
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
case "add-to-blacklist":
|
|
378
|
+
if (value) {
|
|
379
|
+
const cleanNumber = value.replace(/\D/g, "");
|
|
380
|
+
if (!autoReplyConfig.blacklist.includes(cleanNumber)) {
|
|
381
|
+
autoReplyConfig.blacklist.push(cleanNumber);
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
success: true,
|
|
385
|
+
message: `✅ Added ${cleanNumber} to blacklist`,
|
|
386
|
+
currentConfig: getConfigSummary(),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
message: "❌ Please provide a phone number",
|
|
392
|
+
currentConfig: getConfigSummary(),
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
case "remove-from-blacklist":
|
|
396
|
+
if (value) {
|
|
397
|
+
const cleanNumber = value.replace(/\D/g, "");
|
|
398
|
+
autoReplyConfig.blacklist = autoReplyConfig.blacklist.filter(n => n !== cleanNumber);
|
|
399
|
+
return {
|
|
400
|
+
success: true,
|
|
401
|
+
message: `✅ Removed ${cleanNumber} from blacklist`,
|
|
402
|
+
currentConfig: getConfigSummary(),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
success: false,
|
|
407
|
+
message: "❌ Please provide a phone number",
|
|
408
|
+
currentConfig: getConfigSummary(),
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
case "set-context":
|
|
412
|
+
if (value) {
|
|
413
|
+
autoReplyConfig.userContext = value;
|
|
414
|
+
return {
|
|
415
|
+
success: true,
|
|
416
|
+
message: "✅ User context updated",
|
|
417
|
+
currentConfig: getConfigSummary(),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
success: false,
|
|
422
|
+
message: "❌ Please provide context text",
|
|
423
|
+
currentConfig: getConfigSummary(),
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
case "set-instructions":
|
|
427
|
+
if (value) {
|
|
428
|
+
autoReplyConfig.customInstructions = value;
|
|
429
|
+
return {
|
|
430
|
+
success: true,
|
|
431
|
+
message: "✅ Custom instructions updated",
|
|
432
|
+
currentConfig: getConfigSummary(),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
success: false,
|
|
437
|
+
message: "❌ Please provide instructions",
|
|
438
|
+
currentConfig: getConfigSummary(),
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
case "get-status":
|
|
442
|
+
return {
|
|
443
|
+
success: true,
|
|
444
|
+
message: `📊 Auto-reply is ${autoReplyConfig.enabled ? 'enabled' : 'disabled'}\n` +
|
|
445
|
+
`⏱️ Debounce time: ${autoReplyConfig.debounceMs / 60000} minutes\n` +
|
|
446
|
+
`📝 Messages are batched and processed after the sender stops typing for ${autoReplyConfig.debounceMs / 60000} minutes.`,
|
|
447
|
+
currentConfig: getConfigSummary(),
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
case "clear-queue":
|
|
451
|
+
if (value) {
|
|
452
|
+
const cleanNumber = value.replace(/\D/g, "");
|
|
453
|
+
cancelPendingMessages(cleanNumber);
|
|
454
|
+
return {
|
|
455
|
+
success: true,
|
|
456
|
+
message: `✅ Cleared pending message queue for ${cleanNumber}`,
|
|
457
|
+
currentConfig: getConfigSummary(),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
// Clear all queues
|
|
461
|
+
messageQueues.clear();
|
|
462
|
+
return {
|
|
463
|
+
success: true,
|
|
464
|
+
message: "✅ Cleared all pending message queues",
|
|
465
|
+
currentConfig: getConfigSummary(),
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
default:
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
message: "❌ Unknown action",
|
|
472
|
+
currentConfig: getConfigSummary(),
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Tool: Approve and Send Pending Reply
|
|
480
|
+
*/
|
|
481
|
+
export const approvePendingReplyTool = createTool({
|
|
482
|
+
id: "approve-pending-reply",
|
|
483
|
+
description: `
|
|
484
|
+
Approve a pending auto-reply message and send it.
|
|
485
|
+
Use this in manual mode when you want to send a suggested reply.
|
|
486
|
+
`,
|
|
487
|
+
inputSchema: z.object({
|
|
488
|
+
phoneNumber: z.string().describe("Phone number to send reply to"),
|
|
489
|
+
reply: z.string().describe("The reply message to send"),
|
|
490
|
+
}),
|
|
491
|
+
outputSchema: z.object({
|
|
492
|
+
success: z.boolean(),
|
|
493
|
+
message: z.string(),
|
|
494
|
+
messageId: z.string().optional(),
|
|
495
|
+
}),
|
|
496
|
+
execute: async (inputData) => {
|
|
497
|
+
const { phoneNumber, reply } = inputData;
|
|
498
|
+
|
|
499
|
+
// Lazy import to avoid circular dependency: this file ↔ whatsapp-client.ts
|
|
500
|
+
const { whatsappManager } = await import("../utils/whatsapp-client.js");
|
|
501
|
+
|
|
502
|
+
if (!whatsappManager.getReadyState()) {
|
|
503
|
+
return {
|
|
504
|
+
success: false,
|
|
505
|
+
message: "WhatsApp not connected",
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const result = await whatsappManager.sendMessage(phoneNumber, reply);
|
|
510
|
+
|
|
511
|
+
if (result.success) {
|
|
512
|
+
recordReply(phoneNumber);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return {
|
|
516
|
+
success: result.success,
|
|
517
|
+
message: result.success ? "✅ Reply sent successfully" : `❌ Failed: ${result.error}`,
|
|
518
|
+
messageId: result.messageId,
|
|
519
|
+
};
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Get a summary of current config
|
|
525
|
+
*/
|
|
526
|
+
function getConfigSummary() {
|
|
527
|
+
return {
|
|
528
|
+
enabled: autoReplyConfig.enabled,
|
|
529
|
+
mode: autoReplyConfig.mode,
|
|
530
|
+
debounceMinutes: autoReplyConfig.debounceMs / 60000,
|
|
531
|
+
whitelistCount: autoReplyConfig.whitelist.length,
|
|
532
|
+
blacklistCount: autoReplyConfig.blacklist.length,
|
|
533
|
+
maxRepliesPerHour: autoReplyConfig.maxRepliesPerHour,
|
|
534
|
+
pendingQueues: messageQueues.size,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Get full config (for internal use)
|
|
540
|
+
*/
|
|
541
|
+
export function getAutoReplyConfig(): AutoReplyConfig {
|
|
542
|
+
return autoReplyConfig;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Reset config to defaults
|
|
547
|
+
*/
|
|
548
|
+
export function resetAutoReplyConfig(): void {
|
|
549
|
+
autoReplyConfig = { ...defaultConfig };
|
|
550
|
+
messageQueues.clear();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Load auto-reply configuration from environment variables
|
|
555
|
+
* Env vars:
|
|
556
|
+
* WHATSAPP_AUTOREPLY_ENABLED=true/false
|
|
557
|
+
* WHATSAPP_AUTOREPLY_MODE=manual/auto/smart
|
|
558
|
+
* WHATSAPP_AUTOREPLY_DEBOUNCE_MINUTES=2
|
|
559
|
+
* WHATSAPP_AUTOREPLY_MAX_REPLIES_PER_HOUR=10
|
|
560
|
+
* WHATSAPP_AUTOREPLY_WHITELIST=1234567890,9876543210
|
|
561
|
+
* WHATSAPP_AUTOREPLY_BLACKLIST=1111111111,2222222222
|
|
562
|
+
* WHATSAPP_AUTOREPLY_USER_CONTEXT="Your context here"
|
|
563
|
+
* WHATSAPP_AUTOREPLY_CUSTOM_INSTRUCTIONS="Your instructions here"
|
|
564
|
+
*/
|
|
565
|
+
export function loadAutoReplyConfigFromEnv(): void {
|
|
566
|
+
const envEnabled = process.env.WHATSAPP_AUTOREPLY_ENABLED?.toLowerCase();
|
|
567
|
+
const envMode = process.env.WHATSAPP_AUTOREPLY_MODE?.toLowerCase();
|
|
568
|
+
const envDebounce = process.env.WHATSAPP_AUTOREPLY_DEBOUNCE_MINUTES;
|
|
569
|
+
const envMaxReplies = process.env.WHATSAPP_AUTOREPLY_MAX_REPLIES_PER_HOUR;
|
|
570
|
+
const envWhitelist = process.env.WHATSAPP_AUTOREPLY_WHITELIST;
|
|
571
|
+
const envBlacklist = process.env.WHATSAPP_AUTOREPLY_BLACKLIST;
|
|
572
|
+
const envContext = process.env.WHATSAPP_AUTOREPLY_USER_CONTEXT;
|
|
573
|
+
const envInstructions = process.env.WHATSAPP_AUTOREPLY_CUSTOM_INSTRUCTIONS;
|
|
574
|
+
|
|
575
|
+
if (envEnabled === "true") {
|
|
576
|
+
autoReplyConfig.enabled = true;
|
|
577
|
+
} else if (envEnabled === "false") {
|
|
578
|
+
autoReplyConfig.enabled = false;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (envMode && ["manual", "auto", "smart"].includes(envMode)) {
|
|
582
|
+
autoReplyConfig.mode = envMode as "manual" | "auto" | "smart";
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (envDebounce) {
|
|
586
|
+
const minutes = parseInt(envDebounce);
|
|
587
|
+
if (!isNaN(minutes) && minutes >= 1 && minutes <= 30) {
|
|
588
|
+
autoReplyConfig.debounceMs = minutes * 60 * 1000;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (envMaxReplies) {
|
|
593
|
+
const max = parseInt(envMaxReplies);
|
|
594
|
+
if (!isNaN(max) && max >= 1) {
|
|
595
|
+
autoReplyConfig.maxRepliesPerHour = max;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (envWhitelist) {
|
|
600
|
+
autoReplyConfig.whitelist = envWhitelist.split(",").map(n => n.replace(/\D/g, "")).filter(n => n.length > 0);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (envBlacklist) {
|
|
604
|
+
autoReplyConfig.blacklist = envBlacklist.split(",").map(n => n.replace(/\D/g, "")).filter(n => n.length > 0);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (envContext) {
|
|
608
|
+
autoReplyConfig.userContext = envContext;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (envInstructions) {
|
|
612
|
+
autoReplyConfig.customInstructions = envInstructions;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
console.log(`[AutoReply] Config loaded from env: enabled=${autoReplyConfig.enabled}, mode=${autoReplyConfig.mode}, debounce=${autoReplyConfig.debounceMs / 60000}min`);
|
|
616
|
+
}
|