Repository navigation
Mark mail read or unread (mail_mark_read) - #14
Merged
Merged
Conversation
The \Seen marker was readable everywhere -- mail_search reports it, unseen_only filters on it -- and writable nowhere, so an agent could find the unread mail and never clear it. Cleaning an inbox down after a mass mailing needs both halves. mail_mark_read sets it, read=false takes it back off. Two decisions: - A sibling of flag(), not an argument to it. The markers are independent states, and a single call would have to be told which one it was not changing. - The same raw UID STORE, now shared as _store(). imap-tools' mb.flag follows every STORE with an EXPUNGE, which drops whatever another client left marked \Deleted -- and a \Seen sweep is precisely the operation that touches every message in the folder. Reading still does not mark: every fetch keeps mark_seen=False, so "what have I not looked at" stays the user's answer rather than a side effect of an agent looking. The tool reads the provider method through getattr and refuses clearly when it is absent, the same rule as outgoing attachments: a backend from before this would otherwise report a sweep it never made. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gf8kB8Mrx9GaGrEQNnwQ4v
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes the gap in Intern #926:
\Seenwas readable everywhere (mail_searchreports it,unseen_onlyfilters on it) and writable nowhere, so an agent could find unread mail and never clear it -- which is exactly what cleaning an inbox down after a mass mailing needs.What's new
mail_mark_read(uids, folder="INBOX", read=True, account=None).read=falseputs messages back to unread. Noconfirm=gate, same asmail_flag: it alters no message and the same tool takes it straight back off.Decisions worth naming
flag(), not an argument to it.set_seenis its own protocol method because the star and the read state are independent markers -- one call would have to be told which one it was not changing.UID STORE, now shared as_store(). imap-tools'mb.flagfollows every STORE with an EXPUNGE, which permanently drops whatever another mail client left marked\Deleted. A\Seensweep is precisely the operation that touches every message in a folder, so it gets the same treatment the flag already had.fetchkeepsmark_seen=False. "What have I not looked at" stays the user's own answer rather than a side effect of an agent looking, so marking is always deliberate -- and the tool docstring andknowledge.pysay so.getattr(provider, "set_seen", None)and refuses clearly when absent, the same rule as outgoing attachments: a backend written before this would otherwise report a sweep it never made.Tests
tests/test_imap_flag.py-- both STORE command shapes, the absent EXPUNGE, uid injection refused, empty list a no-op.tests/test_tools.py-- annotations (write, non-destructive, idempotent, noconfirm), a mark/unmark round trip visible throughmail_searchandunseen_only, and the refusal for a backend without the method.tests/integration/test_greenmail_e2e.py-- round trip against a real IMAP server, including thatunseen_onlythen agrees with what was written.make lintandmake testgreen (246 passed). The GreenMail job needs Docker, which this session has none of, sotest_mark_read_and_unread_round_triphas not run locally -- CI covers it.Companion PR in
squirrel-mcp-adminimplementsset_seenon the Graph backend; it needs aSQUIRREL_MCP_REFbump to this commit once merged.🤖 Generated with Claude Code
https://claude.ai/code/session_01Gf8kB8Mrx9GaGrEQNnwQ4v
Generated by Claude Code