@hostwebhook/node-types 1.86.0 → 1.88.0

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.
Files changed (59) hide show
  1. package/dist/calendar-toolkit.d.ts +3 -0
  2. package/dist/calendar-toolkit.js +3 -2
  3. package/dist/clase-de-herramienta.d.ts +61 -0
  4. package/dist/clase-de-herramienta.js +153 -0
  5. package/dist/discord-toolkit.d.ts +2 -3
  6. package/dist/discord-toolkit.js +3 -6
  7. package/dist/docs-toolkit.d.ts +2 -1
  8. package/dist/docs-toolkit.js +3 -6
  9. package/dist/drive-toolkit.d.ts +9 -4
  10. package/dist/drive-toolkit.js +3 -4
  11. package/dist/esm/calendar-toolkit.d.ts +3 -0
  12. package/dist/esm/calendar-toolkit.js +3 -2
  13. package/dist/esm/clase-de-herramienta.d.ts +61 -0
  14. package/dist/esm/clase-de-herramienta.js +148 -0
  15. package/dist/esm/discord-toolkit.d.ts +2 -3
  16. package/dist/esm/discord-toolkit.js +3 -6
  17. package/dist/esm/docs-toolkit.d.ts +2 -1
  18. package/dist/esm/docs-toolkit.js +3 -6
  19. package/dist/esm/drive-toolkit.d.ts +9 -4
  20. package/dist/esm/drive-toolkit.js +3 -4
  21. package/dist/esm/gmail-operations.d.ts +2 -0
  22. package/dist/esm/gmail-operations.js +7 -6
  23. package/dist/esm/google-contacts-toolkit.d.ts +46 -0
  24. package/dist/esm/google-contacts-toolkit.js +180 -0
  25. package/dist/esm/index.d.ts +10 -0
  26. package/dist/esm/index.js +9 -0
  27. package/dist/esm/mongo-toolkit.d.ts +47 -0
  28. package/dist/esm/mongo-toolkit.js +98 -0
  29. package/dist/esm/postgres-toolkit.d.ts +52 -0
  30. package/dist/esm/postgres-toolkit.js +85 -0
  31. package/dist/esm/registry.js +10 -0
  32. package/dist/esm/sheets-toolkit.d.ts +2 -1
  33. package/dist/esm/sheets-toolkit.js +3 -6
  34. package/dist/esm/slack-toolkit.d.ts +2 -3
  35. package/dist/esm/slack-toolkit.js +3 -4
  36. package/dist/esm/telegram-toolkit.d.ts +3 -0
  37. package/dist/esm/telegram-toolkit.js +3 -2
  38. package/dist/esm/toolkits.d.ts +111 -0
  39. package/dist/esm/toolkits.js +178 -0
  40. package/dist/gmail-operations.d.ts +2 -0
  41. package/dist/gmail-operations.js +11 -10
  42. package/dist/google-contacts-toolkit.d.ts +46 -0
  43. package/dist/google-contacts-toolkit.js +183 -0
  44. package/dist/index.d.ts +10 -0
  45. package/dist/index.js +32 -1
  46. package/dist/mongo-toolkit.d.ts +47 -0
  47. package/dist/mongo-toolkit.js +101 -0
  48. package/dist/postgres-toolkit.d.ts +52 -0
  49. package/dist/postgres-toolkit.js +88 -0
  50. package/dist/registry.js +10 -0
  51. package/dist/sheets-toolkit.d.ts +2 -1
  52. package/dist/sheets-toolkit.js +3 -6
  53. package/dist/slack-toolkit.d.ts +2 -3
  54. package/dist/slack-toolkit.js +3 -4
  55. package/dist/telegram-toolkit.d.ts +3 -0
  56. package/dist/telegram-toolkit.js +3 -2
  57. package/dist/toolkits.d.ts +111 -0
  58. package/dist/toolkits.js +190 -0
  59. package/package.json +1 -1
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * PostgreSQL AI Toolkit — las herramientas que un postgresAction expone cuando
4
+ * `aiEnabled` está encendido.
5
+ *
6
+ * ## Por qué las escrituras NO reciben SQL (2026-09-24, decisión de Ariel)
7
+ *
8
+ * El nodo, en modo de una operación, ejecuta el SQL que se le escribe: sus
9
+ * cinco operaciones son una etiqueta encima del mismo SQL crudo. Si el toolkit
10
+ * hiciera lo mismo, bloquear una herramienta no bloquearía nada — un
11
+ * `insert_rows` puede llevar un `DELETE` dentro de un `WITH`, y quien bloqueó
12
+ * `delete_rows` seguiría viendo desaparecer filas.
13
+ *
14
+ * Así que las tres escrituras reciben la tabla del nodo y JSON, y el SQL lo
15
+ * genera el handler, parametrizado y con los identificadores entrecomillados:
16
+ * el modelo no escribe SQL que se ejecute como escritura.
17
+ *
18
+ * ## Lo que queda con SQL, y cómo se contiene
19
+ *
20
+ * - `query_rows` recibe SQL, y corre dentro de una transacción **READ ONLY**:
21
+ * un `DELETE`, un `UPDATE` o un `CREATE` los rechaza el propio Postgres, no
22
+ * una expresión regular nuestra. Puede leer otras tablas —un JOIN hace
23
+ * falta a menudo—; el límite ahí lo pone el usuario de base de datos de la
24
+ * credencial.
25
+ * - `execute_sql` recibe SQL cualquiera, DDL incluido. Es la excepción
26
+ * consciente a «sólo la tabla del nodo»: SQL crudo no se puede vallar a una
27
+ * tabla. La contienen tres cosas: es siempre destructiva (el gate del AI
28
+ * Node la para si está encendido), se puede bloquear en el nodo, y el
29
+ * usuario de base de datos.
30
+ *
31
+ * Los textos van en inglés porque los lee el modelo.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.POSTGRES_TOOLKIT_BY_TOOL_NAME = exports.POSTGRES_TOOLKIT_SPECS = void 0;
35
+ const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
36
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
37
+ const donde = (que) => p('where', `JSON object selecting the rows ${que}: each key is a column and each value the value it must equal, all of them at once, e.g. {"id": 42} or {"status": "pending", "customer_id": 7}. It cannot be empty — an empty condition would touch every row.`);
38
+ const parametros = () => p('params', 'Optional JSON array with the values for the $1, $2… placeholders in the SQL, e.g. ["paid", 100]. Always pass values this way instead of writing them into the SQL.', false);
39
+ exports.POSTGRES_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
40
+ {
41
+ operation: 'query',
42
+ label: 'Query rows',
43
+ toolName: 'query_rows',
44
+ description: 'Run a read-only SQL query (SELECT, WITH … SELECT) and return its rows, at most 200. It runs inside a READ ONLY transaction: a statement that writes is rejected by the database. Use it for lookups, counts, sums and joins.',
45
+ parameters: [
46
+ p('sql', 'The SELECT statement, with $1, $2… placeholders for values, e.g. SELECT * FROM orders WHERE status = $1 LIMIT 20.'),
47
+ parametros(),
48
+ ],
49
+ },
50
+ {
51
+ operation: 'insertOne',
52
+ label: 'Insert rows',
53
+ toolName: 'insert_rows',
54
+ description: "Insert one or more rows into this node's table and return them as stored, with the values the database filled in (ids, defaults). It does not check for duplicates.",
55
+ parameters: [
56
+ p('rows', 'JSON object with one row, or a JSON array of rows. Keys are column names, e.g. {"name": "Ariel", "email": "a@b.c"}. A key that is not a column makes the call fail.'),
57
+ ],
58
+ },
59
+ {
60
+ operation: 'update',
61
+ label: 'Update rows',
62
+ toolName: 'update_rows',
63
+ description: "Update the rows of this node's table that match a condition, overwriting the columns you set, and return them after the update. When no row matches it changes nothing.",
64
+ parameters: [
65
+ p('set', 'JSON object with the columns to overwrite and their new values, e.g. {"status": "shipped"}.'),
66
+ donde('to update'),
67
+ ],
68
+ },
69
+ {
70
+ operation: 'delete',
71
+ label: 'Delete rows',
72
+ toolName: 'delete_rows',
73
+ description: "Delete the rows of this node's table that match a condition and return them as they were. This cannot be undone: when you are not sure which rows match, check with query_rows first.",
74
+ parameters: [donde('to delete')],
75
+ },
76
+ {
77
+ operation: 'execute',
78
+ label: 'Execute SQL',
79
+ toolName: 'execute_sql',
80
+ description: 'Run any SQL statement — including ones that change the schema (CREATE, ALTER, DROP) — and return what it produces. It is not limited to this node\'s table. Prefer query_rows, insert_rows, update_rows or delete_rows whenever one of them can do the job.',
81
+ parameters: [
82
+ p('sql', 'The SQL statement, with $1, $2… placeholders for values.'),
83
+ parametros(),
84
+ ],
85
+ },
86
+ ]);
87
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
88
+ exports.POSTGRES_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.POSTGRES_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
package/dist/registry.js CHANGED
@@ -412,6 +412,11 @@ exports.NODE_REGISTRY = {
412
412
  allStateKey: "allMongoActions",
413
413
  color: "#34d399",
414
414
  testable: true,
415
+ /* Modo AI toolkit (2026-09-24): con `aiEnabled` el nodo sólo lo llama un
416
+ AI Node —como el resto de toolkits—, y una arista del lienzo no le
417
+ llevaría nada. Hasta que la api y el dashboard le den `aiEnabled`, el
418
+ campo no existe y esto devuelve `false`: no cambia nada. */
419
+ isToolOnly: (e) => e.aiEnabled === true,
415
420
  },
