import type { GraphClientConfig } from "../lib/graph-client.js"; export interface DraftEditArgs { draftId: string; to?: string[]; cc?: string[]; bcc?: string[]; subject?: string; body?: string; isHtml?: boolean; /** Absolute paths inside the account directory. Added to whatever the draft * already carries; pair with `removeAttachments` to replace a file. */ attachments?: string[]; /** Attachment names or ids to take off the draft, resolved against its * pre-call state. */ removeAttachments?: string[]; } /** * Update an existing draft in place via PATCH /me/messages/{draftId}, and/or add * and remove its attachments. Graph drafts are mutable, so no delete-and-reappend. * Only the supplied fields are patched. Graph rejects a PATCH on a sent * (non-draft) message — that error is surfaced to the caller unchanged. * * Attachments edit in place like every other field: `attachments` adds files, * `removeAttachments` takes them off by name or by attachment id, and the two * together are a replace. * * ORDER (Task 1696). Local path validation, then the listing + removal * resolution, then PATCH, then the DELETEs, then the attaches. Two properties * fall out of resolving removals up front: * * - Removals resolve against the draft's PRE-CALL state, so "swap a.pdf for a * newer a.pdf" pins the old file's id before the new one exists and cannot * match the file it is meant to replace. * - A removal naming a file the draft does not carry, or a name carried twice, * refuses before the PATCH. Listing is a GET, so the draft is untouched. * * "At least one change" counts attachments and removals as well as patchable * fields, so an attachment-only or removal-only call is valid; either issues no * PATCH at all. * * A Graph-side failure is a different matter: the PATCH, the DELETEs and the * attaches are separate calls and Graph has no transaction across them, so a * DELETE or attach that fails AFTER a successful PATCH leaves the field edits * applied while this function throws. The caller is told the edit failed; the * subject/recipients it asked for are nonetheless changed. Re-running with the * same arguments is safe for the fields (PATCH is idempotent) but WILL duplicate * any attachment that already landed, because attaching is additive. A removal * that already landed re-runs as a `not-on-draft` refusal, because the file it * names is gone. Only path validation and removal resolution are all-or-nothing. * * The id in the PATCH response is authoritative for where the draft now lives, * so it is returned rather than the caller's `draftId` — mirroring `runDraft`, * which throws rather than hand back an id it never read. Ids are immutable * plugin-wide (IMMUTABLE_ID_PREFER), so the two normally match; returning the * reported id means the tool never asserts a handle it has not confirmed, and * `rotated=true` in the log makes any divergence visible (Task 1688). * * That is also why the DELETEs and attaches target the REPORTED id, not the * caller's: if the id rotated, the caller's handle no longer addresses the draft * and writing to it would 404 or, worse, hit a stale item. The listing is * necessarily read from the caller's id, since it must precede the PATCH that * reports the authoritative one. Under a rotation — which IMMUTABLE_ID_PREFER * makes effectively impossible — the resolved attachment ids would belong to the * pre-rotation item and the DELETE would fail loudly rather than silently remove * the wrong file. That is the accepted consequence, not a handled path. */ export declare function runDraftEdit(config: GraphClientConfig, args: DraftEditArgs): Promise<{ draftId: string; updated: true; attachments: string[]; removed: string[]; }>; //# sourceMappingURL=draft-edit.d.ts.map