dsh-agentmail
zt8989/dsh-agentmail
Send and receive QQ Agent Mail through an OAuth-authorized agently-cli account — six tools (send, list, read, search, reply, poll) that embed a session/thread marker in the subject, a background poller that routes inbound replies into the owning DSH Session and wakes it, and incoming HTML bodies rendered to clean markdown.
설치
dsh plugin --profile web add github:zt8989/dsh-agentmailREADME
dsh-agentmail — Agent Mail for DeepSeek Harness
A DeepSeek Harness plugin that lets an agent send and receive email through
the QQ Agent Mail mailbox (via agently-cli),
routes incoming replies back into the DSH Session that owns the thread, and
hands mail for unknown conversations to a fallback Session (or ignores it).
Its settings are edited on a configuration page in the Web UI.
What it does
-
Tools for the agent:
agentmail_send— send an email; asession/threadmarker is embedded in the subject automatically.agentmail_list— list a folder (inbox/sent/trash/spam).agentmail_read— read one message in full; HTML bodies are rendered to clean markdown.agentmail_search— search by keyword.agentmail_reply— reply to a message (marker preserved).agentmail_poll— check the inbox now and route new mail.
-
Poller — every
pollIntervalMsit lists unread inbox mail, reads each new message, parses the marker, and delivers it into the bound DSH Session withagent.followup(...)(waking the agent). A conversation that matches no live Session goes to the configured fallback Session; with no fallback the mail stays in the inbox and is ignored. -
Configuration page — Settings → Agent Mail is a full settings page; the same page is also the Configure control on the Plugins → Agent Mail row, and both edit one namespace (fallback Session, sender filter, delivery, polling, connection). The fallback Session comes from a searchable list of known sessions (type to filter by title or id), so no id has to be copied by hand. Edits are staged and written on Save, so no manual
cordis.patch.ymlediting is needed for normal use. -
HTML -> markdown — QQ/Foxmail HTML bodies are rendered down to compact markdown (
html.js, turndown-based): signature/report blocks (data-xmail_bot_mail_report) are removed, Foxmail's border-left/mail_quoteshells become>quotes, tiny icon/tracking pixels drop out, other images become[图片]placeholders, tables render as pipe tables. PLAIN and MARKDOWN bodies pass through untouched. -
Missing-CLI hint — when
agently-clicannot be executed, every tool (and the poller) returns the official setup doc pointer (https://agent.qq.com/doc/cli-setup.md) instead of a bare ENOENT, so the agent can walk the user through installation.
The session / thread marker
Sending mail appends this to the subject:
Subject: Quarterly report [AM:s=<session>|t=<thread>]
s— the conversation owner: the DSH Session id (or a stable key you pass).t— the thread (topic) the agent chose.
Replies keep the marker (mail clients keep Re: … in the subject), so the poller
resolves (session, thread) back to the same DSH Session. A mapping table is
persisted in state.json so routing survives restarts even if the subject is
edited or the marker only survives in the snippet.
Requirements
-
agently-cliinstalled and authorized once:npm install -g @tencent-qqmail/agently-cli agently-cli auth login # interactive OAuth agently-cli +me # verify the mailbox address -
The plugin installed into a profile:
dsh plugin --profile web add @zt8989/dsh-agentmailAny profile name works (
webshown as an example);--profile <name>initializes the profile on first use. If the CLI is missing, the tools return the setup hint above.
Packaging
The published package ships a single self-contained bundle (dist/index.js):
turndown (HTML->markdown) is inlined via esbuild at prepublishOnly time, so
the package has no runtime dependencies. The bundle imports the DSH host
packages (@deepseek-ai/cordis, @deepseek-ai/dsh-llm, @deepseek-ai/dsh-tools)
at runtime; they are not declared as peerDependencies on purpose — they are
resolved from the host's own install (the registry copies are unrelated) and
declaring them crashes npm's arborist. Sources live in index.js + html.js;
npm run build regenerates the bundle.
Configuration
Open Plugins in the sidebar, select @zt8989/dsh-agentmail, and use
Configure on the agentmail row. The page writes the profile's
cordis.patch.yml for you; the same values can be set there by hand:
- id: agentmail
config:
fallbackSessionId: "session-…" # session for unknown conversations; "" = ignore
pollIntervalMs: 60000 # 0 disables the background poller
routeOnlyFrom: [] # e.g. ["boss@example.com"] to only auto-route those
cliPath: agently-cli # absolute path if not on PATH
statePath: "" # default <DSH_HOME>/agentmail/state.json
markRead: true # move routed mail out of the inbox
The fallback Session id is the same id the Session menu copies. Mail that reaches the fallback is marked as an unknown conversation in the delivered text, and it does not create a thread binding: if that Session later comes back, its own threads route to it again.
Notes
- Send/reply use
--confirmedso mail is not blocked on an interactive confirmation prompt. - Attachments can be sent with the CLI directly; this plugin focuses on the session/thread routing workflow.