416
421
  postgresAction: {
417
422
  type: "postgresAction",
@@ -424,6 +429,11 @@ exports.NODE_REGISTRY = {
424
429
  allStateKey: "allPostgresActions",
425
430
  color: "#336791",
426
431
  testable: true,
432
+ /* Modo AI toolkit (2026-09-24): con `aiEnabled` el nodo sólo lo llama un
433
+ AI Node —como el resto de toolkits—, y una arista del lienzo no le
434
+ llevaría nada. Hasta que la api y el dashboard le den `aiEnabled`, el
435
+ campo no existe y esto devuelve `false`: no cambia nada. */
436
+ isToolOnly: (e) => e.aiEnabled === true,
427
437
  },
428
438
  notificationAction: {
429
439
  type: "notificationAction",
@@ -34,7 +34,8 @@ export interface SheetsToolkitSpec {
34
34
  toolName: string;
35
35
  description: string;
36
36
  parameters: SheetsToolkitParameter[];
37
- /** Escribe en la hoja del usuario. Lo usa la confirmación previa del AI Node. */
37
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
38
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
38
39
  destructive?: boolean;
39
40
  }
40
41
  export declare const SHEETS_TOOLKIT_SPECS: SheetsToolkitSpec[];
@@ -22,6 +22,7 @@
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.SHEETS_TOOLKIT_DEFAULTABLE = exports.SHEETS_TOOLKIT_BY_TOOL_NAME = exports.SHEETS_TOOLKIT_SPECS = void 0;
25
+ const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
25
26
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
26
27
  /*
27
28
  * La hoja y la pestaña del nodo van como **defecto**, y el modelo puede
@@ -44,14 +45,13 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
44
45
  const hoja = () => p('spreadsheetId', "Id of the Google Sheets document — the long chunk of its URL, between /d/ and /edit.");
45
46
  const pestana = () => p('sheetName', 'Name of the tab inside the document, exactly as it reads on its tab strip.');
46
47
  const fila = (que) => p('row', `JSON object holding ${que}. Keys are the sheet's **header names**, spelled exactly as they appear in its title row: {"Name": "Ariel", "Email": "a@b.c"}. A key that is not a header makes the call fail, so when you do not know the headers, call get_rows first.`);
47
- exports.SHEETS_TOOLKIT_SPECS = [
48
+ exports.SHEETS_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
48
49
  {
49
50
  operation: 'appendRow',
50
51
  label: 'Append row',
51
52
  toolName: 'append_row',
52
53
  description: 'Add a new row at the bottom of the tab. It does not check whether the row already exists — when it might, use append_or_update_row instead so you do not duplicate it.',
53
54
  parameters: [fila('the values of the new row'), hoja(), pestana()],
54
- destructive: true,
55
55
  },
56
56
  {
57
57
  operation: 'updateRow',
@@ -65,7 +65,6 @@ exports.SHEETS_TOOLKIT_SPECS = [
65
65
  hoja(),
66
66
  pestana(),
67
67
  ],
68
- destructive: true,
69
68
  },
70
69
  {
71
70
  operation: 'appendOrUpdateRow',
@@ -79,7 +78,6 @@ exports.SHEETS_TOOLKIT_SPECS = [
79
78
  hoja(),
80
79
  pestana(),
81
80
  ],
82
- destructive: true,
83
81
  },
84
82
  {
85
83
  operation: 'readRange',
@@ -116,9 +114,8 @@ exports.SHEETS_TOOLKIT_SPECS = [
116
114
  * en el nodo, y la api la aplica.
117
115
  */
118
116
  parameters: [p('title', 'Name of the new document.')],
119
- destructive: true,
120
117
  },
121
- ];
118
+ ]);
122
119
  /** Las herramientas por nombre, para despachar una llamada del modelo. */
123
120
  exports.SHEETS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.SHEETS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
124
121
  /**
@@ -38,9 +38,8 @@ export interface SlackToolkitSpec {
38
38
  /** Descripción (más reglas de uso) que ve el LLM. */
39
39
  description: string;
40
40
  parameters: SlackToolkitParameter[];
41
- /** Operaciones irreversibles o que cambian privilegios. La capa MCP las
42
- * bloquea cuando el nodo de IA lleva `requireConfirmationForDestructive`,
43
- * salvo que la llamada traiga confirmación explícita. */
41
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
42
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
44
43
  destructive?: boolean;
45
44
  }
46
45
  export declare const SLACK_TOOLKIT_SPECS: SlackToolkitSpec[];
@@ -27,8 +27,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.SLACK_TOOLKIT_BY_TOOL_NAME = exports.SLACK_TOOLKIT_SPECS = void 0;
28
28
  exports.herramientasDeSlackPara = herramientasDeSlackPara;
29
29
  const slack_operations_js_1 = require("./slack-operations.js");
30
+ const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
30
31
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
31
- exports.SLACK_TOOLKIT_SPECS = [
32
+ exports.SLACK_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
32
33
  // ── Messages ─────────────────────────────────────────────────────
33
34
  {
34
35
  operation: 'sendMessage',
@@ -67,7 +68,6 @@ exports.SLACK_TOOLKIT_SPECS = [
67
68
  p('channel', 'Channel where the message lives.'),
68
69
  p('ts', 'Timestamp of the message to delete.'),
69
70
  ],
70
- destructive: true,
71
71
  },
72
72
  {
73
73
  operation: 'sendEphemeral',
@@ -146,7 +146,6 @@ exports.SLACK_TOOLKIT_SPECS = [
146
146
  p('name', 'Channel name — lowercase, no spaces, max 80 chars.'),
147
147
  p('isPrivate', 'Create a private channel instead of public. Default false.', false, 'boolean'),
148
148
  ],
149
- destructive: true,
150
149
  },
151
150
  {
152
151
  operation: 'inviteToChannel',
@@ -216,7 +215,7 @@ exports.SLACK_TOOLKIT_SPECS = [
216
215
  p('threadTs', 'Thread to attach the file to.', false),
217
216
  ],
218
217
  },
219
- ];
218
+ ]);
220
219
  exports.SLACK_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.SLACK_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
