@hostwebhook/node-types 1.52.16 → 1.52.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Google Docs AI Toolkit — las herramientas que un docsAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * Se escribe **una sola vez**, aquí, y la importan la api
6
+ * (`mcp-servers/toolkit-specs.ts`) y el dashboard (`NodeAsToolPicker`,
7
+ * `DocsActionDetail`). Es el sexto toolkit que vive en el paquete —van Gmail,
8
+ * native email, Calendar, Drive, Telegram y Sheets—; los que siguen con copia a
9
+ * mano en los dos repos son WhatsApp, Discord, Slack y Contacts, y de ese
10
+ * reparto salió que Contacts anunciara 15 herramientas y expusiera una.
11
+ *
12
+ * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para poder
13
+ * usarse sin adaptador. Los textos van en inglés porque los lee el modelo y
14
+ * quien mire la lista de herramientas del servidor MCP.
15
+ */
16
+ import type { DocsOperation } from './docs-operations';
17
+ export interface DocsToolkitParameter {
18
+ name: string;
19
+ /** El esquema MCP sólo admite estos tres. */
20
+ type: 'string' | 'number' | 'boolean';
21
+ description: string;
22
+ required: boolean;
23
+ }
24
+ export interface DocsToolkitSpec {
25
+ operation: DocsOperation;
26
+ label: string;
27
+ toolName: string;
28
+ description: string;
29
+ parameters: DocsToolkitParameter[];
30
+ /** Escribe en el documento del usuario. Lo usa la confirmación previa del AI Node. */
31
+ destructive?: boolean;
32
+ }
33
+ export declare const DOCS_TOOLKIT_SPECS: DocsToolkitSpec[];
34
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
35
+ export declare const DOCS_TOOLKIT_BY_TOOL_NAME: Record<string, DocsToolkitSpec>;
36
+ /**
37
+ * El parámetro que el nodo puede rellenar por defecto. Se exporta para que la
38
+ * api y el dashboard no vuelvan a escribir la lista a mano — `create_doc` no
39
+ * lo declara y por eso se queda fuera de la inyección sin necesidad de
40
+ * excepciones.
41
+ */
42
+ export declare const DOCS_TOOLKIT_DEFAULTABLE: readonly ["documentId"];
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * Google Docs AI Toolkit — las herramientas que un docsAction expone cuando
4
+ * `aiEnabled` está encendido.
5
+ *
6
+ * Se escribe **una sola vez**, aquí, y la importan la api
7
+ * (`mcp-servers/toolkit-specs.ts`) y el dashboard (`NodeAsToolPicker`,
8
+ * `DocsActionDetail`). Es el sexto toolkit que vive en el paquete —van Gmail,
9
+ * native email, Calendar, Drive, Telegram y Sheets—; los que siguen con copia a
10
+ * mano en los dos repos son WhatsApp, Discord, Slack y Contacts, y de ese
11
+ * reparto salió que Contacts anunciara 15 herramientas y expusiera una.
12
+ *
13
+ * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para poder
14
+ * usarse sin adaptador. Los textos van en inglés porque los lee el modelo y
15
+ * quien mire la lista de herramientas del servidor MCP.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = void 0;
19
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
20
+ /*
21
+ * El documento del nodo va como **defecto** y el modelo puede cambiarlo — la
22
+ * misma decisión y la misma maquinaria que la hoja de Sheets:
23
+ * `toolkitSpecToMcpTool(spec, nombre, { documentId })` lo saca de `required`
24
+ * **y le cuenta el valor al modelo** en la descripción. Las dos cosas o
25
+ * ninguna: mientras el esquema diga `required`, el LLM siempre lo rellena y el
26
+ * documento configurado no se usaría jamás.
27
+ *
28
+ * Por eso aquí se declara `required: true`: lo es para la API de Google, y es
29
+ * el defecto del nodo quien lo vuelve opcional, nodo por nodo.
30
+ *
31
+ * `create_doc` es el único que NO lo declara —todavía no hay documento al que
32
+ * apuntar— y su nombre de campo es `title`. Si se llamara `documentId`, la
33
+ * inyección de defectos se lo rellenaría con el documento del nodo y el
34
+ * documento nuevo nacería con el id del viejo por título.
35
+ */
36
+ const documento = () => p('documentId', 'Id of the Google Docs document — the long chunk of its URL, between /d/ and /edit.');
37
+ exports.DOCS_TOOLKIT_SPECS = [
38
+ {
39
+ operation: 'readDoc',
40
+ label: 'Read document',
41
+ toolName: 'read_doc',
42
+ description: 'Read the whole document as plain text. Use it before editing when you do not know what the document already says — replace_text and append_text act blindly otherwise.',
43
+ parameters: [documento()],
44
+ },
45
+ {
46
+ operation: 'createDoc',
47
+ label: 'Create document',
48
+ toolName: 'create_doc',
49
+ description: 'Create a new Google Docs document and return its id. The id it returns is what the other tools take as documentId.',
50
+ parameters: [
51
+ p('title', 'Name of the new document.'),
52
+ p('content', 'Initial body, as HTML. Basic tags work (<p>, <b>, <i>, <ul>, <li>, <h1>…). Plain text is fine too. Leave it out for an empty document.', false),
53
+ ],
54
+ destructive: true,
55
+ },
56
+ {
57
+ operation: 'appendText',
58
+ label: 'Append text',
59
+ toolName: 'append_text',
60
+ description: 'Add content at the END of the document. It never replaces what is already there, so it is the safe way to add a paragraph, a note or a log line.',
61
+ parameters: [
62
+ p('content', 'What to add, as HTML. Basic tags work (<p>, <b>, <i>, <ul>, <li>, <h1>…). Plain text is fine too.'),
63
+ documento(),
64
+ ],
65
+ destructive: true,
66
+ },
67
+ {
68
+ operation: 'replaceText',
69
+ label: 'Replace text',
70
+ toolName: 'replace_text',
71
+ description: 'Find every occurrence of a piece of text and swap it for another. It matches literally and it is case-sensitive; a search string that appears nowhere changes nothing and is not an error.',
72
+ parameters: [
73
+ p('searchText', 'Exact text to look for. Every occurrence is replaced, not just the first.'),
74
+ p('replaceWith', 'Text to put in its place. An empty string deletes the matches.'),
75
+ documento(),
76
+ ],
77
+ destructive: true,
78
+ },
79
+ {
80
+ operation: 'insertTable',
81
+ label: 'Insert table',
82
+ toolName: 'insert_table',
83
+ description: 'Insert an empty table at the end of the document. It creates the grid only — fill the cells afterwards with replace_text, or build the content as HTML and use append_text instead.',
84
+ parameters: [
85
+ p('rows', 'Number of rows, including the header row if you want one.', true, 'number'),
86
+ p('cols', 'Number of columns.', true, 'number'),
87
+ documento(),
88
+ ],
89
+ destructive: true,
90
+ },
91
+ ];
92
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
93
+ exports.DOCS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.DOCS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
94
+ /**
95
+ * El parámetro que el nodo puede rellenar por defecto. Se exporta para que la
96
+ * api y el dashboard no vuelvan a escribir la lista a mano — `create_doc` no
97
+ * lo declara y por eso se queda fuera de la inyección sin necesidad de
98
+ * excepciones.
99
+ */
100
+ exports.DOCS_TOOLKIT_DEFAULTABLE = ['documentId'];
@@ -16,3 +16,175 @@ export declare const GMAIL_OPERATIONS: readonly ["send", "sendAndWaitForResponse
16
16
  export type GmailOperation = (typeof GMAIL_OPERATIONS)[number];
17
17
  /** Type guard — useful when validating untrusted input (DTOs, tool calls). */
18
18
  export declare function isGmailOperation(value: unknown): value is GmailOperation;
19
+ /**
20
+ * Esquema de formulario, grupos del desplegable y toolkits de Gmail.
21
+ *
22
+ * El enum de arriba ya estaba aquí y ya lo importaban la api (DTO + entidad) y
23
+ * el dashboard. Lo que baja ahora es todo lo demás, que vivía escrito a mano en
24
+ * cuatro sitios:
25
+ *
26
+ * - `OP_SCHEMA` de `OperationFieldsRenderer.tsx` — 17 ops, 53 campos
27
+ * - `OPERATION_GROUPS` de `OperationSelector.tsx` — 16 ops con etiqueta,
28
+ * descripción y grupo
29
+ * - `GMAIL_OPERATION_SPECS` del dashboard y **la misma lista otra vez** en
30
+ * `api/src/mcp-servers/toolkit-specs.ts` — 16 herramientas cada una
31
+ * - `NATIVE_EMAIL_OPERATION_SPECS`, igual, por duplicado
32
+ *
33
+ * Las dos copias del toolkit estaban idénticas el día de la mudanza (0
34
+ * divergencias medidas), lo cual es suerte y no diseño: viven en repos
35
+ * distintos, así que **ningún test podía compararlas**. Por eso bajan.
36
+ *
37
+ * OJO CON EL NOMBRE. En el dashboard `GMAIL_OPERATION_SPECS` eran las specs del
38
+ * *toolkit*, al revés que en los otros diez nodos. Aquí se respeta la convención
39
+ * del paquete: `*_OPERATION_SPECS` es el **formulario** y `*_TOOLKIT_SPECS` son
40
+ * las **herramientas**.
41
+ */
42
+ /** Encabezados del desplegable, en orden de aparición. */
43
+ export declare const GMAIL_OPERATION_GROUPS: readonly ["Messages", "Drafts", "Labels", "Threads"];
44
+ export type GmailOperationGroup = (typeof GMAIL_OPERATION_GROUPS)[number];
45
+ /**
46
+ * Tipo de control del formulario. Todos tienen precedente en el paquete salvo
47
+ * `emails`, que es propio de este nodo (el `EmailChipsInput` con
48
+ * autocompletado), como el `docPicker` de Docs.
49
+ *
50
+ * - `string` — `TemplateInput` de una línea
51
+ * - `htmlTemplate` — cuerpo con el interruptor Plain <-> Advanced (HTML).
52
+ * Igual que en Docs.
53
+ * - `emails` — `EmailChipsInput`; se guarda como **array**
54
+ * - `csvList` — se guarda como array y se edita como texto separado por
55
+ * comas. El mismo de Contacts; perder el array al guardar
56
+ * rompe el nodo en ejecución
57
+ * - `select` — `<select>`; las etiquetas no son los valores, así que
58
+ * `options` es obligatorio
59
+ * - `number`, `boolean` — lo que parecen
60
+ *
61
+ * No hay `min`/`max`: hoy ningún campo numérico de este nodo los usa, y
62
+ * añadirlos sin que nadie los lea es el `color` que nadie leía en Postgres.
63
+ */
64
+ export type GmailParamType = 'string' | 'htmlTemplate' | 'emails' | 'csvList' | 'select' | 'number' | 'boolean';
65
+ export interface GmailParamSpec {
66
+ /** Clave dentro de `operationConfig`. */
67
+ name: string;
68
+ /** Etiqueta encima del control. */
69
+ label: string;
70
+ type: GmailParamType;
71
+ /** Lo exige la operación. Metadato: el formulario no bloquea el guardado. */
72
+ required?: boolean;
73
+ /** Texto de ayuda debajo del control (el `help` de antes). */
74
+ description?: string;
75
+ placeholder?: string;
76
+ /** Sólo `htmlTemplate`: lenguaje del editor cuando está en modo Advanced. */
77
+ language?: string;
78
+ /** Sólo `select`. */
79
+ options?: ReadonlyArray<{
80
+ value: string;
81
+ label: string;
82
+ }>;
83
+ }
84
+ export interface GmailOperationSpec {
85
+ /** Etiqueta del desplegable y de la página. */
86
+ label: string;
87
+ /**
88
+ * Etiqueta corta para la píldora del lienzo, que va en `uppercase` sobre una
89
+ * tarjeta de 260px. Sin esto la tarjeta pintaba el `operation` crudo y decía
90
+ * `SENDANDWAITFORRESPONSE`; es el mismo defecto que tenía Mongo.
91
+ */
92
+ labelShort: string;
93
+ /** Línea debajo de la etiqueta en el desplegable. */
94
+ description?: string;
95
+ /**
96
+ * Grupo del desplegable. **Ausente = no se ofrece en modo de una sola op**,
97
+ * que hoy es el caso de `sendDraft` y sólo de él.
98
+ */
99
+ group?: GmailOperationGroup;
100
+ /** Campos, en orden de pantalla. */
101
+ params: GmailParamSpec[];
102
+ }
103
+ /**
104
+ * El orden de las claves dentro de cada grupo **es** el orden del desplegable,
105
+ * igual que en Contacts: mover una clave mueve la pantalla.
106
+ *
107
+ * `send` ya no lleva `cc` ni `bcc`. Estaban declaradas y no se pintaban nunca —el
108
+ * renderizador se invocaba tras `operation !== "send"`— y la ruta determinista no
109
+ * las sabe mandar: `emailSender.send` recibe to/subject/html y nada más. Darles
110
+ * soporte es una función nueva, no una mudanza.
111
+ */
112
+ export declare const GMAIL_OPERATION_SPECS: Record<GmailOperation, GmailOperationSpec>;
113
+ export declare const GMAIL_DROPDOWN_OPERATIONS: readonly GmailOperation[];
114
+ export declare const GMAIL_TOOLKIT_OPERATIONS: readonly GmailOperation[];
115
+ /**
116
+ * Las herramientas que un emailAction con Gmail expone cuando `aiEnabled` está
117
+ * encendido. La forma es la que ya consume `toolkitSpecToMcpTool` en la api,
118
+ * para que pueda usarse sin adaptador. Los textos van en inglés porque los lee
119
+ * el modelo y quien mire la lista del servidor MCP.
120
+ *
121
+ * Ninguna lleva `destructive`, ni `delete_gmail_message` ni `trash_thread`.
122
+ * Lo merecerían —el toolkit de Drive marca sus borrados— pero hoy ninguna de las
123
+ * dos copias lo tenía y esto es una mudanza, no un cambio de comportamiento.
124
+ */
125
+ export interface GmailToolkitParameter {
126
+ name: string;
127
+ type: 'string' | 'number' | 'boolean';
128
+ description: string;
129
+ required: boolean;
130
+ }
131
+ export interface GmailToolkitSpec {
132
+ operation: GmailOperation;
133
+ label: string;
134
+ /**
135
+ * Nombre con el que lo llama el LLM. Lleva `_gmail_` o `_email` para no
136
+ * chocar con el `send_telegram_message` ni el `create_event` de Calendar
137
+ * cuando un mismo AI Node tiene varios toolkits encendidos.
138
+ */
139
+ toolName: string;
140
+ description: string;
141
+ parameters: GmailToolkitParameter[];
142
+ destructive?: boolean;
143
+ }
144
+ export declare const GMAIL_TOOLKIT_SPECS: GmailToolkitSpec[];
145
+ /**
146
+ * La op que espera, **fuera** del toolkit y aparte a propósito. Sigue haciendo
147
+ * falta para dos cosas que no son el toolkit: reconocer la operación de una
148
+ * herramienta ya guardada (si desapareciera, un `send_email_and_wait` creado antes
149
+ * se quedaría sin etiqueta) y el modo de una sola op elegido como herramienta
150
+ * suelta, donde el pipeline sí espera el clic.
151
+ */
152
+ export declare const GMAIL_SEND_AND_WAIT_TOOL_SPEC: GmailToolkitSpec;
153
+ /** Todo lo que el nodo sabe ejecutar, se ofrezca o no como herramienta. */
154
+ export declare const GMAIL_ALL_TOOLKIT_SPECS: GmailToolkitSpec[];
155
+ /** El toolkit del proveedor nativo (Resend): una sola herramienta. */
156
+ export declare const NATIVE_EMAIL_TOOLKIT_SPECS: GmailToolkitSpec[];
157
+ export declare const GMAIL_TOOLKIT_BY_TOOL_NAME: Record<string, GmailToolkitSpec>;
158
+ export declare const NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME: Record<string, GmailToolkitSpec>;
159
+ /**
160
+ * De dónde salen `to` / `subject` / `body` de un `send`.
161
+ *
162
+ * Los tres se guardaban planos en la raíz de la entidad mientras las otras 16
163
+ * operaciones usaban `operationConfig`. Desde la unificación **se escribe
164
+ * siempre en `operationConfig`**, y esto resuelve la lectura para que ninguna
165
+ * fila anterior se rompa: primero `operationConfig`, y si no está, el campo
166
+ * plano.
167
+ *
168
+ * Vive en el paquete a propósito. Si cada repo se escribiera su propio helper
169
+ * volveríamos a tener dos copias de la misma regla, que es justo lo que esta
170
+ * mudanza viene a quitar. Lo usan los 8 sitios que leían los campos planos: el
171
+ * envío, el send-and-wait, las entregas, la página, la tarjeta del lienzo, el
172
+ * resumen del panel y la validación.
173
+ *
174
+ * `??` y no `||`: si el usuario **vacía** los destinatarios, `operationConfig.to`
175
+ * es `[]` y tiene que ganar. Con `||` resucitaría el valor viejo del campo plano.
176
+ */
177
+ export interface GmailSendFieldSource {
178
+ to?: unknown;
179
+ subject?: unknown;
180
+ body?: unknown;
181
+ operationConfig?: Record<string, unknown> | null;
182
+ }
183
+ export interface GmailSendFields {
184
+ to: string[];
185
+ subject: string;
186
+ body: string;
187
+ }
188
+ /** Los tres campos que un día vivieron en la raíz. */
189
+ export declare const GMAIL_SEND_LEGACY_FIELDS: readonly ["to", "subject", "body"];
190
+ export declare function resolveGmailSendFields(entity: GmailSendFieldSource | null | undefined): GmailSendFields;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GMAIL_OPERATIONS = void 0;
3
+ exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATIONS = void 0;
4
4
  exports.isGmailOperation = isGmailOperation;
5
+ exports.resolveGmailSendFields = resolveGmailSendFields;
5
6
  /**
6
7
  * Gmail operation enum — single source of truth across the API, Dashboard,
7
8
  * and Message Broker. Used by:
@@ -45,3 +46,520 @@ exports.GMAIL_OPERATIONS = [
45
46
  function isGmailOperation(value) {
46
47
  return typeof value === 'string' && exports.GMAIL_OPERATIONS.includes(value);
47
48
  }
49
+ /**
50
+ * Esquema de formulario, grupos del desplegable y toolkits de Gmail.
51
+ *
52
+ * El enum de arriba ya estaba aquí y ya lo importaban la api (DTO + entidad) y
53
+ * el dashboard. Lo que baja ahora es todo lo demás, que vivía escrito a mano en
54
+ * cuatro sitios:
55
+ *
56
+ * - `OP_SCHEMA` de `OperationFieldsRenderer.tsx` — 17 ops, 53 campos
57
+ * - `OPERATION_GROUPS` de `OperationSelector.tsx` — 16 ops con etiqueta,
58
+ * descripción y grupo
59
+ * - `GMAIL_OPERATION_SPECS` del dashboard y **la misma lista otra vez** en
60
+ * `api/src/mcp-servers/toolkit-specs.ts` — 16 herramientas cada una
61
+ * - `NATIVE_EMAIL_OPERATION_SPECS`, igual, por duplicado
62
+ *
63
+ * Las dos copias del toolkit estaban idénticas el día de la mudanza (0
64
+ * divergencias medidas), lo cual es suerte y no diseño: viven en repos
65
+ * distintos, así que **ningún test podía compararlas**. Por eso bajan.
66
+ *
67
+ * OJO CON EL NOMBRE. En el dashboard `GMAIL_OPERATION_SPECS` eran las specs del
68
+ * *toolkit*, al revés que en los otros diez nodos. Aquí se respeta la convención
69
+ * del paquete: `*_OPERATION_SPECS` es el **formulario** y `*_TOOLKIT_SPECS` son
70
+ * las **herramientas**.
71
+ */
72
+ /** Encabezados del desplegable, en orden de aparición. */
73
+ exports.GMAIL_OPERATION_GROUPS = ['Messages', 'Drafts', 'Labels', 'Threads'];
74
+ /**
75
+ * El orden de las claves dentro de cada grupo **es** el orden del desplegable,
76
+ * igual que en Contacts: mover una clave mueve la pantalla.
77
+ *
78
+ * `send` ya no lleva `cc` ni `bcc`. Estaban declaradas y no se pintaban nunca —el
79
+ * renderizador se invocaba tras `operation !== "send"`— y la ruta determinista no
80
+ * las sabe mandar: `emailSender.send` recibe to/subject/html y nada más. Darles
81
+ * soporte es una función nueva, no una mudanza.
82
+ */
83
+ exports.GMAIL_OPERATION_SPECS = {
84
+ send: {
85
+ label: 'Send message',
86
+ labelShort: 'SEND',
87
+ description: 'Send a new email',
88
+ group: 'Messages',
89
+ params: [
90
+ { name: 'to', label: 'To', type: 'emails', required: true, placeholder: '{{payload.email}}' },
91
+ { name: 'subject', label: 'Subject', type: 'string', required: true, placeholder: 'Order #{{payload.id}}' },
92
+ { name: 'body', label: 'Body (HTML)', type: 'htmlTemplate', required: true, placeholder: '<p>Hi {{payload.name}}...</p>', language: 'html' },
93
+ ],
94
+ },
95
+ sendAndWaitForResponse: {
96
+ label: 'Send and wait for response',
97
+ labelShort: 'SEND & WAIT',
98
+ description: 'Send with Approve / Disapprove buttons; pause workflow until clicked',
99
+ group: 'Messages',
100
+ params: [
101
+ { name: 'to', label: 'To', type: 'emails', required: true, placeholder: '{{payload.email}}', description: 'Recipient(s) who will see the Approve / Disapprove buttons.' },
102
+ { name: 'subject', label: 'Subject', type: 'string', required: true, placeholder: 'Approve order #{{payload.id}}?' },
103
+ { name: 'body', label: 'Body (HTML)', type: 'htmlTemplate', required: true, placeholder: '<p>Please review and approve...</p>', language: 'html', description: 'Approve / Disapprove buttons are appended automatically below your content.' },
104
+ { name: 'approveLabel', label: 'Approve button label', type: 'string', placeholder: 'Approve', description: 'Default: Approve' },
105
+ { name: 'includeDisapprove', label: 'Include Disapprove button', type: 'boolean', description: 'Off = approve-only confirmation. Default: on.' },
106
+ { name: 'disapproveLabel', label: 'Disapprove button label', type: 'string', placeholder: 'Disapprove', description: 'Only used when Disapprove button is enabled.' },
107
+ { name: 'requireDisapproveComment', label: 'Require comment on disapprove', type: 'boolean', description: 'Off = 1-click decline. On = recipient sees a feedback form before submitting; the comment lands in payload._waitResponse.comment for downstream AI/Conditional nodes to read.' },
108
+ { name: 'disapproveCommentLabel', label: 'Comment form label', type: 'string', placeholder: 'Why are you disapproving?', description: "Heading shown above the textarea. Only used when 'Require comment' is on." },
109
+ { name: 'disapproveCommentPlaceholder', label: 'Comment placeholder', type: 'string', placeholder: 'Tell the sender why — this helps them improve the next attempt.', description: "Hint shown inside the empty textarea. Only used when 'Require comment' is on." },
110
+ { name: 'timeoutMinutes', label: 'Timeout (minutes)', type: 'number', placeholder: '1440', description: 'Auto-resolve as approved=false after this many minutes. Default 1440 (24h).' },
111
+ ],
112
+ },
113
+ createDraft: {
114
+ label: 'Create draft',
115
+ labelShort: 'DRAFT',
116
+ description: 'Save a draft without sending',
117
+ group: 'Drafts',
118
+ params: [
119
+ { name: 'to', label: 'To', type: 'emails', required: true, placeholder: '{{payload.email}}', description: 'Recipients the draft will be addressed to' },
120
+ { name: 'subject', label: 'Subject', type: 'string', placeholder: 'Draft: {{payload.topic}}' },
121
+ { name: 'body', label: 'Body (HTML)', type: 'htmlTemplate', language: 'html' },
122
+ { name: 'cc', label: 'CC', type: 'emails' },
123
+ { name: 'bcc', label: 'BCC', type: 'emails' },
124
+ { name: 'threadId', label: 'Thread ID', type: 'string', description: 'Optional — attach draft to an existing thread' },
125
+ { name: 'inReplyTo', label: 'In-Reply-To', type: 'string', description: 'Optional Message-ID header' },
126
+ { name: 'references', label: 'References', type: 'string', description: 'Optional References chain' },
127
+ ],
128
+ },
129
+ sendDraft: {
130
+ label: 'Send draft',
131
+ labelShort: 'SEND DRAFT',
132
+ description: 'Send a draft that createDraft already saved, in one shot',
133
+ params: [
134
+ { name: 'draftId', label: 'Draft ID', type: 'string', required: true, placeholder: '{{payload.draftId}}', description: 'Id returned by createDraft. The draft moves from Drafts/ to Sent/ in one shot — no orphans.' },
135
+ ],
136
+ },
137
+ replyToThread: {
138
+ label: 'Reply to thread',
139
+ labelShort: 'REPLY',
140
+ description: 'Reply preserving thread headers',
141
+ group: 'Messages',
142
+ params: [
143
+ { name: 'threadId', label: 'Thread ID', type: 'string', required: true, placeholder: '{{payload.threadId}}' },
144
+ { name: 'body', label: 'Body (HTML)', type: 'htmlTemplate', required: true, language: 'html' },
145
+ { name: 'subject', label: 'Subject', type: 'string', description: 'Optional — defaults to "Re: <original>"' },
146
+ { name: 'to', label: 'To', type: 'emails', description: 'Optional — defaults to last sender in thread' },
147
+ { name: 'cc', label: 'CC', type: 'emails' },
148
+ { name: 'bcc', label: 'BCC', type: 'emails' },
149
+ ],
150
+ },
151
+ getMessage: {
152
+ label: 'Get message',
153
+ labelShort: 'GET',
154
+ description: 'Fetch a single message by id',
155
+ group: 'Messages',
156
+ params: [
157
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true, placeholder: '{{payload.messageId}}' },
158
+ { name: 'format', label: 'Format', type: 'select', options: [{ value: 'full', label: 'full' }, { value: 'metadata', label: 'metadata' }, { value: 'minimal', label: 'minimal' }] },
159
+ ],
160
+ },
161
+ listMessages: {
162
+ label: 'List messages',
163
+ labelShort: 'LIST',
164
+ description: 'Search inbox (Gmail query syntax)',
165
+ group: 'Messages',
166
+ params: [
167
+ { name: 'query', label: 'Query', type: 'string', placeholder: 'from:alice@example.com is:unread', description: 'Gmail search query — supports operators like from:, is:, has:, newer_than:' },
168
+ { name: 'labelIds', label: 'Label IDs', type: 'csvList', placeholder: 'INBOX,UNREAD', description: 'Comma-separated label IDs (use Gmail listLabels op to discover)' },
169
+ { name: 'maxResults', label: 'Max results', type: 'number', placeholder: '25' },
170
+ { name: 'includeSpamTrash', label: 'Include spam + trash', type: 'boolean' },
171
+ { name: 'expand', label: 'Expand each result (≤20)', type: 'boolean', description: 'Fetch headers + snippet per message. Off = id+threadId stubs only.' },
172
+ { name: 'expandFormat', label: 'Expand format', type: 'select', options: [{ value: 'metadata', label: 'Metadata (headers + snippet)' }, { value: 'full', label: 'Full body (expensive)' }], description: 'metadata = ~150-char snippet (default, cheap). full = entire body (expensive on tokens).' },
173
+ ],
174
+ },
175
+ deleteMessage: {
176
+ label: 'Delete message',
177
+ labelShort: 'DELETE',
178
+ description: 'Trash (default) or hard delete',
179
+ group: 'Messages',
180
+ params: [
181
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true },
182
+ { name: 'permanent', label: 'Permanent delete (otherwise moves to trash)', type: 'boolean' },
183
+ ],
184
+ },
185
+ markRead: {
186
+ label: 'Mark as read',
187
+ labelShort: 'MARK READ',
188
+ description: 'Remove UNREAD label',
189
+ group: 'Messages',
190
+ params: [
191
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true },
192
+ ],
193
+ },
194
+ markUnread: {
195
+ label: 'Mark as unread',
196
+ labelShort: 'MARK UNREAD',
197
+ description: 'Add UNREAD label',
198
+ group: 'Messages',
199
+ params: [
200
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true },
201
+ ],
202
+ },
203
+ addLabel: {
204
+ label: 'Add label',
205
+ labelShort: 'ADD LABEL',
206
+ description: 'Attach label IDs to a message',
207
+ group: 'Messages',
208
+ params: [
209
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true },
210
+ { name: 'labelIds', label: 'Label IDs to add', type: 'csvList', required: true, placeholder: 'Label_12345', description: 'Comma-separated' },
211
+ ],
212
+ },
213
+ removeLabel: {
214
+ label: 'Remove label',
215
+ labelShort: 'REMOVE LABEL',
216
+ description: 'Detach label IDs from a message',
217
+ group: 'Messages',
218
+ params: [
219
+ { name: 'messageId', label: 'Message ID', type: 'string', required: true },
220
+ { name: 'labelIds', label: 'Label IDs to remove', type: 'csvList', required: true, placeholder: 'Label_12345' },
221
+ ],
222
+ },
223
+ listLabels: {
224
+ label: 'List labels',
225
+ labelShort: 'LABELS',
226
+ description: 'All user + system labels',
227
+ group: 'Labels',
228
+ params: [],
229
+ },
230
+ createLabel: {
231
+ label: 'Create label',
232
+ labelShort: 'NEW LABEL',
233
+ description: 'New user label (supports / for nesting)',
234
+ group: 'Labels',
235
+ params: [
236
+ { name: 'name', label: 'Label name', type: 'string', required: true, placeholder: 'Clients/Active', description: 'Use / for nested labels' },
237
+ { name: 'messageListVisibility', label: 'Message list visibility', type: 'select', options: [{ value: 'show', label: 'show' }, { value: 'hide', label: 'hide' }] },
238
+ { name: 'labelListVisibility', label: 'Label list visibility', type: 'select', options: [{ value: 'labelShow', label: 'labelShow' }, { value: 'labelShowIfUnread', label: 'labelShowIfUnread' }, { value: 'labelHide', label: 'labelHide' }] },
239
+ ],
240
+ },
241
+ getThread: {
242
+ label: 'Get thread',
243
+ labelShort: 'THREAD',
244
+ description: 'Fetch an entire conversation',
245
+ group: 'Threads',
246
+ params: [
247
+ { name: 'threadId', label: 'Thread ID', type: 'string', required: true },
248
+ { name: 'format', label: 'Format', type: 'select', options: [{ value: 'full', label: 'full' }, { value: 'metadata', label: 'metadata' }, { value: 'minimal', label: 'minimal' }] },
249
+ ],
250
+ },
251
+ trashThread: {
252
+ label: 'Trash thread',
253
+ labelShort: 'TRASH',
254
+ description: 'Move all thread messages to trash',
255
+ group: 'Threads',
256
+ params: [
257
+ { name: 'threadId', label: 'Thread ID', type: 'string', required: true },
258
+ ],
259
+ },
260
+ untrashThread: {
261
+ label: 'Untrash thread',
262
+ labelShort: 'UNTRASH',
263
+ description: 'Restore a thread from trash',
264
+ group: 'Threads',
265
+ params: [
266
+ { name: 'threadId', label: 'Thread ID', type: 'string', required: true },
267
+ ],
268
+ },
269
+ };
270
+ /*
271
+ * Los dos subconjuntos de las 17, **declarados**. Antes había que deducirlos de
272
+ * qué lista se dejaba a quién fuera, y las dos ausencias son decisiones tomadas:
273
+ *
274
+ * - `sendDraft` no se ofrece en el desplegable porque sólo tiene sentido
275
+ * detrás de un `createDraft` de la misma conversación. Sí es herramienta.
276
+ * - `sendAndWaitForResponse` no es herramienta porque por esa vía no espera:
277
+ * el modelo recibe algo inmediato y acaba diciendo que ya se aprobó. Sí se
278
+ * ofrece en el desplegable, con un Conditional detrás leyendo
279
+ * `_waitResponse.approved`.
280
+ *
281
+ * Que cada una siga teniendo 16 lo fija un test. No las "arregles".
282
+ */
283
+ exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATIONS.filter((op) => exports.GMAIL_OPERATION_SPECS[op].group !== undefined);
284
+ exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_OPERATIONS.filter((op) => op !== 'sendAndWaitForResponse');
285
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
286
+ exports.GMAIL_TOOLKIT_SPECS = [
287
+ {
288
+ operation: 'send',
289
+ label: 'Send message',
290
+ toolName: 'send_email',
291
+ description: 'Send a fresh Gmail message that you compose from scratch right now. USAGE RULES: (1) ' +
292
+ 'Only use this when the user asks for a brand-new email and there is NO recent draft to ' +
293
+ 'confirm. (2) If you previously called createDraft in this conversation and the user just ' +
294
+ 'confirmed it, do NOT call send — call sendDraft with that draftId instead. Calling send ' +
295
+ 're-composes the message from your memory and risks sending stale content from an earlier ' +
296
+ "conversation. (3) Compose the body fresh from the user's CURRENT request. Never reuse " +
297
+ 'content recalled from memory unless the user explicitly references it. (4) Not for ' +
298
+ 'replies inside an existing thread — use replyToThread for that.',
299
+ parameters: [
300
+ p('to', 'Recipient email address(es). Single string or comma-separated list.'),
301
+ p('subject', 'Email subject line.'),
302
+ p('body', 'HTML-formatted email body.'),
303
+ p('cc', 'Optional CC recipients.', false),
304
+ p('bcc', 'Optional BCC recipients.', false),
305
+ ],
306
+ },
307
+ {
308
+ operation: 'createDraft',
309
+ label: 'Create draft',
310
+ toolName: 'draft_email',
311
+ description: "Save a draft in the user's Gmail drafts folder. Does NOT send. Use when asked to draft, " +
312
+ 'prepare, or compose without sending. USAGE RULES: (1) Compose the body fresh from the ' +
313
+ "user's CURRENT request. Never reuse content recalled from memory unless the user " +
314
+ "explicitly says 'use the same content as before' or similar. (2) Always return the " +
315
+ 'draftId in your response so the user (and you, on the next turn) can reference it. (3) ' +
316
+ 'After creating, ask the user to confirm. If they confirm, call sendDraft with the ' +
317
+ 'returned draftId — never re-call this tool or send.',
318
+ parameters: [
319
+ p('to', 'Recipient email address(es).'),
320
+ p('subject', 'Email subject line.', false),
321
+ p('body', 'HTML-formatted email body.', false),
322
+ p('cc', 'Optional CC recipients.', false),
323
+ p('bcc', 'Optional BCC recipients.', false),
324
+ p('threadId', 'Optional — attach draft to an existing thread.', false),
325
+ p('inReplyTo', 'Optional Message-ID header to reply to.', false),
326
+ p('references', 'Optional References header chain.', false),
327
+ ],
328
+ },
329
+ {
330
+ operation: 'sendDraft',
331
+ label: 'Send draft',
332
+ toolName: 'send_draft',
333
+ description: 'Send an existing draft (it gets removed from Drafts and a copy appears in Sent). USAGE ' +
334
+ 'RULES: (1) Only call this with a draftId returned by a createDraft tool call earlier in ' +
335
+ 'THE CURRENT conversation. (2) Never use a draftId you remember from past conversations ' +
336
+ 'or memory recall — those drafts may have been already sent, deleted, or belonged to a ' +
337
+ 'different topic; sending the wrong one will silently deliver the wrong message to the ' +
338
+ "user's contacts. (3) If the user confirms a draft, immediately call sendDraft with the " +
339
+ "draftId from your latest createDraft response in this conversation. (4) If you're not " +
340
+ 'sure which draftId is current, call createDraft again with the latest content the user ' +
341
+ 'agreed to instead of guessing.',
342
+ parameters: [
343
+ p('draftId', 'Draft id returned by the MOST RECENT createDraft call in this conversation. Do NOT pull this id from memory or a past conversation — it will send the wrong content.'),
344
+ ],
345
+ },
346
+ {
347
+ operation: 'replyToThread',
348
+ label: 'Reply to thread',
349
+ toolName: 'reply_email',
350
+ description: 'Reply to an existing Gmail thread preserving thread integrity (In-Reply-To / References ' +
351
+ 'chain). Recipient defaults to the last sender if not provided. USAGE RULES: (1) Compose ' +
352
+ "the reply body fresh from the user's CURRENT request. Never reuse content recalled from " +
353
+ 'memory of past replies unless the user explicitly references it. (2) Use the threadId ' +
354
+ 'from a getThread / listMessages tool call EARLIER IN THIS CONVERSATION, not from memory ' +
355
+ 'of past conversations.',
356
+ parameters: [
357
+ p('threadId', 'Thread to reply to.'),
358
+ p('body', 'HTML-formatted reply body.'),
359
+ p('to', 'Optional — defaults to last sender in thread.', false),
360
+ p('subject', 'Optional — defaults to "Re: <original>".', false),
361
+ p('cc', 'Optional CC recipients.', false),
362
+ p('bcc', 'Optional BCC recipients.', false),
363
+ ],
364
+ },
365
+ {
366
+ operation: 'getMessage',
367
+ label: 'Get message',
368
+ toolName: 'get_email',
369
+ description: 'Fetch a single Gmail message by id. Returns headers, snippet, and decoded plain+HTML ' +
370
+ 'body.',
371
+ parameters: [
372
+ p('messageId', 'Gmail message id.'),
373
+ p('format', 'full | metadata | minimal.', false),
374
+ ],
375
+ },
376
+ {
377
+ operation: 'listMessages',
378
+ label: 'List messages',
379
+ toolName: 'list_emails',
380
+ description: 'Search the inbox. Supports Gmail search syntax (e.g. "from:boss is:unread"). Returns ' +
381
+ 'headers + a ~150-char snippet per message by default — enough to identify which messages ' +
382
+ 'match without flooding context with full bodies. If the user needs the full content of a ' +
383
+ 'SPECIFIC message, call getMessage with that id afterwards (cheaper than expanding all ' +
384
+ 'results to full).',
385
+ parameters: [
386
+ p('query', 'Gmail search query. Examples: "from:alice@x.com", "is:unread", "has:attachment newer_than:7d".', false),
387
+ p('labelIds', 'Filter by label id(s), comma-separated.', false),
388
+ p('maxResults', 'Default 25, max 500.', false, 'number'),
389
+ p('includeSpamTrash', 'Include spam + trash (default false).', false, 'boolean'),
390
+ p('expand', 'Fetch headers+snippet for each result (default true). Set false to return only id+threadId stubs.', false, 'boolean'),
391
+ p('expandFormat', "When expanding: 'metadata' (default — headers + snippet) or 'full' (entire body, expensive on context).", false),
392
+ ],
393
+ },
394
+ {
395
+ operation: 'deleteMessage',
396
+ label: 'Delete message',
397
+ toolName: 'delete_email',
398
+ description: 'Move a message to trash (default) or permanently delete it. Trash is safer — users can ' +
399
+ 'restore.',
400
+ parameters: [
401
+ p('messageId', 'Gmail message id.'),
402
+ p('permanent', 'true = permanent delete, false/default = trash.', false, 'boolean'),
403
+ ],
404
+ },
405
+ {
406
+ operation: 'markRead',
407
+ label: 'Mark as read',
408
+ toolName: 'mark_email_read',
409
+ description: 'Mark a message as read (removes the UNREAD label).',
410
+ parameters: [
411
+ p('messageId', 'Gmail message id.'),
412
+ ],
413
+ },
414
+ {
415
+ operation: 'markUnread',
416
+ label: 'Mark as unread',
417
+ toolName: 'mark_email_unread',
418
+ description: 'Mark a message as unread (adds the UNREAD label).',
419
+ parameters: [
420
+ p('messageId', 'Gmail message id.'),
421
+ ],
422
+ },
423
+ {
424
+ operation: 'addLabel',
425
+ label: 'Add label to message',
426
+ toolName: 'add_email_label',
427
+ description: 'Add one or more labels to a message. Use label IDs from listLabels (e.g. "Label_123") ' +
428
+ 'not label names.',
429
+ parameters: [
430
+ p('messageId', 'Gmail message id.'),
431
+ p('labelIds', 'Label id(s) to add, comma-separated.'),
432
+ ],
433
+ },
434
+ {
435
+ operation: 'removeLabel',
436
+ label: 'Remove label from message',
437
+ toolName: 'remove_email_label',
438
+ description: 'Remove one or more labels from a message.',
439
+ parameters: [
440
+ p('messageId', 'Gmail message id.'),
441
+ p('labelIds', 'Label id(s) to remove, comma-separated.'),
442
+ ],
443
+ },
444
+ {
445
+ operation: 'listLabels',
446
+ label: 'List labels',
447
+ toolName: 'list_email_labels',
448
+ description: "List all labels in the user's Gmail account (system + user labels). Use to discover " +
449
+ 'label ids before calling addLabel / removeLabel.',
450
+ parameters: [],
451
+ },
452
+ {
453
+ operation: 'createLabel',
454
+ label: 'Create label',
455
+ toolName: 'create_email_label',
456
+ description: "Create a new user label in the user's Gmail account.",
457
+ parameters: [
458
+ p('name', 'Label name (can use / for nested labels, e.g. "Clients/Active").'),
459
+ p('messageListVisibility', 'show | hide.', false),
460
+ p('labelListVisibility', 'labelShow | labelShowIfUnread | labelHide.', false),
461
+ ],
462
+ },
463
+ {
464
+ operation: 'getThread',
465
+ label: 'Get thread',
466
+ toolName: 'get_email_thread',
467
+ description: 'Fetch an entire Gmail thread with all its messages. Use for summarizing or deeply ' +
468
+ 'analyzing a conversation.',
469
+ parameters: [
470
+ p('threadId', 'Gmail thread id.'),
471
+ p('format', 'full | metadata | minimal.', false),
472
+ ],
473
+ },
474
+ {
475
+ operation: 'trashThread',
476
+ label: 'Trash thread',
477
+ toolName: 'trash_email_thread',
478
+ description: 'Move an entire thread to trash.',
479
+ parameters: [
480
+ p('threadId', 'Gmail thread id.'),
481
+ ],
482
+ },
483
+ {
484
+ operation: 'untrashThread',
485
+ label: 'Untrash thread',
486
+ toolName: 'untrash_email_thread',
487
+ description: 'Restore a thread from trash.',
488
+ parameters: [
489
+ p('threadId', 'Gmail thread id.'),
490
+ ],
491
+ },
492
+ ];
493
+ /**
494
+ * La op que espera, **fuera** del toolkit y aparte a propósito. Sigue haciendo
495
+ * falta para dos cosas que no son el toolkit: reconocer la operación de una
496
+ * herramienta ya guardada (si desapareciera, un `send_email_and_wait` creado antes
497
+ * se quedaría sin etiqueta) y el modo de una sola op elegido como herramienta
498
+ * suelta, donde el pipeline sí espera el clic.
499
+ */
500
+ exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = {
501
+ operation: 'sendAndWaitForResponse',
502
+ label: 'Send and wait for response',
503
+ toolName: 'send_email_and_wait',
504
+ description: 'Send an email with Approve / Disapprove buttons and PAUSE the pipeline until the ' +
505
+ 'recipient clicks one. Resumes downstream with `payload._waitResponse = { approved, ' +
506
+ 'decidedAt, ipAddress, comment }` so a Conditional Node can branch on the decision. Times ' +
507
+ 'out after timeoutMinutes (default 24h) if nobody clicks; the timeout cron auto-resolves ' +
508
+ 'with approved=false. Use for human-in-the-loop approvals (e.g. confirm before sending ' +
509
+ 'money, deploying, or notifying customers).',
510
+ parameters: [
511
+ p('to', 'Recipient email address(es). Single string or comma-separated list.'),
512
+ p('subject', 'Email subject line.'),
513
+ p('body', 'HTML-formatted email body. The Approve / Disapprove buttons are appended automatically below your content.'),
514
+ p('approveLabel', "Optional. Approve button label. Default: 'Approve'.", false),
515
+ p('disapproveLabel', "Optional. Disapprove button label. Default: 'Disapprove'.", false),
516
+ p('includeDisapprove', 'Optional. Include the Disapprove button. Default: true. Set to false for approve-only confirmations.', false),
517
+ p('timeoutMinutes', 'Optional. Minutes to wait before auto-resolving with approved=false. Default 1440 (24h).', false),
518
+ ],
519
+ };
520
+ /** Todo lo que el nodo sabe ejecutar, se ofrezca o no como herramienta. */
521
+ exports.GMAIL_ALL_TOOLKIT_SPECS = [
522
+ ...exports.GMAIL_TOOLKIT_SPECS,
523
+ exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC,
524
+ ];
525
+ /** El toolkit del proveedor nativo (Resend): una sola herramienta. */
526
+ exports.NATIVE_EMAIL_TOOLKIT_SPECS = [
527
+ {
528
+ operation: 'send',
529
+ label: 'Send email',
530
+ toolName: 'send_email',
531
+ description: 'Send a transactional email via Resend. Use for confirmations, notifications, receipts, ' +
532
+ 'password resets. One email per call. The sender domain is fixed by the node; you cannot ' +
533
+ 'change it per call. Body accepts HTML.',
534
+ parameters: [
535
+ p('to', 'Recipient email address(es). Single string or comma-separated list.'),
536
+ p('subject', 'Email subject line.'),
537
+ p('body', 'HTML-formatted email body.'),
538
+ p('cc', 'Optional CC recipients. Single string or comma-separated.', false),
539
+ p('bcc', 'Optional BCC recipients. Single string or comma-separated.', false),
540
+ p('reply_to', 'Optional Reply-To address.', false),
541
+ p('from_name', 'Optional display name for this specific email (overrides the node default).', false),
542
+ p('attachment_file_ids', 'Optional. Comma-separated list of file IDs from `_file.id` refs in the upstream payload (e.g., a File Transform output or a webhook multipart upload). Max 5 files, 30MB total. Do NOT invent IDs — only use values actually present in the current payload.', false),
543
+ ],
544
+ },
545
+ ];
546
+ exports.GMAIL_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.GMAIL_ALL_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
547
+ exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.NATIVE_EMAIL_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
548
+ /** Los tres campos que un día vivieron en la raíz. */
549
+ exports.GMAIL_SEND_LEGACY_FIELDS = ['to', 'subject', 'body'];
550
+ function resolveGmailSendFields(entity) {
551
+ const cfg = (entity?.operationConfig ?? {});
552
+ const crudoTo = cfg.to ?? entity?.to;
553
+ const to = Array.isArray(crudoTo)
554
+ ? crudoTo.filter((x) => typeof x === 'string')
555
+ : typeof crudoTo === 'string' && crudoTo !== ''
556
+ ? [crudoTo]
557
+ : [];
558
+ const subject = cfg.subject ?? entity?.subject;
559
+ const body = cfg.body ?? entity?.body;
560
+ return {
561
+ to,
562
+ subject: typeof subject === 'string' ? subject : '',
563
+ body: typeof body === 'string' ? body : '',
564
+ };
565
+ }
package/dist/index.d.ts CHANGED
@@ -5,8 +5,8 @@ export { NODE_UI, PREFIX_TO_TYPE, resolveNodeId, } from './ui';
5
5
  export { NODE_DISPATCH, getAllNodeCollections, getNodeDispatchConfig, } from './dispatch';
