Summary
processConfirmationResponse in src/server/processor/confirmation/confirmation.service.ts:172-199 only appends the attachment block to denialReason when !approved:
let denialReason: string | undefined;
if (!approved) {
const attachmentBlock = formatConfirmationAttachmentBlock(response.attachments);
...
}
A confirmation response with selectedOptionId: 'yes' (or 'yes_dont_ask') plus a non-empty attachments array therefore has the attachments silently discarded. The client guard at src/client/app.tsx:1375-1377 only forwards attachments for optionId === 'guidance' || operation === 'ask_user', so the GUI doesn't exercise this path, but the server contract is implicit and asymmetric:
- The REST schema (
routes/automations.ts) accepts any selectedOptionId + attachments
- The WS handler (
routes/ws/commands/confirmation-response.ts) accepts any selectedOptionId + attachments
- Only
formatConfirmationAttachmentBlock decides what reaches the model — and only on the !approved branch
Impact
Low frequency under the current GUI, but real for:
- Programmatic API consumers (REST + WS)
- Future client code that forgets the contract
- A potential future "ask_user-style" approval flow that wants to attach context
The data-loss is silent: no 400, no log, no client-visible signal.
Proposed fix
Reject the YES + attachments combination at the transport boundary so the contract is explicit. The client guard already enforces this for the GUI; the server should match.
I have a fix ready and will open a PR shortly.
Summary
processConfirmationResponseinsrc/server/processor/confirmation/confirmation.service.ts:172-199only appends the attachment block todenialReasonwhen!approved:A confirmation response with
selectedOptionId: 'yes'(or'yes_dont_ask') plus a non-emptyattachmentsarray therefore has the attachments silently discarded. The client guard atsrc/client/app.tsx:1375-1377only forwards attachments foroptionId === 'guidance' || operation === 'ask_user', so the GUI doesn't exercise this path, but the server contract is implicit and asymmetric:routes/automations.ts) accepts anyselectedOptionId+ attachmentsroutes/ws/commands/confirmation-response.ts) accepts anyselectedOptionId+ attachmentsformatConfirmationAttachmentBlockdecides what reaches the model — and only on the!approvedbranchImpact
Low frequency under the current GUI, but real for:
The data-loss is silent: no 400, no log, no client-visible signal.
Proposed fix
Reject the YES + attachments combination at the transport boundary so the contract is explicit. The client guard already enforces this for the GUI; the server should match.
I have a fix ready and will open a PR shortly.