/** * Removing an attachment from a draft: the mirror of attach.ts. * * Resolution and execution are separate functions because they must happen at * different moments. `resolveRemovalsOrRefuse` runs against the draft's PRE-CALL * listing and before any Graph write, so a removal naming a file the draft does * not carry leaves the draft untouched. `detachFromDraft` then issues the * DELETEs. * * Only draft-edit calls this. Graph does not allow removing an attachment from a * sent message, so there is no second caller to generalise for. */ import type { GraphClientConfig } from "./graph-client.js"; import type { AttachmentListItem } from "./attachment-list.js"; /** A removal target resolved to the one attachment it addresses. */ export interface ResolvedRemoval { attachmentId: string; name: string; } /** * Resolve each target to exactly one attachment on the draft, or refuse. * * An exact attachment-id match wins over a name match: the id is the precise * handle, and a caller that passed one meant it. Only when no id matches is the * target compared against names, exactly. * * Both refusals are typed and both fire before any Graph write, so the draft is * untouched either way: * - `not-on-draft` — nothing matched. The message names what IS on the draft, * so the agent can retry against a real name rather than guess again. * - `ambiguous-name` — a name matched more than one attachment. Refused rather * than resolved arbitrarily; the message reports the ids that disambiguate. * * Distinct targets can still address the SAME attachment — an agent holding both * an id and a name for one file from a listing passes both. That is unambiguous, * so it is collapsed rather than refused: without the dedup the second DELETE * 404s and the call reports failure for work that wholly succeeded. Each * attachment is therefore removed at most once, and `removed` names it once. * * The `op: "resolve"` line is a post-condition of resolution, not an intention * to write: it is emitted only once every target has resolved, so it never * announces a removal that was subsequently refused. It carries both counts * because they answer different questions — `resolved` below `requested` means * two targets named one file, while a detach count below `resolved` is the * partial-removal signature. Logging only one would make a deduped call * indistinguishable from a half-failed one. */ export declare function resolveRemovalsOrRefuse(items: AttachmentListItem[], targets: string[], tool: string): ResolvedRemoval[]; /** * DELETE each resolved attachment from the draft. * * The `op: "detach"` line is emitted AFTER the DELETE returns, never before, so * a logged detach is one that landed. That makes the log a post-condition rather * than an intention: when a DELETE fails partway through a multi-file removal, * the detach lines state what the draft now carries rather than what was asked * for. * * A detach count below the `resolved` count on the preceding `op: "resolve"` * line, together with a terminal failure, IS the partial-removal signature. It * reads against `resolved` and not against `requested`: two targets naming one * file are collapsed, so a deduped call legitimately issues fewer DELETEs than * the caller passed targets and must not look half-failed. */ export declare function detachFromDraft(config: GraphClientConfig, draftId: string, removals: ResolvedRemoval[], tool: string): Promise; //# sourceMappingURL=detach.d.ts.map