6
6
  export type { NodeRegistryEntry, NodeGroup } from './registry';
7
7
  export { NODE_REGISTRY, getNodeRegistryEntry, NODE_DETAIL_PATHS, NODE_COLORS, NODE_STATE_KEYS, PREFIX_TO_NODE_TYPE, NODE_TYPE_TO_PREFIX, } from './registry';
8
- export { GMAIL_OPERATIONS, isGmailOperation, } from './gmail-operations';
9
- export type { GmailOperation } from './gmail-operations';
8
+ export { GMAIL_OPERATIONS, GMAIL_OPERATION_SPECS, GMAIL_OPERATION_GROUPS, GMAIL_DROPDOWN_OPERATIONS, GMAIL_TOOLKIT_OPERATIONS, GMAIL_TOOLKIT_SPECS, GMAIL_ALL_TOOLKIT_SPECS, GMAIL_SEND_AND_WAIT_TOOL_SPEC, NATIVE_EMAIL_TOOLKIT_SPECS, GMAIL_TOOLKIT_BY_TOOL_NAME, NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME, GMAIL_SEND_LEGACY_FIELDS, resolveGmailSendFields, isGmailOperation, } from './gmail-operations';
9
+ export type { GmailOperation, GmailOperationGroup, GmailParamType, GmailParamSpec, GmailOperationSpec, GmailToolkitParameter, GmailToolkitSpec, GmailSendFieldSource, GmailSendFields, } from './gmail-operations';
10
10
  export { GOOGLE_CALENDAR_OPERATIONS, GOOGLE_CALENDAR_OPERATION_SPECS, isGoogleCalendarOperation, } from './calendar-operations';
11
11
  export type { GoogleCalendarOperation, GoogleCalendarParamTarget, GoogleCalendarParamType, GoogleCalendarParamSpec, GoogleCalendarOperationSpec, } from './calendar-operations';
12
12
  export { GOOGLE_CALENDAR_TOOLKIT_SPECS, GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME, } from './calendar-toolkit';
@@ -37,5 +37,7 @@ export { MONGO_OPERATIONS, MONGO_OPERATION_SPECS, isMongoOperation, } from './mo
37
37
  export type { MongoOperation, MongoParamType, MongoParamSpec, MongoOperationSpec, } from './mongo-operations';
38
38
  export { DOCS_OPERATIONS, DOCS_OPERATION_SPECS, isDocsOperation, } from './docs-operations';
39
39
  export type { DocsOperation, DocsParamType, DocsParamSlot, DocsParamSpec, DocsOperationSpec, } from './docs-operations';
40
+ export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE, } from './docs-toolkit';
41
+ export type { DocsToolkitSpec, DocsToolkitParameter, } from './docs-toolkit';
40
42
  export type { CredentialTypeRegistration, CredentialType } from './credentials';
41
43
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
- exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = void 0;
3
+ exports.TELEGRAM_TOOLKIT_SPECS = exports.isTelegramOperation = exports.TELEGRAM_OPERATION_SPECS = exports.TELEGRAM_OPERATIONS = exports.DRIVE_TOOLKIT_BY_TOOL_NAME = exports.DRIVE_TOOLKIT_SPECS = exports.isDriveOperation = exports.DRIVE_OPERATION_SPECS = exports.DRIVE_OPERATIONS = exports.GOOGLE_CALENDAR_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CALENDAR_TOOLKIT_SPECS = exports.isGoogleCalendarOperation = exports.GOOGLE_CALENDAR_OPERATION_SPECS = exports.GOOGLE_CALENDAR_OPERATIONS = exports.isGmailOperation = exports.resolveGmailSendFields = exports.GMAIL_SEND_LEGACY_FIELDS = exports.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = exports.GMAIL_TOOLKIT_BY_TOOL_NAME = exports.NATIVE_EMAIL_TOOLKIT_SPECS = exports.GMAIL_SEND_AND_WAIT_TOOL_SPEC = exports.GMAIL_ALL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_SPECS = exports.GMAIL_TOOLKIT_OPERATIONS = exports.GMAIL_DROPDOWN_OPERATIONS = exports.GMAIL_OPERATION_GROUPS = exports.GMAIL_OPERATION_SPECS = exports.GMAIL_OPERATIONS = exports.NODE_TYPE_TO_PREFIX = exports.PREFIX_TO_NODE_TYPE = exports.NODE_STATE_KEYS = exports.NODE_COLORS = exports.NODE_DETAIL_PATHS = exports.getNodeRegistryEntry = exports.NODE_REGISTRY = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.resolveNodeId = exports.PREFIX_TO_TYPE = exports.NODE_UI = exports.ALL_NODE_TYPES = exports.isNodeType = exports.isTerminal = exports.canSendToNodes = exports.canReceiveFromNodes = exports.canReceiveFrom = exports.NODE_CONNECTIONS = exports.iterableMeta = exports.singleMeta = void 0;
4
+ exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.DOCS_TOOLKIT_DEFAULTABLE = exports.DOCS_TOOLKIT_BY_TOOL_NAME = exports.DOCS_TOOLKIT_SPECS = exports.isDocsOperation = exports.DOCS_OPERATION_SPECS = exports.DOCS_OPERATIONS = exports.isMongoOperation = exports.MONGO_OPERATION_SPECS = exports.MONGO_OPERATIONS = exports.isPostgresOperation = exports.POSTGRES_OPERATION_SPECS = exports.POSTGRES_MODES = exports.POSTGRES_OPERATIONS = exports.isGoogleContactsOperation = exports.GOOGLE_CONTACTS_DEFAULT_PERSON_FIELDS = exports.GOOGLE_CONTACTS_OPERATION_GROUPS = exports.GOOGLE_CONTACTS_OPERATION_SPECS = exports.GOOGLE_CONTACTS_OPERATIONS_V2 = exports.GOOGLE_CONTACTS_OPERATIONS_V1 = exports.GOOGLE_CONTACTS_OPERATIONS = exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = exports.isSheetsOperation = exports.SHEETS_OPERATION_SPECS = exports.SHEETS_OPERATIONS = exports.isSlackOperation = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isDiscordOperation = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.isWhatsAppOperation = exports.WHATSAPP_OPERATIONS = exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = void 0;
5
5
  var types_1 = require("./types");
