[pull] main from rmyndharis:main - #114
Merged
Merged
Conversation
Baileys acknowledges individual messages, not chats, so POST
/sessions/{id}/chats/read could only mark the newest message the engine
still held in memory. A burst of inbound messages left everything but the
last one unread for good, and a session that restarted since the message
arrived had nothing to acknowledge at all, answering false under a 200.
The request body now takes an optional messageIds array. A caller that
persists inbound message IDs names exactly what to acknowledge and is
subject to neither case; omitting it keeps the previous last-message
behaviour. The list is capped at 100 so one request cannot hand the engine
an unbounded key list to round-trip.
whatsapp-web.js ignores the field: its sendSeen is chat-level and already
marks every message in the chat.
Group chats are only partly served: the keys built here carry remoteJid and
id but no participant, which Baileys aggregates receipts by, so a supplied
id in a group is acknowledged without the sender it belongs to. Direct
chats are unaffected, as is the last-message fallback in both, which
carries the stored key whole.
Address review on the messageIds PR. A synthesised receipt key carried no participant, so a group receipt named no sender and WhatsApp could not attribute it while the API answered success: true. Its hardcoded fromMe was also wrong for an outbound id, and its jid was whichever dialect the caller happened to send. Each supplied id is now resolved through the message store and the stored key is used whole. Ids the store has never seen - history backfill is emitted but not persisted - keep the synthesised key, which is what the 1:1 case ran on before. The store gains a batched getMessages: the receipt path resolves up to a hundred ids per request, and a findOne apiece was a hundred sequential round trips. POST /chats/unread gets its own MarkChatUnreadDto instead of sharing MarkChatReadDto. Sharing published messageIds on a route that discards it. The JS SDK splits the same way: MarkChatReadRequest for markRead, with MarkChatRequest left to markUnread and subscribePresence, which is served by a chatId-only DTO and would have taken a 400 on the extra field under forbidNonWhitelisted. check-contract-shapes maps both pairs, taking the JavaScript floor from 78 to 79. An empty messageIds array is refused rather than read as "acknowledge the newest message", so a caller that computed an empty unread set no longer acknowledges a message it never named. The cap is hoisted to MARK_READ_MESSAGE_IDS_MAX and published as maxItems, since @ArrayMaxSize is runtime-only and the schema advertised an unbounded array. Entries are matched against a non-whitespace token, because @isnotempty accepted ' '. The send-seen spec stubs the real @c.us to @s.whatsapp.net fold instead of an identity function, which had let the whole suite pass with the toEngineJid call deleted. The DTO spec covers the four new validators, and SessionMarkChatRead forwards the list to MCP callers.
Rebasing on main brought the shape gate's request-body coverage to the Python, Go and Java clients, each of which maps MarkChatRequest to MarkChatReadDto. That pair now fails the same way the JavaScript one did: the contract carries messageIds and the hand-written type does not. Each client gets a MarkChatReadRequest carrying the optional list, with MarkChatRequest left to markUnread and subscribePresence, which take the chat id alone. The gate maps both pairs for all four clients and each coverage floor rises by one.
supertest starts a listener per request with `listen(0)` and no host, which binds the wildcard address, and then dials 127.0.0.1 whatever it bound. macOS hands a wildcard listener an ephemeral port that another process already holds on 127.0.0.1 specifically, permits the overlapping bind, and routes the loopback connection to the more specific holder. That process answers, so an assertion reads a status the app has no route for. Measured here: 40 of 40 such ports went to a wildcard listener, and a captured run took a 501 from a desktop helper holding 127.0.0.1:49657 on a machine with 29 loopback-specific listeners. It surfaced as a failure on a different test each run, which is why it read as flakiness and why redirecting state never moved it. Listening on 127.0.0.1 while the app initialises makes supertest reuse that server instead of opening one, and takes the lane from 230 listeners a run to 27. The host cannot simply be added to supertest's own call: a host argument routes the bind through dns.lookup, so address() is still null when supertest reads it synchronously. listen() initialises first and then binds the same server, so it hands the port taken during init back before binding what it was asked for. A bind failure is settled from the 'error' event, which otherwise leaves init pending and throws detached from the boot that caused it. Linux refuses the overlapping bind and its allocator skips held ports, so CI was not exposed; the change is inert there.
test(e2e): put each suite's server on a loopback port
External contributions carry "Thanks @handle." on their CHANGELOG entry; this one was missing it.
feat(session): let callers name which messages a read receipt covers
The REST body rejects a whitespace-only entry in `messageIds`, but `SessionMarkChatRead` declared its own list and copied only the count bound. `invokeTool` parses the tool's schema and calls the service directly, so the DTO never applies on that path and `[' ']` reached the engine as a receipt key. The pattern and its message move into exported constants next to the existing cap, and the tool holds them rather than restating the rule. Restating it is how the two surfaces drifted in the first place.
Entries under `[Unreleased]` had grown past anything the released sections carry: median 44 words against 34, and a longest of 139 against a historical maximum of 80. They read as explanations rather than release notes. Rewritten to the same distribution (median 39, longest 57), keeping what a reader acts on and dropping the reasoning. Contributor credit and the section headings are unchanged.
fix(mcp): apply the message-id format rule on the agent tool
`Install sqlite3` held the scripts-smoke job open for over an hour on two consecutive main runs while every other job had already gone green, so neither run ever reported and main went without a signal. The step had no timeout, so a stalled apt-get would have burned the six-hour job default before failing. Both apt steps now carry a five-minute timeout and skip the install when the runner image already ships the tool, which is the common case. The shellcheck step runs its own apt-get update rather than inheriting one from the step above, which no longer always runs. A failing install still fails the step: the short-circuit only covers the tool already being present. release.yml carries the same job and had the same two unbounded steps, so it is fixed alongside; a release cut could have stalled the same way.
`SubscribePresenceDto` had no contract-shape coverage at all. Four clients declared one `MarkChatRequest` for both `subscribePresence` and `markUnread`, and the gate mapped that type to `MarkChatUnreadDto`, so the pair being compared was not the pair that exists. The two DTOs are identical today, which is the only reason nothing failed; a field added to either would have gone unnoticed on every typed client. Each of the four typed clients now declares `SubscribePresenceRequest`, the gate maps it to `SubscribePresenceDto`, and the per-client coverage floors rise by one so the pair cannot be dropped again in silence. Comparisons go from 320 to 324. BREAKING for the Go and Java clients: `SubscribePresence` takes the new type. The wire body is unchanged.
…body fix(sdk): give subscribe-presence its own request type
ci: bound the apt steps so a slow mirror cannot hang a run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )