Adds a new top-level safety section "数据真实性与操作合规" to the
lark-mail skill via the canonical generation pipeline:
- skill-template/domains/mail.md (source) — adds the section to the
domain introduction file that gen-skills.py renders into SKILL.md.
- skills/lark-mail/SKILL.md (regenerated product) — produced by
`make gen-skills project=mail` from larksuite-cli-registry against
the modified mail.md source.
Why both files: skills/lark-mail/SKILL.md is auto-generated from
skill-template/domains/mail.md + registry-conf/skill-meta.yaml +
output/from_meta/mail.json. Editing only SKILL.md would be reverted on
the next `make gen-skills` run because SKILL.md has no AUTO-GENERATED
markers and falls into the "no markers -> overwrite whole file" branch
in scripts/gen-skills.py.
The section adds 3 hard constraints on agent behavior:
- empty result is a valid answer; do not fabricate IDs or placeholders
- explicit action preview before destructive write operations
(delete / trash / batch_trash / cancel_scheduled_send / rules.*)
- reversible modifications (label / read state / folder move) are
exempt from the preview requirement
Addresses recurring evaluation failures (c03/c04/c06/c09/c14/c19~c24/c40)
where the agent fabricated IDs or auto-executed destructive operations.
* feat(ics): add RFC 5545 iCalendar generator and parser
Add shortcuts/mail/ics package:
- builder.go: generates METHOD:REQUEST ICS with VEVENT, ORGANIZER,
ATTENDEE, DTSTART/DTEND with timezone, UID, and X-LARK-MAIL-DRAFT
- parser.go: parses ICS into ParsedEvent struct, detects IsLarkDraft
- Handles CN quoting, control-char sanitization, email validation,
line folding per RFC 5545, and TZID edge cases
Change-Id: I01d13285a57a5a4de50891c54d655efa8423c3c1
* feat(mail): support calendar events in emails
- Add --event-summary/start/end/location flags to +send, +reply,
+reply-all, +forward, +draft-create
- Build ICS and embed as text/calendar in multipart/alternative
- Validate event time range and enforce --event/--send-time mutual
exclusion (extracted into validateEventSendTimeExclusion)
- CalendarBody() in emlbuilder places ICS correctly
- Exclude BCC from ATTENDEE list
Change-Id: Icf9e49ababebc4e8fcf36760ab613c64938c2744
* feat(mail): X-LARK-MAIL-DRAFT and read-only calendar guard
- ics.Build() writes X-LARK-MAIL-DRAFT:TRUE so Feishu client
recognizes CLI-created calendar events as editable
- ics.ParseEvent() detects IsLarkDraft field
- +draft-edit rejects --set-event-* on calendars without
X-LARK-MAIL-DRAFT marker (read-only after send)
- Export FindPartByMediaType from draft package for cross-package use
- Add set_calendar/remove_calendar patch ops with full test coverage
Change-Id: I7d547a4b40880e8d4ee3fecf68864d6ea89e66cd
* feat(mail): forward preserves original calendar ICS
When forwarding an email that contains a calendar event (body_calendar),
pass through the original ICS bytes as text/calendar part if no new
--event-* flags are specified.
Change-Id: I67d2e82604eaf969cee8c7e0bedcf32198d12d57
* docs(mail): document calendar invitation feature
- Add --event-* params to +send, +reply, +reply-all, +forward,
+draft-create, +draft-edit reference docs
- Add calendar_event output section to +message reference
- Add calendar invitation workflow to skill-template/domains/mail.md
- Regenerate SKILL.md via gen-skills
Change-Id: Iccacd06990d91e1cf3beb896d5b772d27e5e29ff
* fix(mail): reject --set-event-start/end/location without --set-event-summary
Change-Id: Icb651ff28ede526ff96b22e7b304b7bdea86d01f
Co-Authored-By: AI
* fix(mail): include --event-location in validateEventFlags; fix stale comment
Change-Id: I2f47016b6bfa11957dfe2c8c499cf36737efba53
Co-Authored-By: AI
* fix(mail): clear stale headers when wrapping single-leaf body in multipart/alternative
Change-Id: I29fe883c9151570f7939d372523b128cbea0b1ed
Co-Authored-By: AI
* fix(mail): add method=REQUEST to text/calendar MIME part created by set_calendar
Change-Id: I4d23674e20e4c42adab36385ff5ee8bb6d97625d
Co-Authored-By: AI
* fix(mail): use post-edit recipients for ICS attendees when --set-to combined with --set-event-*
Change-Id: I659e06635dd043f798d2f2e90d7dbca6e13d7f3d
Co-Authored-By: AI
* fix(mail): cover add_recipient/remove_recipient in ICS attendee resolution
Extract effectiveRecipients() to replay all three recipient op types
(set_recipients, add_recipient, remove_recipient) before building the
ICS for set_calendar, so patch-file recipient changes are reflected in
ATTENDEE metadata.
Change-Id: I3a7a55f96df8fac7d924a4dbeedd5b3d0d9d443c
Co-Authored-By: AI
* fix(mail): derive method= from ICS body in writeCalendarPart instead of hardcoding REQUEST
Passthrough ICS (e.g. forwarded METHOD:CANCEL) previously emitted a
Content-Type with method=REQUEST, disagreeing with the body. Now
extractICSMethod() scans the ICS for METHOD: and falls back to REQUEST
when absent, keeping existing behavior for our own generated ICS.
Change-Id: I4bf6c3755a189a436c2d26b082372d9f838f4051
Co-Authored-By: AI
* fix(mail): normalize calendar_event start/end to UTC in output
Callers expect RFC 3339 UTC strings; source ICS with TZID offsets
previously emitted +08:00 instead of Z.
Change-Id: I88bd4b925f8fc3b4f569e41712ae58ab50d94a2f
Co-Authored-By: AI
* fix(mail): make ICS parser case-insensitive and handle parameterized property names
RFC 5545 §3.1 allows any case and optional parameters on all property
names. Unify UID/SUMMARY/LOCATION/DTSTART/etc. to compare via
strings.ToUpper(name) and add HasPrefix checks for the NAME; form,
consistent with how ORGANIZER and ATTENDEE were already handled.
Change-Id: I7dc642dd210a3256f2189a901a2d9518ea284815
Co-Authored-By: AI
* feat(mail): add +share-to-chat shortcut to share emails as IM cards
Two-step API (create share token → send card) wrapped in a single
shortcut. Supports message-id/thread-id, five receive-id-type variants
(chat_id, open_id, user_id, union_id, email), and dry-run mode.
Change-Id: Ic7b8c01c0d25fef262f35be92555f1fd019bd679
Co-Authored-By: AI
* fix(mail): regenerate SKILL.md from skill-template instead of manual edit
Add missing safety rule 8 (draft link rule) to skill-template/domains/mail.md
so it survives regeneration. SKILL.md is now produced by `make gen-skills`
in the registry repo rather than hand-edited.
Change-Id: I9cf3605deae8b6de2042e40819fedc304967e78e
Co-Authored-By: AI
* fix(mail): add docstrings and use real validation path in tests
- Add Go doc comments to exported symbols for docstring coverage
- Rewrite tests to exercise MailShareToChat.Validate via RuntimeContext
instead of duplicating validation logic
- Replace hand-rolled containsStr with strings.Contains
- Add httpmock stubs for execute and error path tests
Change-Id: Ic781494f61e9e844224185844bce7b0c48e8e200
Co-Authored-By: AI
* test(mail): add dry-run E2E test for +share-to-chat
Validate request shape (method, URL, mailbox path) under --dry-run
with fake credentials. Covers message-id, thread-id, and custom
mailbox variants.
Change-Id: Iae87bf141cbe4f312d3e9b1fca4ba175052c5c35
Co-Authored-By: AI
* fix(mail): include request body and params in dry-run output
DryRun now mirrors Execute: the share-token POST shows message_id or
thread_id, and the send POST shows receive_id_type and receive_id.
E2E test updated to assert these fields. Also fix strconv.Itoa usage.
Change-Id: I00f8770fd5a12b7354986c5e5077f97cfe5d6653
* style(mail): gofmt dry-run test file
Change-Id: I47dc6a9a47252dcfb7853737f88dfdaef65a0ae7
* test(mail): assert exact API call count in dry-run test
Change-Id: I9f4a1a183b55d03f5248eb4adddfddb08037ca95
End-to-end RFC 3798 Message Disposition Notification support, covering
both sides of the receipt flow — requesting a receipt when composing, and
responding to one (send or decline) when reading.
Request side (compose)
- New --request-receipt flag on +send / +reply / +reply-all / +forward /
+draft-create / +draft-edit. When set, the outgoing EML carries a
Disposition-Notification-To header (RFC 3798) addressed to the resolved
sender. Recipient mail clients may prompt the user, auto-send a receipt,
or silently ignore — delivery is not guaranteed.
- requireSenderForRequestReceipt gates the flag against a controlled
sender address resolved BEFORE the orig.headTo fallback in +reply /
+reply-all / +forward, so the DNT cannot silently land on someone else
in CC / shared-mailbox flows.
Response side
- +send-receipt: build a system-templated reply for messages carrying the
READ_RECEIPT_REQUEST label (-607). Subject / recipient / sent / read
time layout matches the Lark client; body is non-customizable — receipt
bodies are system templates by industry convention; free-form notes
belong in +reply. Risk:"high-risk-write" + --yes required.
- +decline-receipt: clear READ_RECEIPT_REQUEST without sending anything
(mirrors the client's "不发送" / "Don't send" button). Idempotent on
re-run; Risk:"write" — no --yes needed.
Read-path hints
- +message / +messages / +thread emit a stderr hint when surfacing a
mail carrying READ_RECEIPT_REQUEST, exposing BOTH response paths
(+send-receipt --yes / +decline-receipt) so agents present a real
choice to the user instead of silently auto-sending.
Guard rails
- +send / +reply / +reply-all / +forward stay draft-by-default and
require --confirm-send to send, gated by a dynamic scope check for
mail:user_mailbox.message:send (absent from the default scope set so
draft-only flows don't need the sensitive permission).
- All header-bound user input (sender / display name / recipient /
subject) goes through CR/LF rejection plus Bidi / zero-width / line-
separator guards, mirroring emlbuilder.validateHeaderValue, to block
header injection and visual spoofing.
- Hint output strips terminal control characters (CR, LF) from any
untrusted field embedded into the user-visible suggestion.
Backend coupling
- Outgoing receipt EML carries the private header
X-Lark-Read-Receipt-Mail: 1. The data-access backend parses it into
BodyExtra.IsReadReceiptMail; DraftSend then applies READ_RECEIPT_SENT
(-608) and clears READ_RECEIPT_REQUEST (-607) from the original
message, closing the client-side banner.
- en receipts require backend TCC SubjectPrefixListForAdvancedSearch to
include "Read Receipt:" for conversation-view aggregation; zh prefix
("已读回执:") is already configured.
Docs: new reference pages for +send-receipt / +decline-receipt;
--request-receipt noted on each compose-side reference; SKILL.md
workflow (section 9) describes the full privacy-safe decision tree on
both sides.
Tests cover emlbuilder DispositionNotificationTo / IsReadReceiptMail
helpers, receiptMetaLabels (zh / en), buildReceiptSubject, text and HTML
body generators (with HTML escaping and Bidi guards), header-injection
defenses, sender-resolution gating (CC-only / shared-mailbox regression),
hint emission paths, and the full +send-receipt / +decline-receipt happy
+ idempotent paths via httpmock.
The <<<<<<< HEAD marker was accidentally left in mail.md and SKILL.md
by commit cb301a3 (draft preview URL). Remove it.
Change-Id: I6e1d5c0c66761302a3c4ee1421a16961b666bd80
* feat(mail): add contact search workflow and multi_entity search API
- Add recipient search workflow to mail skill template (search by name,
email keyword, or group name with rich result display)
- Regenerate SKILL.md with multi_entity.search command
Change-Id: Ie307af16a5ee38dac99c1d8d0df528730bf847d0
Co-Authored-By: AI
* fix: require user confirmation for all contact search results
multi_entity.search is a fuzzy keyword search — a single result does
not guarantee an exact match (e.g. searching "张三" may only return
"张三丰"). Always show candidates for user confirmation before using
the email address in compose parameters.
Change-Id: I447c54cd59b06a88c5d6806bfe76f0adfdceb1ce
Co-Authored-By: AI
- Add buildSendResult helper that includes recall_available/recall_tip
when backend returns recall_status in send response
- Update +send, +reply, +reply-all, +forward to use buildSendResult
- Add "Recall Email" section to mail skill template with recall and
get_recall_detail command examples
- Regenerate SKILL.md
Change-Id: I44317ead8f8a65db81e874cfc3529ffeb21e1384
Co-Authored-By: AI
New capabilities:
1. Alias (send_as) sending for all compose shortcuts (+send, +reply, +reply-all,
+forward, +draft-create, +draft-edit):
- New --mailbox flag separates mailbox routing from sender identity, enabling
alias sending where --mailbox specifies the owning mailbox and --from
specifies the alias address in the From header.
- Example: --mailbox me --from alias@example.com --to bob@example.com
- --mailbox priority: --mailbox > --from > "me"
- --from priority: --from > --mailbox > profile("me")
2. Discovery APIs for available mailboxes and sender addresses:
- accessible_mailboxes: lists all mailboxes the user can access (primary + shared)
- send_as: lists available sender addresses for a mailbox (primary, aliases, mailing lists)
3. Mail rules API:
- user_mailbox.rules resource: create, delete, list, reorder, update
4. Reply-all self-exclusion improvement:
fetchSelfEmailSet now also excludes the --from alias address, preventing the
sender from appearing in the recipient list when replying via an alias.
No breaking changes — omitting --mailbox preserves existing behavior.
* docs(mail): add identity guidance to prefer user over bot for mail APIs
Add an identity selection section to the mail skill documentation,
guiding AI agents to default to --as user when operating on mailboxes.
Bot identity requires the app to have tenant-level mail scopes enabled
in the developer console, which most apps do not.
* docs(mail): clarify identity selection wording and bot scope limits
- Replace ambiguous "默认使用" with "策略上应优先显式使用" to
distinguish policy recommendation from CLI default (auto)
- Note that bot identity only supports read operations; all write
operations (send, reply, forward, draft edit) require user identity
- Rewrite decision rules by read/write classification
Users reported that AI agents sometimes wrote shell scripts to manually
extract and re-decode JSON string fields (e.g. unicode_escape), causing
Chinese character corruption. Add notes to mail skill docs clarifying
that JSON output can be read directly without additional encoding
conversion.