6
6
  Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_1.singleMeta; } });
7
7
  Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_1.iterableMeta; } });
@@ -35,6 +35,18 @@ Object.defineProperty(exports, "NODE_TYPE_TO_PREFIX", { enumerable: true, get: f
35
35
  // ── Per-service operation enums (ALL repos) ──
36
36
  var gmail_operations_1 = require("./gmail-operations");
37
37
  Object.defineProperty(exports, "GMAIL_OPERATIONS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_OPERATIONS; } });
38
+ Object.defineProperty(exports, "GMAIL_OPERATION_SPECS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_OPERATION_SPECS; } });
39
+ Object.defineProperty(exports, "GMAIL_OPERATION_GROUPS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_OPERATION_GROUPS; } });
40
+ Object.defineProperty(exports, "GMAIL_DROPDOWN_OPERATIONS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_DROPDOWN_OPERATIONS; } });
41
+ Object.defineProperty(exports, "GMAIL_TOOLKIT_OPERATIONS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_TOOLKIT_OPERATIONS; } });
42
+ Object.defineProperty(exports, "GMAIL_TOOLKIT_SPECS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_TOOLKIT_SPECS; } });
43
+ Object.defineProperty(exports, "GMAIL_ALL_TOOLKIT_SPECS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_ALL_TOOLKIT_SPECS; } });
44
+ Object.defineProperty(exports, "GMAIL_SEND_AND_WAIT_TOOL_SPEC", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_SEND_AND_WAIT_TOOL_SPEC; } });
45
+ Object.defineProperty(exports, "NATIVE_EMAIL_TOOLKIT_SPECS", { enumerable: true, get: function () { return gmail_operations_1.NATIVE_EMAIL_TOOLKIT_SPECS; } });
46
+ Object.defineProperty(exports, "GMAIL_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_TOOLKIT_BY_TOOL_NAME; } });
47
+ Object.defineProperty(exports, "NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return gmail_operations_1.NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME; } });
48
+ Object.defineProperty(exports, "GMAIL_SEND_LEGACY_FIELDS", { enumerable: true, get: function () { return gmail_operations_1.GMAIL_SEND_LEGACY_FIELDS; } });
49
+ Object.defineProperty(exports, "resolveGmailSendFields", { enumerable: true, get: function () { return gmail_operations_1.resolveGmailSendFields; } });
38
50
  Object.defineProperty(exports, "isGmailOperation", { enumerable: true, get: function () { return gmail_operations_1.isGmailOperation; } });
39
51
  var calendar_operations_1 = require("./calendar-operations");
40
52
  Object.defineProperty(exports, "GOOGLE_CALENDAR_OPERATIONS", { enumerable: true, get: function () { return calendar_operations_1.GOOGLE_CALENDAR_OPERATIONS; } });
@@ -97,6 +109,10 @@ var docs_operations_1 = require("./docs-operations");
97
109
  Object.defineProperty(exports, "DOCS_OPERATIONS", { enumerable: true, get: function () { return docs_operations_1.DOCS_OPERATIONS; } });
98
110
  Object.defineProperty(exports, "DOCS_OPERATION_SPECS", { enumerable: true, get: function () { return docs_operations_1.DOCS_OPERATION_SPECS; } });
99
111
  Object.defineProperty(exports, "isDocsOperation", { enumerable: true, get: function () { return docs_operations_1.isDocsOperation; } });
112
+ var docs_toolkit_1 = require("./docs-toolkit");
113
+ Object.defineProperty(exports, "DOCS_TOOLKIT_SPECS", { enumerable: true, get: function () { return docs_toolkit_1.DOCS_TOOLKIT_SPECS; } });
114
+ Object.defineProperty(exports, "DOCS_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return docs_toolkit_1.DOCS_TOOLKIT_BY_TOOL_NAME; } });
115
+ Object.defineProperty(exports, "DOCS_TOOLKIT_DEFAULTABLE", { enumerable: true, get: function () { return docs_toolkit_1.DOCS_TOOLKIT_DEFAULTABLE; } });
100
116
  var credentials_1 = require("./credentials");
101
117
  Object.defineProperty(exports, "CREDENTIAL_TYPES", { enumerable: true, get: function () { return credentials_1.CREDENTIAL_TYPES; } });
102
118
  Object.defineProperty(exports, "CREDENTIAL_TYPE_VALUES", { enumerable: true, get: function () { return credentials_1.CREDENTIAL_TYPE_VALUES; } });
@@ -2,11 +2,18 @@
2
2
  * Google Sheets AI Toolkit — las herramientas que un sheetsAction expone
3
3
  * cuando `aiEnabled` está encendido.
4
4
  *
5
- * **Se escribe una sola vez.** Las especificaciones de toolkit de los demás
6
- * nodos viven dos veces —una copia en el dashboard y otra en
5
+ * **Se escribe una sola vez.**
6
+ *
7
+ * Cuando esto se escribió, las especificaciones de los demás nodos vivían dos
8
+ * veces —una copia en el dashboard y otra en
7
9
  * `api/src/mcp-servers/toolkit-specs.ts`, las dos a mano y sin nada que las
8
10
  * ate— y de ahí salió que Contacts anunciara 15 herramientas y expusiera una.
9
- * Sheets empieza por el otro lado: aquí, y las dos capas la importan.
11
+ *
12
+ * Ya no es así, y este comentario decía lo contrario hasta 2026-08-12: hoy en
13
+ * el paquete viven **siete** toolkits —Gmail, native email, Calendar, Drive,
14
+ * Telegram, Sheets y Docs— y los que siguen duplicados a mano son WhatsApp,
15
+ * Discord, Slack y Contacts. El paquete es el sitio por defecto para uno
16
+ * nuevo; los cuatro rezagados están pendientes de migrar.
10
17
  *
11
18
  * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para que
12
19
  * pueda usarse sin adaptador. Los textos van en inglés porque los lee el modelo
@@ -3,11 +3,18 @@
3
3
  * Google Sheets AI Toolkit — las herramientas que un sheetsAction expone
4
4
  * cuando `aiEnabled` está encendido.
5
5
  *
6
- * **Se escribe una sola vez.** Las especificaciones de toolkit de los demás
7
- * nodos viven dos veces —una copia en el dashboard y otra en
6
+ * **Se escribe una sola vez.**
7
+ *
8
+ * Cuando esto se escribió, las especificaciones de los demás nodos vivían dos
9
+ * veces —una copia en el dashboard y otra en
8
10
  * `api/src/mcp-servers/toolkit-specs.ts`, las dos a mano y sin nada que las
9
11
  * ate— y de ahí salió que Contacts anunciara 15 herramientas y expusiera una.
10
- * Sheets empieza por el otro lado: aquí, y las dos capas la importan.
12
+ *
13
+ * Ya no es así, y este comentario decía lo contrario hasta 2026-08-12: hoy en
14
+ * el paquete viven **siete** toolkits —Gmail, native email, Calendar, Drive,
15
+ * Telegram, Sheets y Docs— y los que siguen duplicados a mano son WhatsApp,
16
+ * Discord, Slack y Contacts. El paquete es el sitio por defecto para uno
17
+ * nuevo; los cuatro rezagados están pendientes de migrar.
11
18
  *
12
19
  * La forma es la que ya consume `toolkitSpecToMcpTool` en la api, para que
13
20
  * pueda usarse sin adaptador. Los textos van en inglés porque los lee el modelo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.52.16",
3
+ "version": "1.52.18",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",