wagent 1.0.0 → 1.0.2
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/dist/index.js +58 -12
- package/dist/quiet-console.js +31 -0
- package/dist/utils/logger.js +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "./quiet-console.js";
|
|
2
3
|
import { InstanceManager } from "./services/instance-manager.js";
|
|
3
4
|
import { createChildLogger } from "./utils/logger.js";
|
|
4
5
|
import { generateText, tool } from "ai";
|
|
@@ -15,6 +16,12 @@ const typingUntil = new Map();
|
|
|
15
16
|
function getOwnJid(adapter) {
|
|
16
17
|
return adapter.getMyJid();
|
|
17
18
|
}
|
|
19
|
+
function toUserJid(jid) {
|
|
20
|
+
if (!jid || !jid.includes("@"))
|
|
21
|
+
return jid;
|
|
22
|
+
const [user, domain] = jid.split("@");
|
|
23
|
+
return `${user.split(":")[0]}@${domain}`;
|
|
24
|
+
}
|
|
18
25
|
function getContactName(adapter, jid) {
|
|
19
26
|
return adapter.getContacts(jid).then((c) => {
|
|
20
27
|
const match = c.find((x) => x.jid === jid);
|
|
@@ -146,7 +153,7 @@ function buildTools(adapter) {
|
|
|
146
153
|
execute: async ({ to, text, quotedMessageId }, { experimental_context }) => {
|
|
147
154
|
const ctx = experimental_context;
|
|
148
155
|
const target = await resolveTarget(adapter, to, ctx);
|
|
149
|
-
const content = { type: "text", text };
|
|
156
|
+
const content = { type: "text", text: withAgentPrefix(text) };
|
|
150
157
|
if (quotedMessageId)
|
|
151
158
|
content.quotedMessageId = quotedMessageId;
|
|
152
159
|
await delayBeforeReply(ctx.config);
|
|
@@ -677,15 +684,34 @@ function messageCanReachAgent(config, event, isSelf, text) {
|
|
|
677
684
|
function isConfirmation(text) {
|
|
678
685
|
return /\b(confirm|confirmed|approve|approved|yes|ship it|looks good)\b/i.test(text);
|
|
679
686
|
}
|
|
687
|
+
function isPromptDraftRequest(text) {
|
|
688
|
+
return /\b(enough|draft|write.*prompt|make.*prompt|system prompt|ready|done|that's it|that is it)\b/i.test(text);
|
|
689
|
+
}
|
|
690
|
+
function maybeExtractSayCommand(text) {
|
|
691
|
+
const match = text.match(/^\s*say\s+["']?(.+?)["']?\s*$/i);
|
|
692
|
+
return match?.[1]?.trim() ?? null;
|
|
693
|
+
}
|
|
694
|
+
function withAgentPrefix(text) {
|
|
695
|
+
return text.trim().toLowerCase().startsWith("wagent:") ? text : `wagent: ${text}`;
|
|
696
|
+
}
|
|
697
|
+
function nextBootstrapQuestion(notes) {
|
|
698
|
+
const questions = [
|
|
699
|
+
"Got it. Who are you to me, and what should I call you?",
|
|
700
|
+
"What should my purpose be?",
|
|
701
|
+
"Who am I allowed to read or reply to?",
|
|
702
|
+
"What vibe should I use when I talk?",
|
|
703
|
+
];
|
|
704
|
+
return questions[Math.min(Math.max(notes.length - 1, 0), questions.length - 1)];
|
|
705
|
+
}
|
|
680
706
|
async function sendTracked(adapter, to, text) {
|
|
681
|
-
const res = await adapter.sendMessage(to, { type: "text", text });
|
|
707
|
+
const res = await adapter.sendMessage(to, { type: "text", text: withAgentPrefix(text) });
|
|
682
708
|
rememberAgentMessage(res.messageId);
|
|
683
709
|
}
|
|
684
710
|
async function startBootstrap(adapter, config, myJid) {
|
|
685
711
|
if (config.agent.bootstrapStarted || config.agent.mode !== "bootstrap")
|
|
686
712
|
return config;
|
|
687
713
|
await sendTracked(adapter, myJid, "I'm online. I don't know who I am yet.");
|
|
688
|
-
await sendTracked(adapter, myJid, "Tell me my purpose,
|
|
714
|
+
await sendTracked(adapter, myJid, "Tell me my purpose, what to call you, my vibe, and who I can read/reply to. Say 'draft prompt' when ready.");
|
|
689
715
|
await sendTracked(adapter, myJid, "I can message contacts, manage WhatsApp tools, remember context, compact memory, and enforce read/reply permissions.");
|
|
690
716
|
config.agent.bootstrapStarted = true;
|
|
691
717
|
saveConfig(config);
|
|
@@ -701,7 +727,24 @@ async function handleBootstrapMessage(adapter, config, chatId, text) {
|
|
|
701
727
|
await sendTracked(adapter, chatId, "Confirmed. I compacted setup context and will follow this system prompt from now on.");
|
|
702
728
|
return config;
|
|
703
729
|
}
|
|
730
|
+
if (config.agent.pendingSystemPrompt && !isConfirmation(text)) {
|
|
731
|
+
config.agent.pendingSystemPrompt = undefined;
|
|
732
|
+
config.agent.bootstrapNotes.push(`Feedback on rejected draft: ${text}`);
|
|
733
|
+
saveConfig(config);
|
|
734
|
+
await sendTracked(adapter, chatId, "Okay, I won't use that draft. Tell me what to change, or say 'draft prompt' when ready.");
|
|
735
|
+
return config;
|
|
736
|
+
}
|
|
737
|
+
const say = maybeExtractSayCommand(text);
|
|
738
|
+
if (say) {
|
|
739
|
+
await sendTracked(adapter, chatId, say);
|
|
740
|
+
return config;
|
|
741
|
+
}
|
|
704
742
|
config.agent.bootstrapNotes.push(text);
|
|
743
|
+
if (!isPromptDraftRequest(text)) {
|
|
744
|
+
saveConfig(config);
|
|
745
|
+
await sendTracked(adapter, chatId, nextBootstrapQuestion(config.agent.bootstrapNotes));
|
|
746
|
+
return config;
|
|
747
|
+
}
|
|
705
748
|
const draft = await generateWithFallback(config, {
|
|
706
749
|
system: "Draft a concise first-person system prompt for a WhatsApp agent from the user's setup notes. Include identity, purpose, vibe, read/reply boundaries, and tool behavior. Return only the prompt.",
|
|
707
750
|
messages: [{ role: "user", content: config.agent.bootstrapNotes.join("\n\n") }],
|
|
@@ -743,7 +786,7 @@ async function processMessage(adapter, config, instId, event, text, isSelf, send
|
|
|
743
786
|
if (responseText) {
|
|
744
787
|
await adapter.sendPresence(chatId, "composing");
|
|
745
788
|
await delayBeforeReply(config);
|
|
746
|
-
const content = { type: "text", text: responseText };
|
|
789
|
+
const content = { type: "text", text: withAgentPrefix(responseText) };
|
|
747
790
|
const res = await adapter.sendMessage(chatId, content);
|
|
748
791
|
rememberAgentMessage(res.messageId);
|
|
749
792
|
await adapter.sendPresence(chatId, "paused");
|
|
@@ -751,7 +794,7 @@ async function processMessage(adapter, config, instId, event, text, isSelf, send
|
|
|
751
794
|
}
|
|
752
795
|
else if (allToolCalls.length > 0) {
|
|
753
796
|
logger.info({ chatId, toolCount: allToolCalls.length }, "Tools executed silently");
|
|
754
|
-
const res = await adapter.sendMessage(chatId, { type: "text", text: "Done." });
|
|
797
|
+
const res = await adapter.sendMessage(chatId, { type: "text", text: withAgentPrefix("Done.") });
|
|
755
798
|
rememberAgentMessage(res.messageId);
|
|
756
799
|
}
|
|
757
800
|
}
|
|
@@ -759,7 +802,7 @@ async function processMessage(adapter, config, instId, event, text, isSelf, send
|
|
|
759
802
|
const msg = err instanceof Error ? err.message : String(err);
|
|
760
803
|
logger.error({ err, chatId }, "Agent error");
|
|
761
804
|
try {
|
|
762
|
-
const fallback = "Sorry, I hit an error processing that. Please try again.";
|
|
805
|
+
const fallback = withAgentPrefix("Sorry, I hit an error processing that. Please try again.");
|
|
763
806
|
const res = await adapter.sendMessage(chatId, { type: "text", text: fallback });
|
|
764
807
|
rememberAgentMessage(res.messageId);
|
|
765
808
|
}
|
|
@@ -794,6 +837,7 @@ async function main() {
|
|
|
794
837
|
instanceId = instances[0].id;
|
|
795
838
|
}
|
|
796
839
|
let myJid = null;
|
|
840
|
+
let myUserJid = null;
|
|
797
841
|
instanceManager.onAnyEvent((event, instId, payload) => {
|
|
798
842
|
if (event === "connection.changed" && instId === instanceId) {
|
|
799
843
|
const conn = payload;
|
|
@@ -804,6 +848,7 @@ async function main() {
|
|
|
804
848
|
if (conn.status === "open") {
|
|
805
849
|
const adapter = instanceManager.getAdapter(instId);
|
|
806
850
|
myJid = getOwnJid(adapter);
|
|
851
|
+
myUserJid = toUserJid(myJid);
|
|
807
852
|
console.log(`\nWhatsApp connected as ${myJid}`);
|
|
808
853
|
}
|
|
809
854
|
}
|
|
@@ -812,7 +857,8 @@ async function main() {
|
|
|
812
857
|
const adapter = instanceManager.getAdapter(instanceId);
|
|
813
858
|
for (let i = 0; i < 60; i++) {
|
|
814
859
|
myJid = getOwnJid(adapter);
|
|
815
|
-
|
|
860
|
+
myUserJid = toUserJid(myJid);
|
|
861
|
+
if (myJid && myUserJid)
|
|
816
862
|
break;
|
|
817
863
|
await new Promise((r) => setTimeout(r, 1000));
|
|
818
864
|
}
|
|
@@ -822,7 +868,7 @@ async function main() {
|
|
|
822
868
|
}
|
|
823
869
|
config = await ensureAiConfig(config);
|
|
824
870
|
const tools = buildTools(adapter);
|
|
825
|
-
config = await startBootstrap(adapter, config, myJid);
|
|
871
|
+
config = await startBootstrap(adapter, config, myUserJid ?? myJid);
|
|
826
872
|
console.log("Agent ready. Message yourself to configure or command it.");
|
|
827
873
|
instanceManager.onAnyEvent(async (event, instId, payload) => {
|
|
828
874
|
if (event === "presence.updated" && instId === instanceId) {
|
|
@@ -850,11 +896,11 @@ async function main() {
|
|
|
850
896
|
for (const k of toDelete)
|
|
851
897
|
processedMessages.delete(k);
|
|
852
898
|
}
|
|
853
|
-
const
|
|
854
|
-
if (!messageCanReachAgent(config, msg,
|
|
899
|
+
const isOwnerSelfChat = Boolean(msg.message.isFromMe && myUserJid && msg.chatId === myUserJid);
|
|
900
|
+
if (!messageCanReachAgent(config, msg, isOwnerSelfChat, text))
|
|
855
901
|
return;
|
|
856
902
|
await waitForTypingToStop(msg.chatId);
|
|
857
|
-
if (config.agent.mode === "bootstrap" &&
|
|
903
|
+
if (config.agent.mode === "bootstrap" && isOwnerSelfChat) {
|
|
858
904
|
config = await handleBootstrapMessage(adapter, config, msg.chatId, text);
|
|
859
905
|
return;
|
|
860
906
|
}
|
|
@@ -862,7 +908,7 @@ async function main() {
|
|
|
862
908
|
const chatName = msg.chatId.endsWith("@g.us")
|
|
863
909
|
? await getGroupName(adapter, msg.chatId)
|
|
864
910
|
: senderName;
|
|
865
|
-
processMessage(adapter, config, instanceId, msg, text,
|
|
911
|
+
processMessage(adapter, config, instanceId, msg, text, isOwnerSelfChat, senderName, chatName, tools)
|
|
866
912
|
.catch((err) => logger.error({ err }, "processMessage error"));
|
|
867
913
|
}
|
|
868
914
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const originalConsole = {
|
|
2
|
+
log: console.log.bind(console),
|
|
3
|
+
warn: console.warn.bind(console),
|
|
4
|
+
error: console.error.bind(console),
|
|
5
|
+
};
|
|
6
|
+
function shouldSuppressConsole(args) {
|
|
7
|
+
if (process.argv.includes("--debug"))
|
|
8
|
+
return false;
|
|
9
|
+
const text = args.map((arg) => (typeof arg === "string" ? arg : "")).join(" ");
|
|
10
|
+
return (text.startsWith("Closing session:") ||
|
|
11
|
+
text.includes("Decrypted message with closed session") ||
|
|
12
|
+
text.includes("stream errored out") ||
|
|
13
|
+
text.includes("no name present") ||
|
|
14
|
+
text.includes("blocked on missing key") ||
|
|
15
|
+
text.includes("failed to find key") ||
|
|
16
|
+
text.includes("transaction failed") ||
|
|
17
|
+
text.includes("failed to decrypt message"));
|
|
18
|
+
}
|
|
19
|
+
console.log = (...args) => {
|
|
20
|
+
if (!shouldSuppressConsole(args))
|
|
21
|
+
originalConsole.log(...args);
|
|
22
|
+
};
|
|
23
|
+
console.warn = (...args) => {
|
|
24
|
+
if (!shouldSuppressConsole(args))
|
|
25
|
+
originalConsole.warn(...args);
|
|
26
|
+
};
|
|
27
|
+
console.error = (...args) => {
|
|
28
|
+
if (!shouldSuppressConsole(args))
|
|
29
|
+
originalConsole.error(...args);
|
|
30
|
+
};
|
|
31
|
+
export {};
|
package/dist/utils/logger.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// WA MCP — Structured Logger with Request Tracing
|
|
3
3
|
// ============================================================
|
|
4
4
|
import pino from "pino";
|
|
5
|
-
import { SERVER_NAME
|
|
5
|
+
import { SERVER_NAME } from "../constants.js";
|
|
6
6
|
const logLevel = process.argv.includes("--debug")
|
|
7
7
|
? "debug"
|
|
8
8
|
: process.argv.includes("--verbose")
|
|
9
9
|
? "info"
|
|
10
|
-
:
|
|
10
|
+
: "silent";
|
|
11
11
|
const isStdio = process.env.WA_TRANSPORT === "stdio";
|
|
12
12
|
export const logger = pino({
|
|
13
13
|
name: SERVER_NAME,
|