221
220
  /**
222
221
  * Las herramientas que ofrecerle al modelo con ESTA credencial.
@@ -39,6 +39,9 @@ export interface TelegramToolkitSpec {
39
39
  /** Descripción (más reglas de uso) que ve el LLM. */
40
40
  description: string;
41
41
  parameters: TelegramToolkitParameter[];
42
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
43
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
44
+ destructive?: boolean;
42
45
  /**
43
46
  * Encabezado de grupo para la lista de la página de detalle. Las filas
44
47
  * consecutivas que comparten grupo se pintan bajo un único título, así que el
@@ -22,6 +22,7 @@
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = exports.TELEGRAM_TOOLKIT_SPECS = void 0;
25
+ const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
25
26
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
26
27
  /*
27
28
  * `sendAndWaitForResponse` NO está aquí, y es una decisión, no un olvido: la op
@@ -36,7 +37,7 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
36
37
  * campo en ninguna de las dos capas, y añadirlo cambiaría cuándo el AI Node pide
37
38
  * confirmación. Eso es otra decisión, no parte de mudar el esquema de sitio.
38
39
  */
39
- exports.TELEGRAM_TOOLKIT_SPECS = [
40
+ exports.TELEGRAM_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
40
41
  {
41
42
  operation: 'sendMessage',
42
43
  label: 'Send message',
@@ -181,6 +182,6 @@ exports.TELEGRAM_TOOLKIT_SPECS = [
181
182
  p('showAlert', 'Optional. true = modal alert; false (default) = toast notification.', false, 'boolean'),
182
183
  ],
183
184
  },
184
- ];
185
+ ]);
185
186
  /** Las herramientas por nombre, para despachar una llamada del modelo. */
186
187
  exports.TELEGRAM_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.TELEGRAM_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Los toolkits por tipo de nodo: qué herramientas expone cada nodo en modo
3
+ * AI toolkit, en un solo sitio.
4
+ *
5
+ * ## Para qué (2026-09-24)
6
+ *
7
+ * Para que el gate «Block destructive tool calls» del AI Node pueda preguntar
8
+ * por la OPERACIÓN y no sólo por el nombre. Hasta hoy sólo miraba el nombre
9
+ * contra unos patrones (`delete_*`, `drop_*`…), así que `update_row` de Sheets
10
+ * —que pisa una fila— pasaba con el gate encendido. Con esto, la herramienta
11
+ * que el AI Node guardó al conectar (`nodeRefType` + `operationOverride`) se
12
+ * resuelve aquí a su operación, y su `destructive` —que sale del verbo, ver
13
+ * `clase-de-herramienta.ts`— decide.
14
+ *
15
+ * ## Lo que falta aquí, a propósito
16
+ *
17
+ * WhatsApp y Social siguen con sus specs fuera de este paquete (WhatsApp
18
+ * escrito a mano en la api y el dashboard; Social armado por entidad desde el
19
+ * registro de proveedores). Quien consulte este registro tiene que componerlos
20
+ * por su lado — {@link esOperacionDestructiva} devuelve `null` para ellos, que
21
+ * significa «no lo sé», no «no es destructiva». Contacts se mudó el
22
+ * 2026-09-24.
23
+ *
24
+ * ## El bloqueo por nodo y la puerta sin herramienta (2026-09-24)
25
+ *
26
+ * Abajo viven también las dos reglas que se aplican a TODA llamada a un nodo en
27
+ * modo toolkit, en un solo sitio para que la api, hw-nodes, node-sdk y el
28
+ * dashboard pregunten lo mismo:
29
+ *
30
+ * - «Block these tool calls»: el dueño bloquea herramientas en el nodo
31
+ * (`blockedTools`, por nombre). Vale para todo tipo de este registro
32
+ * ({@link admiteBloqueo}).
33
+ * - Sin herramienta no se corre: en modo toolkit un nodo sólo ejecuta la
34
+ * herramienta que le nombran en `operation`. Un evento sin ella —una arista
35
+ * vieja del lienzo— ya no corre la operación de «Single operation», que en
36
+ * ese modo no se ve. Decisión de Ariel para los catorce toolkits
37
+ * ({@link TIPOS_CON_TOOLKIT}), no sólo los de este registro.
38
+ *
39
+ * {@link motivoParaNoCorrer} junta las dos y la llama node-sdk al registrar
40
+ * cada handler, así que cubre todo camino que acabe en `handler.execute`.
41
+ */
42
+ /** Lo mínimo de una herramienta de toolkit que hace falta para decidir. */
43
+ export interface OperacionDeToolkit {
44
+ operation: string;
45
+ toolName: string;
46
+ label: string;
47
+ destructive?: boolean;
48
+ }
49
+ export declare const TOOLKITS_POR_TIPO: Readonly<Record<string, readonly OperacionDeToolkit[]>>;
50
+ /**
51
+ * La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
52
+ * por el nombre de su herramienta (`update_row`).
53
+ *
54
+ * Las dos formas porque las dos llegan: el AI Node guarda `operationOverride`
55
+ * con el nombre interno, y el servidor MCP recibe el nombre de la herramienta.
56
+ */
57
+ export declare function operacionDeToolkit(nodeType: string | undefined | null, operacionONombre: string | undefined | null): OperacionDeToolkit | null;
58
+ /**
59
+ * ¿Es destructiva esta operación de toolkit?
60
+ *
61
+ * `null` cuando no se sabe —el tipo no es un toolkit de este paquete, o la
62
+ * operación no existe en él—. Quien llama decide qué hace con el «no sé»; el
63
+ * gate del AI Node lo combina con los patrones de nombre, que siguen ahí para
64
+ * las herramientas que no son de toolkit (MCP, HTTP…).
65
+ */
66
+ export declare function esOperacionDestructiva(nodeType: string | undefined | null, operacionONombre: string | undefined | null): boolean | null;
67
+ /**
68
+ * Los tipos con modo AI toolkit de VARIAS herramientas: los de este registro
69
+ * más WhatsApp y Social, cuyas herramientas aún no viven aquí. Es la lista de
70
+ * quién se niega a correr sin herramienta pedida.
71
+ *
72
+ * RSS, Mailchimp o Shopify también tienen `aiEnabled`, pero exponen el nodo
73
+ * como UNA herramienta sin `operation`: no están aquí a propósito.
74
+ */
75
+ export declare const TIPOS_CON_TOOLKIT: readonly string[];
76
+ export declare function tieneModoToolkit(nodeType: string | undefined | null): boolean;
77
+ /**
78
+ * ¿Admite «Block these tool calls»? Los de este registro: para bloquear hay
79
+ * que saber traducir la operación que guarda el AI Node al nombre de la
80
+ * herramienta que guarda el nodo, y eso sólo se sabe aquí.
81
+ */
82
+ export declare function admiteBloqueo(nodeType: string | undefined | null): boolean;
83
+ /** Los nombres de herramienta de un toolkit: lo único que `blockedTools` acepta. */
84
+ export declare function nombresDeHerramientas(nodeType: string | undefined | null): string[];
85
+ /** Los nombres bloqueados de un nodo, tolerando documentos sin el campo. */
86
+ export declare function bloqueadasDe(entidad: unknown): string[];
87
+ /**
88
+ * ¿Está bloqueada en este nodo? Acepta la operación (`deleteOne`, lo que guarda
89
+ * el AI Node en `operationOverride`) o el nombre (`delete_document`, lo que
90
+ * recibe el servidor MCP).
91
+ */
92
+ export declare function herramientaBloqueadaEn(entidad: unknown, nodeType: string | undefined | null, operacionONombre: string | undefined | null): boolean;
93
+ /** Lo que se contesta a una llamada sin herramienta. Lo lee una persona en el
94
+ * historial —una arista vieja— o el modelo, así que dice qué hacer. */
95
+ export declare const MENSAJE_SIN_HERRAMIENTA = "This node is an AI toolkit: it only runs the tool an AI Node or an MCP client names, and this call named none. If a connection in the flow leads here, remove it \u2014 in toolkit mode the node is not part of the pipeline.";
96
+ export declare function mensajeDeHerramientaBloqueada(toolName: string): string;
97
+ /**
98
+ * Por qué una llamada NO debe correr en este nodo, o `null` si puede.
99
+ *
100
+ * Sólo mira nodos en modo toolkit (`aiEnabled` y un tipo de
101
+ * {@link TIPOS_CON_TOOLKIT}); cualquier otro nodo corre como siempre. En modo
102
+ * toolkit:
103
+ * - sin `operation` → {@link MENSAJE_SIN_HERRAMIENTA};
104
+ * - con una herramienta bloqueada en el nodo → su mensaje.
105
+ *
106
+ * Una operación que el registro no conoce NO se rechaza aquí: cada servicio
107
+ * sabe qué operaciones acepta (Mongo y Postgres las validan con más detalle),
108
+ * y rechazar lo desconocido aquí rompería operaciones legítimas que no son
109
+ * herramientas.
110
+ */
111
+ export declare function motivoParaNoCorrer(nodeType: string | undefined | null, entidad: unknown, payload: unknown): string | null;
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ /**
3
+ * Los toolkits por tipo de nodo: qué herramientas expone cada nodo en modo
4
+ * AI toolkit, en un solo sitio.
5
+ *
6
+ * ## Para qué (2026-09-24)
7
+ *
8
+ * Para que el gate «Block destructive tool calls» del AI Node pueda preguntar
9
+ * por la OPERACIÓN y no sólo por el nombre. Hasta hoy sólo miraba el nombre
10
+ * contra unos patrones (`delete_*`, `drop_*`…), así que `update_row` de Sheets
11
+ * —que pisa una fila— pasaba con el gate encendido. Con esto, la herramienta
12
+ * que el AI Node guardó al conectar (`nodeRefType` + `operationOverride`) se
13
+ * resuelve aquí a su operación, y su `destructive` —que sale del verbo, ver
14
+ * `clase-de-herramienta.ts`— decide.
15
+ *
16
+ * ## Lo que falta aquí, a propósito
17
+ *
18
+ * WhatsApp y Social siguen con sus specs fuera de este paquete (WhatsApp
19
+ * escrito a mano en la api y el dashboard; Social armado por entidad desde el
20
+ * registro de proveedores). Quien consulte este registro tiene que componerlos
21
+ * por su lado — {@link esOperacionDestructiva} devuelve `null` para ellos, que
22
+ * significa «no lo sé», no «no es destructiva». Contacts se mudó el
23
+ * 2026-09-24.
24
+ *
25
+ * ## El bloqueo por nodo y la puerta sin herramienta (2026-09-24)
26
+ *
27
+ * Abajo viven también las dos reglas que se aplican a TODA llamada a un nodo en
28
+ * modo toolkit, en un solo sitio para que la api, hw-nodes, node-sdk y el
29
+ * dashboard pregunten lo mismo:
30
+ *
31
+ * - «Block these tool calls»: el dueño bloquea herramientas en el nodo
32
+ * (`blockedTools`, por nombre). Vale para todo tipo de este registro
33
+ * ({@link admiteBloqueo}).
34
+ * - Sin herramienta no se corre: en modo toolkit un nodo sólo ejecuta la
35
+ * herramienta que le nombran en `operation`. Un evento sin ella —una arista
36
+ * vieja del lienzo— ya no corre la operación de «Single operation», que en
37
+ * ese modo no se ve. Decisión de Ariel para los catorce toolkits
38
+ * ({@link TIPOS_CON_TOOLKIT}), no sólo los de este registro.
39
+ *
40
+ * {@link motivoParaNoCorrer} junta las dos y la llama node-sdk al registrar
41
+ * cada handler, así que cubre todo camino que acabe en `handler.execute`.
42
+ */
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.MENSAJE_SIN_HERRAMIENTA = exports.TIPOS_CON_TOOLKIT = exports.TOOLKITS_POR_TIPO = void 0;
45
+ exports.operacionDeToolkit = operacionDeToolkit;
46
+ exports.esOperacionDestructiva = esOperacionDestructiva;
47
+ exports.tieneModoToolkit = tieneModoToolkit;
48
+ exports.admiteBloqueo = admiteBloqueo;
49
+ exports.nombresDeHerramientas = nombresDeHerramientas;
50
+ exports.bloqueadasDe = bloqueadasDe;
51
+ exports.herramientaBloqueadaEn = herramientaBloqueadaEn;
52
+ exports.mensajeDeHerramientaBloqueada = mensajeDeHerramientaBloqueada;
53
+ exports.motivoParaNoCorrer = motivoParaNoCorrer;
54
+ const gmail_operations_js_1 = require("./gmail-operations.js");
55
+ const calendar_toolkit_js_1 = require("./calendar-toolkit.js");
56
+ const drive_toolkit_js_1 = require("./drive-toolkit.js");
57
+ const telegram_toolkit_js_1 = require("./telegram-toolkit.js");
58
+ const discord_toolkit_js_1 = require("./discord-toolkit.js");
59
+ const slack_toolkit_js_1 = require("./slack-toolkit.js");
60
+ const sheets_toolkit_js_1 = require("./sheets-toolkit.js");
61
+ const docs_toolkit_js_1 = require("./docs-toolkit.js");
62
+ const mongo_toolkit_js_1 = require("./mongo-toolkit.js");
63
+ const postgres_toolkit_js_1 = require("./postgres-toolkit.js");
64
+ const google_contacts_toolkit_js_1 = require("./google-contacts-toolkit.js");
65
+ exports.TOOLKITS_POR_TIPO = Object.freeze({
66
+ gmailAction: gmail_operations_js_1.GMAIL_ALL_TOOLKIT_SPECS,
67
+ emailAction: gmail_operations_js_1.NATIVE_EMAIL_TOOLKIT_SPECS,
68
+ calendarAction: calendar_toolkit_js_1.GOOGLE_CALENDAR_TOOLKIT_SPECS,
69
+ driveAction: drive_toolkit_js_1.DRIVE_TOOLKIT_SPECS,
70
+ telegramAction: telegram_toolkit_js_1.TELEGRAM_TOOLKIT_SPECS,
71
+ discordAction: discord_toolkit_js_1.DISCORD_TOOLKIT_SPECS,
72
+ slackAction: slack_toolkit_js_1.SLACK_TOOLKIT_SPECS,
73
+ sheetsAction: sheets_toolkit_js_1.SHEETS_TOOLKIT_SPECS,
74
+ docsAction: docs_toolkit_js_1.DOCS_TOOLKIT_SPECS,
75
+ mongoAction: mongo_toolkit_js_1.MONGO_TOOLKIT_SPECS,
76
+ postgresAction: postgres_toolkit_js_1.POSTGRES_TOOLKIT_SPECS,
77
+ googleContactsAction: google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_SPECS,
78
+ });
79
+ /**
80
+ * La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
81
+ * por el nombre de su herramienta (`update_row`).
82
+ *
83
+ * Las dos formas porque las dos llegan: el AI Node guarda `operationOverride`
84
+ * con el nombre interno, y el servidor MCP recibe el nombre de la herramienta.
85
+ */
86
+ function operacionDeToolkit(nodeType, operacionONombre) {
87
+ if (!nodeType || !operacionONombre)
88
+ return null;
89
+ const toolkit = exports.TOOLKITS_POR_TIPO[nodeType];
90
+ if (!toolkit)
91
+ return null;
92
+ return (toolkit.find((s) => s.operation === operacionONombre) ??
93
+ toolkit.find((s) => s.toolName === operacionONombre) ??
94
+ null);
95
+ }
96
+ /**
97
+ * ¿Es destructiva esta operación de toolkit?
98
+ *
99
+ * `null` cuando no se sabe —el tipo no es un toolkit de este paquete, o la
100
+ * operación no existe en él—. Quien llama decide qué hace con el «no sé»; el
101
+ * gate del AI Node lo combina con los patrones de nombre, que siguen ahí para
102
+ * las herramientas que no son de toolkit (MCP, HTTP…).
103
+ */
104
+ function esOperacionDestructiva(nodeType, operacionONombre) {
105
+ const op = operacionDeToolkit(nodeType, operacionONombre);
106
+ return op ? op.destructive === true : null;
107
+ }
108
+ /* ── Quién tiene toolkit, el bloqueo por nodo y la puerta ───────────────── */
109
+ /**
110
+ * Los tipos con modo AI toolkit de VARIAS herramientas: los de este registro
111
+ * más WhatsApp y Social, cuyas herramientas aún no viven aquí. Es la lista de
112
+ * quién se niega a correr sin herramienta pedida.
113
+ *
114
+ * RSS, Mailchimp o Shopify también tienen `aiEnabled`, pero exponen el nodo
115
+ * como UNA herramienta sin `operation`: no están aquí a propósito.
116
+ */
117
+ exports.TIPOS_CON_TOOLKIT = Object.freeze([
118
+ ...Object.keys(exports.TOOLKITS_POR_TIPO),
119
+ 'whatsappAction',
120
+ 'socialMediaAction',
121
+ ]);
122
+ function tieneModoToolkit(nodeType) {
123
+ return !!nodeType && exports.TIPOS_CON_TOOLKIT.includes(nodeType);
124
+ }
125
+ /**
126
+ * ¿Admite «Block these tool calls»? Los de este registro: para bloquear hay
127
+ * que saber traducir la operación que guarda el AI Node al nombre de la
128
+ * herramienta que guarda el nodo, y eso sólo se sabe aquí.
129
+ */
130
+ function admiteBloqueo(nodeType) {
131
+ return !!nodeType && Object.prototype.hasOwnProperty.call(exports.TOOLKITS_POR_TIPO, nodeType);
132
+ }
133
+ /** Los nombres de herramienta de un toolkit: lo único que `blockedTools` acepta. */
134
+ function nombresDeHerramientas(nodeType) {
135
+ if (!admiteBloqueo(nodeType))
136
+ return [];
137
+ return exports.TOOLKITS_POR_TIPO[nodeType].map((s) => s.toolName);
138
+ }
139
+ /** Los nombres bloqueados de un nodo, tolerando documentos sin el campo. */
140
+ function bloqueadasDe(entidad) {
141
+ const lista = entidad?.blockedTools;
142
+ return Array.isArray(lista) ? lista.filter((x) => typeof x === 'string') : [];
143
+ }
144
+ /**
145
+ * ¿Está bloqueada en este nodo? Acepta la operación (`deleteOne`, lo que guarda
146
+ * el AI Node en `operationOverride`) o el nombre (`delete_document`, lo que
147
+ * recibe el servidor MCP).
148
+ */
149
+ function herramientaBloqueadaEn(entidad, nodeType, operacionONombre) {
150
+ if (!operacionONombre)
151
+ return false;
152
+ const bloqueadas = bloqueadasDe(entidad);
153
+ if (bloqueadas.length === 0)
154
+ return false;
155
+ const op = operacionDeToolkit(nodeType, operacionONombre);
156
+ return bloqueadas.includes(op?.toolName ?? operacionONombre);
157
+ }
158
+ /** Lo que se contesta a una llamada sin herramienta. Lo lee una persona en el
159
+ * historial —una arista vieja— o el modelo, así que dice qué hacer. */
160
+ exports.MENSAJE_SIN_HERRAMIENTA = 'This node is an AI toolkit: it only runs the tool an AI Node or an MCP client names, and this call named none. If a connection in the flow leads here, remove it — in toolkit mode the node is not part of the pipeline.';
161
+ function mensajeDeHerramientaBloqueada(toolName) {
162
+ return `The tool "${toolName}" is blocked on this node by its owner. Do not retry it; tell the user it is not allowed here.`;
163
+ }
164
+ /**
165
+ * Por qué una llamada NO debe correr en este nodo, o `null` si puede.
166
+ *
167
+ * Sólo mira nodos en modo toolkit (`aiEnabled` y un tipo de
168
+ * {@link TIPOS_CON_TOOLKIT}); cualquier otro nodo corre como siempre. En modo
169
+ * toolkit:
170
+ * - sin `operation` → {@link MENSAJE_SIN_HERRAMIENTA};
171
+ * - con una herramienta bloqueada en el nodo → su mensaje.
172
+ *
173
+ * Una operación que el registro no conoce NO se rechaza aquí: cada servicio
174
+ * sabe qué operaciones acepta (Mongo y Postgres las validan con más detalle),
175
+ * y rechazar lo desconocido aquí rompería operaciones legítimas que no son
176
+ * herramientas.
177
+ */
178
+ function motivoParaNoCorrer(nodeType, entidad, payload) {
179
+ const e = entidad;
180
+ if (!e || e.aiEnabled !== true || !tieneModoToolkit(nodeType))
181
+ return null;
182
+ const pedida = payload?.operation;
183
+ if (typeof pedida !== 'string' || !pedida.trim())
184
+ return exports.MENSAJE_SIN_HERRAMIENTA;
185
+ if (herramientaBloqueadaEn(entidad, nodeType, pedida)) {
186
+ const op = operacionDeToolkit(nodeType, pedida);
187
+ return mensajeDeHerramientaBloqueada(op?.toolName ?? pedida);
188
+ }
189
+ return null;
190
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostwebhook/node-types",
3
- "version": "1.86.0",
3
+ "version": "1.88.0",
4
4
  "description": "Shared node type definitions, connection rules, and dispatch config for HostWebhook",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",