mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
Merge upstream main into fix/hermes-relay-anthropic-context
Signed-off-by: Alex Fournier <afournier@nvidia.com>
This commit is contained in:
commit
b1a5d67e71
214 changed files with 11437 additions and 977 deletions
|
|
@ -39,6 +39,7 @@ import {
|
|||
buildTextSendPayload,
|
||||
createBoundedMessageStore,
|
||||
extractBridgeEvent,
|
||||
inboundReadReceiptKeys,
|
||||
inferMediaType,
|
||||
mediaPayloadForFile,
|
||||
pollCreationMessageFromPayload,
|
||||
|
|
@ -75,6 +76,12 @@ const FORWARD_OWNER_MESSAGES =
|
|||
typeof process.env.WHATSAPP_FORWARD_OWNER_MESSAGES === 'string' &&
|
||||
['1', 'true', 'yes', 'on'].includes(process.env.WHATSAPP_FORWARD_OWNER_MESSAGES.toLowerCase());
|
||||
|
||||
const SEND_READ_RECEIPTS =
|
||||
typeof process !== 'undefined' &&
|
||||
process.env &&
|
||||
typeof process.env.WHATSAPP_SEND_READ_RECEIPTS === 'string' &&
|
||||
['1', 'true', 'yes', 'on'].includes(process.env.WHATSAPP_SEND_READ_RECEIPTS.toLowerCase());
|
||||
|
||||
const PORT = parseInt(getArg('port', '3000'), 10);
|
||||
const SESSION_DIR = getArg('session', path.join(process.env.HOME || '~', '.hermes', 'whatsapp', 'session'));
|
||||
// Cache directories: the Python gateway passes the profile-aware paths via
|
||||
|
|
@ -1042,6 +1049,30 @@ app.post('/typing', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// Mark an inbound message as read only after the Python adapter has accepted
|
||||
// it through the authoritative DM/group/mention intake policy.
|
||||
app.post('/read', async (req, res) => {
|
||||
if (!sock || connectionState !== 'connected') {
|
||||
return res.status(503).json({ error: 'Not connected' });
|
||||
}
|
||||
|
||||
const receiptKeys = inboundReadReceiptKeys({
|
||||
key: req.body?.key,
|
||||
enabled: SEND_READ_RECEIPTS,
|
||||
});
|
||||
if (receiptKeys.length === 0) {
|
||||
return res.json({ success: true, marked: false });
|
||||
}
|
||||
|
||||
try {
|
||||
await sock.readMessages(receiptKeys);
|
||||
return res.json({ success: true, marked: true });
|
||||
} catch (err) {
|
||||
console.warn('[bridge] failed to send read receipt:', err.message);
|
||||
return res.status(500).json({ error: 'Failed to send read receipt' });
|
||||
}
|
||||
});
|
||||
|
||||
// Chat info
|
||||
app.get('/chat/:id', async (req, res) => {
|
||||
const chatId = req.params.id;
|
||||
|
|
@ -1074,6 +1105,7 @@ app.get('/health', (req, res) => {
|
|||
queueLength: messageQueue.length,
|
||||
uptime: process.uptime(),
|
||||
scriptHash: SCRIPT_HASH,
|
||||
sendReadReceipts: SEND_READ_RECEIPTS,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,33 @@ import {
|
|||
createBoundedMessageStore,
|
||||
appendMediaFailureNote,
|
||||
extractBridgeEvent,
|
||||
inboundReadReceiptKeys,
|
||||
mediaPayloadForFile,
|
||||
pollCreationMessageFromPayload,
|
||||
pollUpdateForAggregation,
|
||||
} from './bridge_helpers.js';
|
||||
|
||||
// -- inbound read receipts ------------------------------------------------
|
||||
{
|
||||
const groupKey = {
|
||||
id: 'incoming-group-1',
|
||||
remoteJid: '120363001234567890@g.us',
|
||||
participant: '15550001111@s.whatsapp.net',
|
||||
fromMe: false,
|
||||
};
|
||||
|
||||
assert.deepEqual(inboundReadReceiptKeys({ key: groupKey, enabled: false }), []);
|
||||
assert.deepEqual(
|
||||
inboundReadReceiptKeys({ key: { ...groupKey, fromMe: true }, enabled: true }),
|
||||
[],
|
||||
);
|
||||
const receiptKeys = inboundReadReceiptKeys({ key: groupKey, enabled: true });
|
||||
assert.equal(receiptKeys.length, 1);
|
||||
assert.equal(receiptKeys[0], groupKey);
|
||||
assert.equal(receiptKeys[0].participant, groupKey.participant);
|
||||
console.log(' ✓ inbound read receipts preserve the original group message key');
|
||||
}
|
||||
|
||||
// -- quoted outbound text -------------------------------------------------
|
||||
{
|
||||
const store = createBoundedMessageStore(2);
|
||||
|
|
@ -96,6 +118,12 @@ import {
|
|||
assert.equal(event.quotedParticipant, '15559998888@s.whatsapp.net');
|
||||
assert.equal(event.quotedRemoteJid, '15551234567@s.whatsapp.net');
|
||||
assert.equal(event.quotedText, 'approve deploy?');
|
||||
assert.deepEqual(event.readReceiptKey, {
|
||||
id: 'incoming-1',
|
||||
remoteJid: '15551234567@s.whatsapp.net',
|
||||
participant: '15550001111@s.whatsapp.net',
|
||||
fromMe: false,
|
||||
});
|
||||
assert.equal(event.hasQuotedMessage, true);
|
||||
assert.equal(event.body, 'approved');
|
||||
console.log(' ✓ inbound quoted metadata includes quoted text');
|
||||
|
|
|
|||
|
|
@ -483,6 +483,12 @@ export async function extractBridgeEvent({
|
|||
quotedText,
|
||||
hasQuotedMessage,
|
||||
botIds,
|
||||
readReceiptKey: {
|
||||
remoteJid: msg.key.remoteJid || chatId,
|
||||
id: msg.key.id,
|
||||
participant: msg.key.participant || senderId,
|
||||
fromMe: Boolean(msg.key.fromMe),
|
||||
},
|
||||
timestamp: msg.messageTimestamp,
|
||||
};
|
||||
}
|
||||
|
|
@ -494,6 +500,12 @@ export function inferMediaType(ext) {
|
|||
return 'document';
|
||||
}
|
||||
|
||||
export function inboundReadReceiptKeys({ key, enabled }) {
|
||||
if (!enabled || !key || key.fromMe || !key.id || !key.remoteJid) return [];
|
||||
// Preserve participant for group messages: Baileys needs the original key.
|
||||
return [key];
|
||||
}
|
||||
|
||||
export function mediaPayloadForFile({ buffer, filePath, mediaType, caption, fileName }) {
|
||||
const ext = filePath.toLowerCase().split('.').pop();
|
||||
const type = mediaType || inferMediaType(ext);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue