{ "kind": "open-flow-workflow", "version": 1, "name": "Gmail AI Reply Draft", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_673ba010753b345d": { "name": "New Gmail messages every minute", "kind": "poll", "config": { "labelNamesOrIds": { "kind": "value", "value": [] }, "readStatus": { "kind": "value", "value": "All" }, "search": { "kind": "value", "value": "-from:me" }, "sender": { "kind": "value", "value": "" }, "includeDrafts": { "kind": "value", "value": false }, "includeSpamAndTrash": { "kind": "value", "value": false }, "maxMessagesPerPoll": { "kind": "value", "value": 25 } }, "definition": { "configInputs": [ { "description": "Only trigger on messages carrying all of these labels, referenced by name or ID.", "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "nullable": false, "handle": "labelNamesOrIds", "value": [] }, { "jsonSchema": { "type": "string", "enum": [ "Read", "Unread", "All" ] }, "nullable": false, "handle": "readStatus", "value": "All" }, { "description": "Gmail search-syntax query the message must match.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "search", "value": "" }, { "description": "Only trigger on messages whose sender matches this address or name.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "sender", "value": "" }, { "collapsed": true, "group": "Advanced settings" }, { "description": "Also trigger on draft messages.", "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "includeDrafts", "value": false }, { "description": "Also trigger on messages in SPAM and TRASH.", "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "includeSpamAndTrash", "value": false }, { "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100 }, "nullable": false, "handle": "maxMessagesPerPoll", "value": 25 } ], "definitionVersion": 2, "description": "Polls the Gmail mailbox and triggers when a new message is received.", "displayName": "New Message Received", "key": "gmail.on_message_received", "name": "on_message_received", "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "historyId": { "type": [ "string", "null" ] }, "labelIds": { "type": "array", "items": { "type": "string" } }, "messageId": { "type": "string" }, "messageTimestamp": { "type": "string" }, "sender": { "type": "string" }, "subject": { "type": "string" }, "threadId": { "type": "string" }, "to": { "type": "string" } }, "additionalProperties": false, "required": [ "messageId", "threadId", "historyId", "labelIds", "subject", "sender", "to", "messageTimestamp" ] } }, "nullable": false, "handle": "events" } ], "provider": "gmail", "type": "poll" }, "pollTimes": [ { "type": "every", "unit": "minute", "value": 1 } ] }, "n_5b990999e406c650": { "inputs": { "events": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_673ba010753b345d", "output": "events" } ] } }, "name": "Validate incoming message IDs", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "historyId": { "type": [ "string", "null" ] }, "labelIds": { "type": "array", "items": { "type": "string" } }, "messageId": { "type": "string" }, "messageTimestamp": { "type": "string" }, "sender": { "type": "string" }, "subject": { "type": "string" }, "threadId": { "type": "string" }, "to": { "type": "string" } }, "additionalProperties": false, "required": [ "messageId", "threadId", "historyId", "labelIds", "subject", "sender", "to", "messageTimestamp" ] } }, "nullable": false, "handle": "events" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "nullable": false, "handle": "messageIds" } ], "name": "Validate incoming message IDs", "moduleId": "n_bd6edd3825e9e5b5", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d5ddbaa9b69d94f2": { "inputs": { "messageIds": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_5b990999e406c650", "output": "messageIds" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d5ddbaa9b69d94f2", "output": "state" } ] }, "messageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_00bfcff2bcd7576d", "output": "messageId" } ] }, "threadId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_00bfcff2bcd7576d", "output": "threadId" } ] }, "from": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_00bfcff2bcd7576d", "output": "from" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_00bfcff2bcd7576d", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_00bfcff2bcd7576d", "output": "body" } ] } }, "name": "Collect all incoming email bodies", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "nullable": false, "handle": "messageIds" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": true, "handle": "messageId" }, { "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId" }, { "jsonSchema": { "type": "string", "description": "Normalized From header value." }, "nullable": true, "handle": "from" }, { "jsonSchema": { "type": "string", "description": "Normalized message subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Best-effort plain-text message body." }, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "messages": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "from": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string" } }, "required": [ "messageId", "threadId", "from", "subject", "body" ], "additionalProperties": false } } }, "required": [ "index", "messages" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "messageId" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect all incoming email bodies", "moduleId": "n_7b785db6630fc807", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_73784b3826a2c9b7": { "inputs": {}, "name": "More email bodies to read", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_d5ddbaa9b69d94f2", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_00bfcff2bcd7576d": { "inputs": { "messageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d5ddbaa9b69d94f2", "output": "messageId" } ] } }, "name": "Read one incoming email body", "kind": "task", "task": { "inputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" } ], "outputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": false, "handle": "threadId" }, { "description": "Normalized message subject line.", "jsonSchema": { "type": "string", "description": "Normalized message subject line." }, "nullable": false, "handle": "subject" }, { "description": "Normalized From header value.", "jsonSchema": { "type": "string", "description": "Normalized From header value." }, "nullable": false, "handle": "from" }, { "description": "Normalized To header value.", "jsonSchema": { "type": "string", "description": "Normalized To header value." }, "nullable": false, "handle": "to" }, { "description": "Message date string returned by Gmail.", "jsonSchema": { "type": "string", "description": "Message date string returned by Gmail." }, "nullable": false, "handle": "date" }, { "description": "Best-effort plain-text message body.", "jsonSchema": { "type": "string", "description": "Best-effort plain-text message body." }, "nullable": false, "handle": "body" } ], "name": "Read one incoming email body", "executor": { "kind": "connector", "action": "gmail.get_message" } } }, "n_74bce5bb306ca8f0": { "inputs": { "messages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d5ddbaa9b69d94f2", "output": "state", "field": "messages" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "state" } ] }, "completion": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_97077397a0a927fc", "output": "output" } ] } }, "name": "Collect all reply decisions", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "from": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string" } }, "required": [ "messageId", "threadId", "from", "subject", "body" ], "additionalProperties": false } }, "nullable": false, "handle": "messages" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object", "properties": { "needsReply": { "type": "boolean" } }, "required": [ "needsReply" ], "additionalProperties": false }, "nullable": true, "handle": "completion" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "decisions": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "needsReply": { "type": "boolean" } }, "required": [ "messageId", "needsReply" ], "additionalProperties": false } } }, "required": [ "index", "decisions" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "from": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string" } }, "required": [ "messageId", "threadId", "from", "subject", "body" ], "additionalProperties": false } }, "nullable": false, "handle": "selected" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect all reply decisions", "moduleId": "n_7cfb8c1e0e5c338a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8066f4de5c5f9bc3": { "inputs": {}, "name": "More emails to assess", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_97077397a0a927fc": { "inputs": { "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "body" } ] } }, "name": "Assess whether a reply is needed", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" } ], "outputs": [ { "description": "The final result produced by the Agent.", "jsonSchema": { "type": "object", "properties": { "needsReply": { "type": "boolean" } }, "required": [ "needsReply" ], "additionalProperties": false }, "nullable": false, "handle": "output" } ], "name": "Assess whether a reply is needed", "executor": { "kind": "agent", "code": false, "model": "YOUR_ASSESSMENT_GATEWAY_MODEL_ID", "prompt": "Your task is to assess if the message requires a response. Return in JSON format true if it does, false otherwise.\nMarketing emails don't require a response.\nReturn exactly {\"needsReply\":true} or {\"needsReply\":false}.\n\nSubject: {{subject}}\nMessage:\n{{body}}", "maxRounds": 1, "tools": [] } } }, "n_8fc0336df1ece242": { "inputs": { "messages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "selected" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8fc0336df1ece242", "output": "state" } ] }, "completion": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_aa525b651e335e3e", "output": "output" } ] } }, "name": "Collect all generated reply drafts", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "from": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string" } }, "required": [ "messageId", "threadId", "from", "subject", "body" ], "additionalProperties": false } }, "nullable": false, "handle": "messages" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "completion" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "drafts": { "type": "array", "items": { "type": "object", "properties": { "sourceMessageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "to": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string", "minLength": 1 } }, "required": [ "sourceMessageId", "threadId", "to", "subject", "body" ], "additionalProperties": false } } }, "required": [ "index", "drafts" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" } ], "name": "Collect all generated reply drafts", "moduleId": "n_95b0f4558c0d8a6c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8eca1f0d182e454d": { "inputs": {}, "name": "More selected replies to generate", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_8fc0336df1ece242", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_aa525b651e335e3e": { "inputs": { "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8fc0336df1ece242", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8fc0336df1ece242", "output": "body" } ] } }, "name": "Draft a professional reply", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" } ], "outputs": [ { "description": "The final result produced by the Agent.", "jsonSchema": { "type": "string" }, "nullable": false, "handle": "output" } ], "name": "Draft a professional reply", "executor": { "kind": "agent", "code": false, "model": "YOUR_REPLY_GATEWAY_MODEL_ID", "prompt": "You're a helpful personal assistant and your task is to draft replies on my behalf to my incoming emails. Whenever I provide some text from an email, return an appropriate draft reply for it and nothing else.\nEnsure that the reply is suitable for a professional email setting and addresses the topic in a clear, structured, and detailed manner.\nDo not make things up.\n\nDetailed instructions:\n- Be concise and maintain a business casual tone.\n- Start with \"Hello,\", and end with \"Best,\"\n- When replying to yes-no questions, draft 2 responses: one affirmative and one negative separated by \" - - - - - - - OR - - - - - - - \"\n- If you don't know an answer, you can leave placeholders like \"[YOUR_ANSWER_HERE]\".\n- Don't use any special formatting, only plain text.\n- Reply in the same language as the inbound email.\n\nSubject: {{subject}}\nMessage: {{body}}", "maxRounds": 1, "tools": [] } } }, "n_72767638e446dd1b": { "inputs": { "plans": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8fc0336df1ece242", "output": "state", "field": "drafts" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "state" } ] }, "draftId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_253905f3fcb2bd85", "output": "draftId" } ] }, "returnedThreadId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_253905f3fcb2bd85", "output": "threadId" } ] } }, "name": "Collect created Gmail draft IDs", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "sourceMessageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "to": { "type": "string", "minLength": 1 }, "subject": { "type": "string" }, "body": { "type": "string", "minLength": 1 } }, "required": [ "sourceMessageId", "threadId", "to", "subject", "body" ], "additionalProperties": false } }, "nullable": false, "handle": "plans" }, { "jsonSchema": { "type": "object" }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "string", "description": "Gmail draft ID." }, "nullable": true, "handle": "draftId" }, { "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "returnedThreadId" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "index": { "type": "integer", "minimum": 0 }, "drafts": { "type": "array", "items": { "type": "object", "properties": { "sourceMessageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "draftId": { "type": "string", "minLength": 1 } }, "required": [ "sourceMessageId", "threadId", "draftId" ], "additionalProperties": false } } }, "required": [ "index", "drafts" ], "additionalProperties": false }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "to" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "threadId" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Collect created Gmail draft IDs", "moduleId": "n_e352d320e4ea1ce6", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_bc9659068c42f138": { "inputs": {}, "name": "More drafts to save", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_253905f3fcb2bd85": { "inputs": { "to": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "to" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "body" } ] }, "threadId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "threadId" } ] }, "isHtml": { "kind": "value", "value": true } }, "name": "Create one draft in its conversation", "kind": "task", "task": { "inputs": [ { "description": "Primary recipient email address.", "jsonSchema": { "type": "string", "description": "Primary recipient email address." }, "nullable": true, "handle": "to", "value": null }, { "description": "Primary recipient email address.", "jsonSchema": { "type": "string", "description": "Primary recipient email address." }, "nullable": true, "handle": "recipientEmail", "value": null }, { "description": "Additional To recipients.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Additional To recipients." }, "nullable": true, "handle": "extraRecipients", "value": null }, { "description": "Cc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Cc recipients." }, "nullable": true, "handle": "cc", "value": null }, { "description": "Bcc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Bcc recipients." }, "nullable": true, "handle": "bcc", "value": null }, { "description": "Email subject line.", "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject", "value": null }, { "description": "Email body content.", "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body", "value": null }, { "description": "Reply or draft body content.", "jsonSchema": { "type": "string", "description": "Reply or draft body content." }, "nullable": true, "handle": "messageBody", "value": null }, { "description": "Set to true when the body content is already HTML.", "jsonSchema": { "type": "boolean", "description": "Set to true when the body content is already HTML." }, "nullable": true, "handle": "isHtml", "value": null }, { "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "description": "Attachment filename; omit for an unnamed attachment." }, "mimeType": { "type": "string", "description": "MIME type without parameters; defaults to application/octet-stream." }, "contentBase64": { "type": "string", "description": "Standard padded Base64 content. Empty string represents a zero-byte file. Supply this or fileUrl." }, "fileUrl": { "type": "string", "format": "uri", "description": "Public URL to download the attachment. Supply this or contentBase64." }, "contentId": { "type": "string", "minLength": 1, "description": "Bare Content-ID without cid: or angle brackets; reference it as cid: in HTML." }, "disposition": { "type": "string", "enum": [ "inline", "attachment" ], "description": "Defaults to inline when contentId is set, otherwise attachment. Inline requires contentId." } }, "additionalProperties": false, "description": "An attachment or CID inline resource.", "oneOf": [ { "required": [ "contentBase64" ] }, { "required": [ "fileUrl" ] } ] }, "maxItems": 100, "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId", "value": null }, { "description": "Gmail message ID to reply to. Requires mailbox read access; threadId must match when supplied.", "jsonSchema": { "type": "string", "minLength": 1, "description": "Gmail message ID to reply to. Requires mailbox read access; threadId must match when supplied." }, "nullable": true, "handle": "replyToMessageId", "value": null }, { "description": "Verified Gmail send-as alias to use in the From header.", "jsonSchema": { "type": "string", "description": "Verified Gmail send-as alias to use in the From header." }, "nullable": true, "handle": "fromEmail", "value": null } ], "outputs": [ { "description": "Gmail draft ID.", "jsonSchema": { "type": "string", "description": "Gmail draft ID." }, "nullable": false, "handle": "draftId" }, { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": true, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId" } ], "name": "Create one draft in its conversation", "executor": { "kind": "connector", "action": "gmail.create_email_draft" } } }, "n_6605b59a31cbcf05": { "name": "Reply decisions and created drafts", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "messageId": { "type": "string", "minLength": 1 }, "needsReply": { "type": "boolean" } }, "required": [ "messageId", "needsReply" ], "additionalProperties": false } }, "nullable": false, "handle": "decisions" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "sourceMessageId": { "type": "string", "minLength": 1 }, "threadId": { "type": "string", "minLength": 1 }, "draftId": { "type": "string", "minLength": 1 } }, "required": [ "sourceMessageId", "threadId", "draftId" ], "additionalProperties": false } }, "nullable": false, "handle": "drafts" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "decisions": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_74bce5bb306ca8f0", "output": "state", "field": "decisions" } ] }, "drafts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "state", "field": "drafts" } ] }, "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_72767638e446dd1b", "output": "count" } ] } } } }, "edges": [ { "source": "n_673ba010753b345d", "target": "n_5b990999e406c650" }, { "source": "n_5b990999e406c650", "target": "n_d5ddbaa9b69d94f2" }, { "source": "n_d5ddbaa9b69d94f2", "target": "n_73784b3826a2c9b7" }, { "source": "n_73784b3826a2c9b7", "target": "n_00bfcff2bcd7576d", "sourceHandle": "more" }, { "source": "n_00bfcff2bcd7576d", "target": "n_d5ddbaa9b69d94f2" }, { "source": "n_73784b3826a2c9b7", "target": "n_74bce5bb306ca8f0", "sourceHandle": "otherwise" }, { "source": "n_74bce5bb306ca8f0", "target": "n_8066f4de5c5f9bc3" }, { "source": "n_8066f4de5c5f9bc3", "target": "n_97077397a0a927fc", "sourceHandle": "more" }, { "source": "n_97077397a0a927fc", "target": "n_74bce5bb306ca8f0" }, { "source": "n_8066f4de5c5f9bc3", "target": "n_8fc0336df1ece242", "sourceHandle": "otherwise" }, { "source": "n_8fc0336df1ece242", "target": "n_8eca1f0d182e454d" }, { "source": "n_8eca1f0d182e454d", "target": "n_aa525b651e335e3e", "sourceHandle": "more" }, { "source": "n_aa525b651e335e3e", "target": "n_8fc0336df1ece242" }, { "source": "n_8eca1f0d182e454d", "target": "n_72767638e446dd1b", "sourceHandle": "otherwise" }, { "source": "n_72767638e446dd1b", "target": "n_bc9659068c42f138" }, { "source": "n_bc9659068c42f138", "target": "n_253905f3fcb2bd85", "sourceHandle": "more" }, { "source": "n_253905f3fcb2bd85", "target": "n_72767638e446dd1b" }, { "source": "n_bc9659068c42f138", "target": "n_6605b59a31cbcf05", "sourceHandle": "otherwise" } ] } }, "modules": { "n_bd6edd3825e9e5b5": { "name": "Validate incoming message IDs", "imports": [], "source": "export default function prepareReplyMessageIds({ events }) {\n if (!Array.isArray(events)) throw new Error('Gmail must supply a message event array.');\n const messageIds = events.map(event => event?.messageId);\n if (messageIds.some(id => typeof id !== 'string' || id.trim() === '') || new Set(messageIds).size !== messageIds.length) throw new Error('Every incoming Gmail message needs one unique message ID.');\n return { messageIds };\n}\n" }, "n_7b785db6630fc807": { "name": "Collect all incoming email bodies", "imports": [], "source": "export function validateReplyMessage(message) {\n for (const key of ['messageId', 'threadId', 'from']) if (typeof message?.[key] !== 'string' || message[key].trim() === '') throw new Error('Incoming Gmail message is missing ' + key + '.');\n if (typeof message.subject !== 'string' || typeof message.body !== 'string' || /[\\r\\n]/.test(message.from + message.subject)) throw new Error('Gmail subject, sender and body fields are invalid.');\n return { messageId: message.messageId, threadId: message.threadId, from: message.from, subject: message.subject, body: message.body };\n}\n\nexport default function collectReplyMessages({ messageIds, previousState, messageId, threadId, from, subject, body }) {\n let state;\n if (previousState === null) {\n if ([messageId, threadId, from, subject, body].some(value => value !== null)) throw new Error('Unexpected message before collection starts.');\n state = { index: 0, messages: [] };\n } else {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= messageIds.length || !Array.isArray(previousState.messages) || previousState.messages.length !== previousState.index || messageId !== messageIds[previousState.index]) throw new Error('The Gmail message does not match this read frame.');\n state = { index: previousState.index + 1, messages: [...previousState.messages, validateReplyMessage({ messageId, threadId, from, subject, body })] };\n }\n return { state, messageId: messageIds[state.index] ?? 'unused-completed-frame', more: state.index < messageIds.length };\n}\n" }, "n_7cfb8c1e0e5c338a": { "name": "Collect all reply decisions", "imports": [], "source": "export function validateReplyDecision(completion) {\n if (!completion || typeof completion !== 'object' || Array.isArray(completion) || typeof completion.needsReply !== 'boolean') throw new Error('The assessment must contain a boolean needsReply.');\n return completion.needsReply;\n}\n\nexport default function collectReplyDecisions({ messages, previousState, completion }) {\n let state;\n if (previousState === null) {\n if (completion !== null) throw new Error('Unexpected reply assessment before processing starts.');\n state = { index: 0, decisions: [] };\n } else {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= messages.length || !Array.isArray(previousState.decisions) || previousState.decisions.length !== previousState.index) throw new Error('Invalid Gmail assessment state.');\n const decision = { messageId: messages[previousState.index].messageId, needsReply: validateReplyDecision(completion) };\n state = { index: previousState.index + 1, decisions: [...previousState.decisions, decision] };\n }\n const more = state.index < messages.length, selected = more ? [] : messages.filter((_message, index) => state.decisions[index].needsReply);\n return { state, selected, subject: messages[state.index]?.subject ?? '', body: messages[state.index]?.body ?? '', more };\n}\n" }, "n_95b0f4558c0d8a6c": { "name": "Collect all generated reply drafts", "imports": [], "source": "export function prepareReplyDraft(message, completion) {\n if (typeof completion !== 'string' || completion.trim() === '') throw new Error('The reply model returned no draft text.');\n const escape = text => text.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('\"', '"').replaceAll(\"'\", ''');\n return { sourceMessageId: message.messageId, threadId: message.threadId, to: message.from, subject: 'Re: ' + message.subject, body: escape(completion).replace(/\\r\\n|\\r|\\n/g, '
\\n') };\n}\n\nexport default function collectReplyDrafts({ messages, previousState, completion }) {\n let state;\n if (previousState === null) {\n if (completion !== null) throw new Error('Unexpected reply before draft generation starts.');\n state = { index: 0, drafts: [] };\n } else {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= messages.length || !Array.isArray(previousState.drafts) || previousState.drafts.length !== previousState.index) throw new Error('Invalid Gmail draft generation state.');\n state = { index: previousState.index + 1, drafts: [...previousState.drafts, prepareReplyDraft(messages[previousState.index], completion)] };\n }\n return { state, subject: messages[state.index]?.subject ?? '', body: messages[state.index]?.body ?? '', more: state.index < messages.length };\n}\n" }, "n_e352d320e4ea1ce6": { "name": "Collect created Gmail draft IDs", "imports": [], "source": "export default function collectReplyReceipts({ plans, previousState, draftId, returnedThreadId }) {\n let state;\n if (previousState === null) {\n if (draftId !== null || returnedThreadId !== null) throw new Error('Unexpected draft receipt before writing starts.');\n state = { index: 0, drafts: [] };\n } else {\n if (!Number.isSafeInteger(previousState.index) || previousState.index < 0 || previousState.index >= plans.length || !Array.isArray(previousState.drafts) || previousState.drafts.length !== previousState.index) throw new Error('Invalid draft receipt state.');\n const plan = plans[previousState.index];\n if (typeof draftId !== 'string' || draftId.trim() === '' || (returnedThreadId !== null && returnedThreadId !== plan.threadId)) throw new Error('Gmail did not confirm a draft in the requested conversation; check Drafts before repeating.');\n state = { index: previousState.index + 1, drafts: [...previousState.drafts, { sourceMessageId: plan.sourceMessageId, threadId: plan.threadId, draftId }] };\n }\n const plan = plans[state.index];\n return { state, to: plan?.to ?? '', subject: plan?.subject ?? '', body: plan?.body ?? '', threadId: plan?.threadId ?? '', more: state.index < plans.length, count: state.index };\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }