@hostwebhook/node-types 1.87.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.
- package/dist/esm/google-contacts-toolkit.d.ts +46 -0
- package/dist/esm/google-contacts-toolkit.js +180 -0
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +5 -1
- package/dist/esm/toolkits.d.ts +69 -5
- package/dist/esm/toolkits.js +109 -5
- package/dist/google-contacts-toolkit.d.ts +46 -0
- package/dist/google-contacts-toolkit.js +183 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +17 -1
- package/dist/toolkits.d.ts +69 -5
- package/dist/toolkits.js +117 -6
- package/package.json +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
3
|
+
* expone cuando `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
6
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
7
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
8
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
9
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
10
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
11
|
+
*
|
|
12
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
13
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
14
|
+
* Discord y Slack.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
17
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
18
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
19
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
20
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
21
|
+
*
|
|
22
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
23
|
+
*/
|
|
24
|
+
import type { GoogleContactsOperation } from './google-contacts-operations.js';
|
|
25
|
+
export interface GoogleContactsToolkitParameter {
|
|
26
|
+
name: string;
|
|
27
|
+
type: 'string' | 'number' | 'boolean';
|
|
28
|
+
description: string;
|
|
29
|
+
required: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface GoogleContactsToolkitSpec {
|
|
32
|
+
operation: GoogleContactsOperation;
|
|
33
|
+
/** Etiqueta corta de la fila en la lista de herramientas. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** Nombre con el que el LLM llama a la herramienta. */
|
|
36
|
+
toolName: string;
|
|
37
|
+
/** Descripción (más reglas de uso) que ve el LLM. */
|
|
38
|
+
description: string;
|
|
39
|
+
parameters: GoogleContactsToolkitParameter[];
|
|
40
|
+
/** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
|
|
41
|
+
* nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
|
|
42
|
+
destructive?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_SPECS: GoogleContactsToolkitSpec[];
|
|
45
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
46
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME: Record<string, GoogleContactsToolkitSpec>;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
3
|
+
* expone cuando `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
6
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
7
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
8
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
9
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
10
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
11
|
+
*
|
|
12
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
13
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
14
|
+
* Discord y Slack.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
17
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
18
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
19
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
20
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
21
|
+
*
|
|
22
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
23
|
+
*/
|
|
24
|
+
import { marcarDestructivas } from './clase-de-herramienta.js';
|
|
25
|
+
const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
|
|
26
|
+
export const GOOGLE_CONTACTS_TOOLKIT_SPECS = marcarDestructivas([
|
|
27
|
+
// ── Read ─────────────────────────────────────────────────────────
|
|
28
|
+
{
|
|
29
|
+
operation: 'searchContacts',
|
|
30
|
+
label: 'Search contacts',
|
|
31
|
+
toolName: 'search_contacts',
|
|
32
|
+
description: "Free-text search across the user's contacts. Matches names, emails, phones, and organization. Use this BEFORE updateContact / deleteContact / addContactToGroup when the user references a contact by name or email — never invent a resourceName.",
|
|
33
|
+
parameters: [
|
|
34
|
+
p('query', 'Free-text query (name fragment, email, phone, organization).'),
|
|
35
|
+
p('pageSize', 'Max results (default 30, hard cap 30).', false, 'number'),
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
operation: 'listContacts',
|
|
40
|
+
label: 'List contacts',
|
|
41
|
+
toolName: 'list_contacts',
|
|
42
|
+
description: 'Paginated list of every contact. Prefer searchContacts for name/email lookup — listContacts returns hundreds of rows even when the user named one.',
|
|
43
|
+
parameters: [
|
|
44
|
+
p('pageSize', 'Page size (default 100, max 1000).', false, 'number'),
|
|
45
|
+
p('pageToken', 'Token returned by previous listContacts. Empty for first page.', false),
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
operation: 'getContact',
|
|
50
|
+
label: 'Get contact',
|
|
51
|
+
toolName: 'get_contact',
|
|
52
|
+
description: 'Fetch a single contact by resourceName. Use AFTER searchContacts to read the full record before updating.',
|
|
53
|
+
parameters: [
|
|
54
|
+
p('resourceName', 'People API id, e.g. "people/c12345". Get from searchContacts results.'),
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
// ── Write ────────────────────────────────────────────────────────
|
|
58
|
+
{
|
|
59
|
+
operation: 'createContact',
|
|
60
|
+
label: 'Create contact',
|
|
61
|
+
toolName: 'create_contact',
|
|
62
|
+
description: "Add a new contact to the user's address book. NEVER call this without confirming with the user first — duplicate contacts are messy to clean up. Pass `fullName` and the helper auto-splits given/family. Pass `emailAddresses` and `phoneNumbers` as arrays of strings.",
|
|
63
|
+
parameters: [
|
|
64
|
+
p('fullName', 'Full name (e.g. "Ariel Pérez"). Auto-splits to given + family.', false),
|
|
65
|
+
p('givenName', 'First name. Overrides the fullName split.', false),
|
|
66
|
+
p('familyName', 'Last name. Overrides the fullName split.', false),
|
|
67
|
+
p('emailAddresses', 'Array of email addresses. JSON-encode if your tool layer expects a string.', false),
|
|
68
|
+
p('phoneNumbers', 'Array of phone numbers.', false),
|
|
69
|
+
p('notes', 'Free-form note attached to the contact.', false),
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
operation: 'updateContact',
|
|
74
|
+
label: 'Update contact',
|
|
75
|
+
toolName: 'update_contact',
|
|
76
|
+
description: "Change fields on an existing contact. Get the resourceName from searchContacts first. Update mask is auto-derived from the fields you pass — only include fields you want changed (passing `notes` won't blank out the email).",
|
|
77
|
+
parameters: [
|
|
78
|
+
p('resourceName', 'People API id, e.g. "people/c12345".'),
|
|
79
|
+
p('fullName', 'New full name (auto-splits).', false),
|
|
80
|
+
p('emailAddresses', 'Replace email list. Pass [] to clear.', false),
|
|
81
|
+
p('phoneNumbers', 'Replace phone list.', false),
|
|
82
|
+
p('notes', "Replace the contact's note.", false),
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
operation: 'deleteContact',
|
|
87
|
+
label: 'Delete contact',
|
|
88
|
+
toolName: 'delete_contact',
|
|
89
|
+
description: 'Permanently remove a contact. Irreversible. ALWAYS confirm with the user before calling. Use searchContacts first to verify you have the right resourceName.',
|
|
90
|
+
parameters: [p('resourceName', 'People API id of the contact to delete.')],
|
|
91
|
+
},
|
|
92
|
+
// ── Groups ───────────────────────────────────────────────────────
|
|
93
|
+
{
|
|
94
|
+
operation: 'listContactGroups',
|
|
95
|
+
label: 'List groups',
|
|
96
|
+
toolName: 'list_contact_groups',
|
|
97
|
+
description: "List the user's contact groups (labels). Call this BEFORE addContactToGroup / removeContactFromGroup so you have the right groupResourceName.",
|
|
98
|
+
parameters: [
|
|
99
|
+
p('pageSize', 'Page size (default 200, max 1000).', false, 'number'),
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
operation: 'addContactToGroup',
|
|
104
|
+
label: 'Add to group',
|
|
105
|
+
toolName: 'add_contact_to_group',
|
|
106
|
+
description: 'Add a contact to a group (label). Idempotent — no error if the contact is already in the group. Get the groupResourceName from listContactGroups.',
|
|
107
|
+
parameters: [
|
|
108
|
+
p('resourceName', 'Contact id, e.g. "people/c12345". Or array of contact ids.'),
|
|
109
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
operation: 'removeContactFromGroup',
|
|
114
|
+
label: 'Remove from group',
|
|
115
|
+
toolName: 'remove_contact_from_group',
|
|
116
|
+
description: 'Remove a contact from a group. Idempotent. Does NOT delete the contact, only its membership in that group.',
|
|
117
|
+
parameters: [
|
|
118
|
+
p('resourceName', 'Contact id, e.g. "people/c12345".'),
|
|
119
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
// ── V2 — bulk + group management ─────────────────────────────────
|
|
123
|
+
{
|
|
124
|
+
operation: 'batchCreateContacts',
|
|
125
|
+
label: 'Batch create contacts',
|
|
126
|
+
toolName: 'batch_create_contacts',
|
|
127
|
+
description: 'Bulk-create up to 200 contacts in one call. Each contact in the array uses the same shape as createContact (fullName / emailAddresses / phoneNumbers / notes / etc.).',
|
|
128
|
+
parameters: [
|
|
129
|
+
p('contacts', 'Array of contact objects. Same shape as createContact.', true),
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
operation: 'batchUpdateContacts',
|
|
134
|
+
label: 'Batch update contacts',
|
|
135
|
+
toolName: 'batch_update_contacts',
|
|
136
|
+
description: 'Bulk-update up to 200 existing contacts. Each entry must include `resourceName`. Pass `updateMask` (e.g. "names,emailAddresses") to control which fields the API replaces.',
|
|
137
|
+
parameters: [
|
|
138
|
+
p('contacts', 'Array of {resourceName, ...fields} objects.', true),
|
|
139
|
+
p('updateMask', 'Comma-separated People API field paths to replace.'),
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
operation: 'batchDeleteContacts',
|
|
144
|
+
label: 'Batch delete contacts',
|
|
145
|
+
toolName: 'batch_delete_contacts',
|
|
146
|
+
description: 'Bulk-delete up to 500 contacts by resourceName. Irreversible.',
|
|
147
|
+
parameters: [
|
|
148
|
+
p('resourceNames', 'Array of People API ids, each like "people/c12345".'),
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
operation: 'createContactGroup',
|
|
153
|
+
label: 'Create group',
|
|
154
|
+
toolName: 'create_contact_group',
|
|
155
|
+
description: 'Create a new contact group (label).',
|
|
156
|
+
parameters: [p('name', 'Group name as displayed in Contacts.')],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
operation: 'updateContactGroup',
|
|
160
|
+
label: 'Rename group',
|
|
161
|
+
toolName: 'update_contact_group',
|
|
162
|
+
description: 'Rename an existing contact group.',
|
|
163
|
+
parameters: [
|
|
164
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
165
|
+
p('name', 'New name for the group.'),
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
operation: 'deleteContactGroup',
|
|
170
|
+
label: 'Delete group',
|
|
171
|
+
toolName: 'delete_contact_group',
|
|
172
|
+
description: 'Delete a contact group. Pass `deleteContacts: true` to also delete the contacts in the group (otherwise contacts stay, just lose this membership). Irreversible — confirm with the user before calling.',
|
|
173
|
+
parameters: [
|
|
174
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
175
|
+
p('deleteContacts', 'When true, also delete contacts in the group. Default false.', false, 'boolean'),
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
]);
|
|
179
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
180
|
+
export const GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(GOOGLE_CONTACTS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -76,7 +76,9 @@ export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgr
|
|
|
76
76
|
export type { PostgresToolkitSpec, PostgresToolkitParameter, } from './postgres-toolkit.js';
|
|
77
77
|
export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
|
|
78
78
|
export type { ClaseDeHerramienta } from './clase-de-herramienta.js';
|
|
79
|
-
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
|
|
79
|
+
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CON_TOOLKIT, tieneModoToolkit, admiteBloqueo, nombresDeHerramientas, bloqueadasDe, herramientaBloqueadaEn, MENSAJE_SIN_HERRAMIENTA, mensajeDeHerramientaBloqueada, motivoParaNoCorrer, } from './toolkits.js';
|
|
80
|
+
export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
|
|
81
|
+
export type { GoogleContactsToolkitSpec, GoogleContactsToolkitParameter, } from './google-contacts-toolkit.js';
|
|
80
82
|
export type { OperacionDeToolkit } from './toolkits.js';
|
|
81
83
|
export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
|
|
82
84
|
export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -57,7 +57,11 @@ export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE
|
|
|
57
57
|
export { MONGO_TOOLKIT_SPECS, MONGO_TOOLKIT_BY_TOOL_NAME, } from './mongo-toolkit.js';
|
|
58
58
|
export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgres-toolkit.js';
|
|
59
59
|
export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
|
|
60
|
-
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
|
|
60
|
+
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CON_TOOLKIT, tieneModoToolkit, admiteBloqueo, nombresDeHerramientas, bloqueadasDe, herramientaBloqueadaEn, MENSAJE_SIN_HERRAMIENTA, mensajeDeHerramientaBloqueada, motivoParaNoCorrer, } from './toolkits.js';
|
|
61
|
+
/* Contacts se escribía a mano en la api y en el dashboard; ahora una vez. Se
|
|
62
|
+
llama `…_TOOLKIT_SPECS` porque `GOOGLE_CONTACTS_OPERATION_SPECS` ya es el
|
|
63
|
+
formulario del nodo — la misma pareja que Discord y Slack. */
|
|
64
|
+
export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
|
|
61
65
|
export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
|
|
62
66
|
export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
|
|
63
67
|
export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
|
package/dist/esm/toolkits.d.ts
CHANGED
|
@@ -14,11 +14,30 @@
|
|
|
14
14
|
*
|
|
15
15
|
* ## Lo que falta aquí, a propósito
|
|
16
16
|
*
|
|
17
|
-
* WhatsApp y
|
|
18
|
-
*
|
|
19
|
-
* registro
|
|
20
|
-
* devuelve `null` para ellos, que
|
|
21
|
-
* destructiva».
|
|
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`.
|
|
22
41
|
*/
|
|
23
42
|
/** Lo mínimo de una herramienta de toolkit que hace falta para decidir. */
|
|
24
43
|
export interface OperacionDeToolkit {
|
|
@@ -45,3 +64,48 @@ export declare function operacionDeToolkit(nodeType: string | undefined | null,
|
|
|
45
64
|
* las herramientas que no son de toolkit (MCP, HTTP…).
|
|
46
65
|
*/
|
|
47
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;
|
package/dist/esm/toolkits.js
CHANGED
|
@@ -14,11 +14,30 @@
|
|
|
14
14
|
*
|
|
15
15
|
* ## Lo que falta aquí, a propósito
|
|
16
16
|
*
|
|
17
|
-
* WhatsApp y
|
|
18
|
-
*
|
|
19
|
-
* registro
|
|
20
|
-
* devuelve `null` para ellos, que
|
|
21
|
-
* destructiva».
|
|
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`.
|
|
22
41
|
*/
|
|
23
42
|
import { GMAIL_ALL_TOOLKIT_SPECS, NATIVE_EMAIL_TOOLKIT_SPECS } from './gmail-operations.js';
|
|
24
43
|
import { GOOGLE_CALENDAR_TOOLKIT_SPECS } from './calendar-toolkit.js';
|
|
@@ -30,6 +49,7 @@ import { SHEETS_TOOLKIT_SPECS } from './sheets-toolkit.js';
|
|
|
30
49
|
import { DOCS_TOOLKIT_SPECS } from './docs-toolkit.js';
|
|
31
50
|
import { MONGO_TOOLKIT_SPECS } from './mongo-toolkit.js';
|
|
32
51
|
import { POSTGRES_TOOLKIT_SPECS } from './postgres-toolkit.js';
|
|
52
|
+
import { GOOGLE_CONTACTS_TOOLKIT_SPECS } from './google-contacts-toolkit.js';
|
|
33
53
|
export const TOOLKITS_POR_TIPO = Object.freeze({
|
|
34
54
|
gmailAction: GMAIL_ALL_TOOLKIT_SPECS,
|
|
35
55
|
emailAction: NATIVE_EMAIL_TOOLKIT_SPECS,
|
|
@@ -42,6 +62,7 @@ export const TOOLKITS_POR_TIPO = Object.freeze({
|
|
|
42
62
|
docsAction: DOCS_TOOLKIT_SPECS,
|
|
43
63
|
mongoAction: MONGO_TOOLKIT_SPECS,
|
|
44
64
|
postgresAction: POSTGRES_TOOLKIT_SPECS,
|
|
65
|
+
googleContactsAction: GOOGLE_CONTACTS_TOOLKIT_SPECS,
|
|
45
66
|
});
|
|
46
67
|
/**
|
|
47
68
|
* La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
|
|
@@ -72,3 +93,86 @@ export function esOperacionDestructiva(nodeType, operacionONombre) {
|
|
|
72
93
|
const op = operacionDeToolkit(nodeType, operacionONombre);
|
|
73
94
|
return op ? op.destructive === true : null;
|
|
74
95
|
}
|
|
96
|
+
/* ── Quién tiene toolkit, el bloqueo por nodo y la puerta ───────────────── */
|
|
97
|
+
/**
|
|
98
|
+
* Los tipos con modo AI toolkit de VARIAS herramientas: los de este registro
|
|
99
|
+
* más WhatsApp y Social, cuyas herramientas aún no viven aquí. Es la lista de
|
|
100
|
+
* quién se niega a correr sin herramienta pedida.
|
|
101
|
+
*
|
|
102
|
+
* RSS, Mailchimp o Shopify también tienen `aiEnabled`, pero exponen el nodo
|
|
103
|
+
* como UNA herramienta sin `operation`: no están aquí a propósito.
|
|
104
|
+
*/
|
|
105
|
+
export const TIPOS_CON_TOOLKIT = Object.freeze([
|
|
106
|
+
...Object.keys(TOOLKITS_POR_TIPO),
|
|
107
|
+
'whatsappAction',
|
|
108
|
+
'socialMediaAction',
|
|
109
|
+
]);
|
|
110
|
+
export function tieneModoToolkit(nodeType) {
|
|
111
|
+
return !!nodeType && TIPOS_CON_TOOLKIT.includes(nodeType);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* ¿Admite «Block these tool calls»? Los de este registro: para bloquear hay
|
|
115
|
+
* que saber traducir la operación que guarda el AI Node al nombre de la
|
|
116
|
+
* herramienta que guarda el nodo, y eso sólo se sabe aquí.
|
|
117
|
+
*/
|
|
118
|
+
export function admiteBloqueo(nodeType) {
|
|
119
|
+
return !!nodeType && Object.prototype.hasOwnProperty.call(TOOLKITS_POR_TIPO, nodeType);
|
|
120
|
+
}
|
|
121
|
+
/** Los nombres de herramienta de un toolkit: lo único que `blockedTools` acepta. */
|
|
122
|
+
export function nombresDeHerramientas(nodeType) {
|
|
123
|
+
if (!admiteBloqueo(nodeType))
|
|
124
|
+
return [];
|
|
125
|
+
return TOOLKITS_POR_TIPO[nodeType].map((s) => s.toolName);
|
|
126
|
+
}
|
|
127
|
+
/** Los nombres bloqueados de un nodo, tolerando documentos sin el campo. */
|
|
128
|
+
export function bloqueadasDe(entidad) {
|
|
129
|
+
const lista = entidad?.blockedTools;
|
|
130
|
+
return Array.isArray(lista) ? lista.filter((x) => typeof x === 'string') : [];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* ¿Está bloqueada en este nodo? Acepta la operación (`deleteOne`, lo que guarda
|
|
134
|
+
* el AI Node en `operationOverride`) o el nombre (`delete_document`, lo que
|
|
135
|
+
* recibe el servidor MCP).
|
|
136
|
+
*/
|
|
137
|
+
export function herramientaBloqueadaEn(entidad, nodeType, operacionONombre) {
|
|
138
|
+
if (!operacionONombre)
|
|
139
|
+
return false;
|
|
140
|
+
const bloqueadas = bloqueadasDe(entidad);
|
|
141
|
+
if (bloqueadas.length === 0)
|
|
142
|
+
return false;
|
|
143
|
+
const op = operacionDeToolkit(nodeType, operacionONombre);
|
|
144
|
+
return bloqueadas.includes(op?.toolName ?? operacionONombre);
|
|
145
|
+
}
|
|
146
|
+
/** Lo que se contesta a una llamada sin herramienta. Lo lee una persona en el
|
|
147
|
+
* historial —una arista vieja— o el modelo, así que dice qué hacer. */
|
|
148
|
+
export 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 — in toolkit mode the node is not part of the pipeline.';
|
|
149
|
+
export function mensajeDeHerramientaBloqueada(toolName) {
|
|
150
|
+
return `The tool "${toolName}" is blocked on this node by its owner. Do not retry it; tell the user it is not allowed here.`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Por qué una llamada NO debe correr en este nodo, o `null` si puede.
|
|
154
|
+
*
|
|
155
|
+
* Sólo mira nodos en modo toolkit (`aiEnabled` y un tipo de
|
|
156
|
+
* {@link TIPOS_CON_TOOLKIT}); cualquier otro nodo corre como siempre. En modo
|
|
157
|
+
* toolkit:
|
|
158
|
+
* - sin `operation` → {@link MENSAJE_SIN_HERRAMIENTA};
|
|
159
|
+
* - con una herramienta bloqueada en el nodo → su mensaje.
|
|
160
|
+
*
|
|
161
|
+
* Una operación que el registro no conoce NO se rechaza aquí: cada servicio
|
|
162
|
+
* sabe qué operaciones acepta (Mongo y Postgres las validan con más detalle),
|
|
163
|
+
* y rechazar lo desconocido aquí rompería operaciones legítimas que no son
|
|
164
|
+
* herramientas.
|
|
165
|
+
*/
|
|
166
|
+
export function motivoParaNoCorrer(nodeType, entidad, payload) {
|
|
167
|
+
const e = entidad;
|
|
168
|
+
if (!e || e.aiEnabled !== true || !tieneModoToolkit(nodeType))
|
|
169
|
+
return null;
|
|
170
|
+
const pedida = payload?.operation;
|
|
171
|
+
if (typeof pedida !== 'string' || !pedida.trim())
|
|
172
|
+
return MENSAJE_SIN_HERRAMIENTA;
|
|
173
|
+
if (herramientaBloqueadaEn(entidad, nodeType, pedida)) {
|
|
174
|
+
const op = operacionDeToolkit(nodeType, pedida);
|
|
175
|
+
return mensajeDeHerramientaBloqueada(op?.toolName ?? pedida);
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
3
|
+
* expone cuando `aiEnabled` está encendido.
|
|
4
|
+
*
|
|
5
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
6
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
7
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
8
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
9
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
10
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
11
|
+
*
|
|
12
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
13
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
14
|
+
* Discord y Slack.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
17
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
18
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
19
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
20
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
21
|
+
*
|
|
22
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
23
|
+
*/
|
|
24
|
+
import type { GoogleContactsOperation } from './google-contacts-operations.js';
|
|
25
|
+
export interface GoogleContactsToolkitParameter {
|
|
26
|
+
name: string;
|
|
27
|
+
type: 'string' | 'number' | 'boolean';
|
|
28
|
+
description: string;
|
|
29
|
+
required: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface GoogleContactsToolkitSpec {
|
|
32
|
+
operation: GoogleContactsOperation;
|
|
33
|
+
/** Etiqueta corta de la fila en la lista de herramientas. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** Nombre con el que el LLM llama a la herramienta. */
|
|
36
|
+
toolName: string;
|
|
37
|
+
/** Descripción (más reglas de uso) que ve el LLM. */
|
|
38
|
+
description: string;
|
|
39
|
+
parameters: GoogleContactsToolkitParameter[];
|
|
40
|
+
/** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
|
|
41
|
+
* nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
|
|
42
|
+
destructive?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_SPECS: GoogleContactsToolkitSpec[];
|
|
45
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
46
|
+
export declare const GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME: Record<string, GoogleContactsToolkitSpec>;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Google Contacts AI Toolkit — las herramientas que un googleContactsAction
|
|
4
|
+
* expone cuando `aiEnabled` está encendido.
|
|
5
|
+
*
|
|
6
|
+
* **Se escribe una sola vez** (2026-09-24). Vivía dos veces: en
|
|
7
|
+
* `api/src/mcp-servers/toolkit-specs.ts` y en
|
|
8
|
+
* `dashboard/components/google-contacts-actions/google-contacts-operations-schemas.ts`,
|
|
9
|
+
* las dos a mano. Al mudarlas coincidían exactamente —las 15 operaciones, sus
|
|
10
|
+
* nombres, textos y parámetros—; el `group` del dashboard ya lo lleva el spec
|
|
11
|
+
* de formulario (`GOOGLE_CONTACTS_OPERATION_SPECS[op].group`).
|
|
12
|
+
*
|
|
13
|
+
* Se llama `GOOGLE_CONTACTS_TOOLKIT_SPECS` y no `…_OPERATION_SPECS` porque ese
|
|
14
|
+
* nombre ya es el del FORMULARIO del nodo en este paquete — la misma pareja que
|
|
15
|
+
* Discord y Slack.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ El `destructive` ya no se escribe a mano: lo pone `marcarDestructivas` por
|
|
18
|
+
* el verbo. Cuatro que la copia vieja marcaba dejan de serlo —crear o añadir no
|
|
19
|
+
* pisa nada—: `create_contact`, `add_contact_to_group`,
|
|
20
|
+
* `batch_create_contacts` y `create_contact_group`. El gate del AI Node ya las
|
|
21
|
+
* leía así (por el verbo del nombre); lo que cambia es la etiqueta del panel.
|
|
22
|
+
*
|
|
23
|
+
* Los textos van en inglés porque los lee el modelo.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CONTACTS_TOOLKIT_SPECS = void 0;
|
|
27
|
+
const clase_de_herramienta_js_1 = require("./clase-de-herramienta.js");
|
|
28
|
+
const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
|
|
29
|
+
exports.GOOGLE_CONTACTS_TOOLKIT_SPECS = (0, clase_de_herramienta_js_1.marcarDestructivas)([
|
|
30
|
+
// ── Read ─────────────────────────────────────────────────────────
|
|
31
|
+
{
|
|
32
|
+
operation: 'searchContacts',
|
|
33
|
+
label: 'Search contacts',
|
|
34
|
+
toolName: 'search_contacts',
|
|
35
|
+
description: "Free-text search across the user's contacts. Matches names, emails, phones, and organization. Use this BEFORE updateContact / deleteContact / addContactToGroup when the user references a contact by name or email — never invent a resourceName.",
|
|
36
|
+
parameters: [
|
|
37
|
+
p('query', 'Free-text query (name fragment, email, phone, organization).'),
|
|
38
|
+
p('pageSize', 'Max results (default 30, hard cap 30).', false, 'number'),
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
operation: 'listContacts',
|
|
43
|
+
label: 'List contacts',
|
|
44
|
+
toolName: 'list_contacts',
|
|
45
|
+
description: 'Paginated list of every contact. Prefer searchContacts for name/email lookup — listContacts returns hundreds of rows even when the user named one.',
|
|
46
|
+
parameters: [
|
|
47
|
+
p('pageSize', 'Page size (default 100, max 1000).', false, 'number'),
|
|
48
|
+
p('pageToken', 'Token returned by previous listContacts. Empty for first page.', false),
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
operation: 'getContact',
|
|
53
|
+
label: 'Get contact',
|
|
54
|
+
toolName: 'get_contact',
|
|
55
|
+
description: 'Fetch a single contact by resourceName. Use AFTER searchContacts to read the full record before updating.',
|
|
56
|
+
parameters: [
|
|
57
|
+
p('resourceName', 'People API id, e.g. "people/c12345". Get from searchContacts results.'),
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
// ── Write ────────────────────────────────────────────────────────
|
|
61
|
+
{
|
|
62
|
+
operation: 'createContact',
|
|
63
|
+
label: 'Create contact',
|
|
64
|
+
toolName: 'create_contact',
|
|
65
|
+
description: "Add a new contact to the user's address book. NEVER call this without confirming with the user first — duplicate contacts are messy to clean up. Pass `fullName` and the helper auto-splits given/family. Pass `emailAddresses` and `phoneNumbers` as arrays of strings.",
|
|
66
|
+
parameters: [
|
|
67
|
+
p('fullName', 'Full name (e.g. "Ariel Pérez"). Auto-splits to given + family.', false),
|
|
68
|
+
p('givenName', 'First name. Overrides the fullName split.', false),
|
|
69
|
+
p('familyName', 'Last name. Overrides the fullName split.', false),
|
|
70
|
+
p('emailAddresses', 'Array of email addresses. JSON-encode if your tool layer expects a string.', false),
|
|
71
|
+
p('phoneNumbers', 'Array of phone numbers.', false),
|
|
72
|
+
p('notes', 'Free-form note attached to the contact.', false),
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
operation: 'updateContact',
|
|
77
|
+
label: 'Update contact',
|
|
78
|
+
toolName: 'update_contact',
|
|
79
|
+
description: "Change fields on an existing contact. Get the resourceName from searchContacts first. Update mask is auto-derived from the fields you pass — only include fields you want changed (passing `notes` won't blank out the email).",
|
|
80
|
+
parameters: [
|
|
81
|
+
p('resourceName', 'People API id, e.g. "people/c12345".'),
|
|
82
|
+
p('fullName', 'New full name (auto-splits).', false),
|
|
83
|
+
p('emailAddresses', 'Replace email list. Pass [] to clear.', false),
|
|
84
|
+
p('phoneNumbers', 'Replace phone list.', false),
|
|
85
|
+
p('notes', "Replace the contact's note.", false),
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
operation: 'deleteContact',
|
|
90
|
+
label: 'Delete contact',
|
|
91
|
+
toolName: 'delete_contact',
|
|
92
|
+
description: 'Permanently remove a contact. Irreversible. ALWAYS confirm with the user before calling. Use searchContacts first to verify you have the right resourceName.',
|
|
93
|
+
parameters: [p('resourceName', 'People API id of the contact to delete.')],
|
|
94
|
+
},
|
|
95
|
+
// ── Groups ───────────────────────────────────────────────────────
|
|
96
|
+
{
|
|
97
|
+
operation: 'listContactGroups',
|
|
98
|
+
label: 'List groups',
|
|
99
|
+
toolName: 'list_contact_groups',
|
|
100
|
+
description: "List the user's contact groups (labels). Call this BEFORE addContactToGroup / removeContactFromGroup so you have the right groupResourceName.",
|
|
101
|
+
parameters: [
|
|
102
|
+
p('pageSize', 'Page size (default 200, max 1000).', false, 'number'),
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
operation: 'addContactToGroup',
|
|
107
|
+
label: 'Add to group',
|
|
108
|
+
toolName: 'add_contact_to_group',
|
|
109
|
+
description: 'Add a contact to a group (label). Idempotent — no error if the contact is already in the group. Get the groupResourceName from listContactGroups.',
|
|
110
|
+
parameters: [
|
|
111
|
+
p('resourceName', 'Contact id, e.g. "people/c12345". Or array of contact ids.'),
|
|
112
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
operation: 'removeContactFromGroup',
|
|
117
|
+
label: 'Remove from group',
|
|
118
|
+
toolName: 'remove_contact_from_group',
|
|
119
|
+
description: 'Remove a contact from a group. Idempotent. Does NOT delete the contact, only its membership in that group.',
|
|
120
|
+
parameters: [
|
|
121
|
+
p('resourceName', 'Contact id, e.g. "people/c12345".'),
|
|
122
|
+
p('groupResourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
// ── V2 — bulk + group management ─────────────────────────────────
|
|
126
|
+
{
|
|
127
|
+
operation: 'batchCreateContacts',
|
|
128
|
+
label: 'Batch create contacts',
|
|
129
|
+
toolName: 'batch_create_contacts',
|
|
130
|
+
description: 'Bulk-create up to 200 contacts in one call. Each contact in the array uses the same shape as createContact (fullName / emailAddresses / phoneNumbers / notes / etc.).',
|
|
131
|
+
parameters: [
|
|
132
|
+
p('contacts', 'Array of contact objects. Same shape as createContact.', true),
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
operation: 'batchUpdateContacts',
|
|
137
|
+
label: 'Batch update contacts',
|
|
138
|
+
toolName: 'batch_update_contacts',
|
|
139
|
+
description: 'Bulk-update up to 200 existing contacts. Each entry must include `resourceName`. Pass `updateMask` (e.g. "names,emailAddresses") to control which fields the API replaces.',
|
|
140
|
+
parameters: [
|
|
141
|
+
p('contacts', 'Array of {resourceName, ...fields} objects.', true),
|
|
142
|
+
p('updateMask', 'Comma-separated People API field paths to replace.'),
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
operation: 'batchDeleteContacts',
|
|
147
|
+
label: 'Batch delete contacts',
|
|
148
|
+
toolName: 'batch_delete_contacts',
|
|
149
|
+
description: 'Bulk-delete up to 500 contacts by resourceName. Irreversible.',
|
|
150
|
+
parameters: [
|
|
151
|
+
p('resourceNames', 'Array of People API ids, each like "people/c12345".'),
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
operation: 'createContactGroup',
|
|
156
|
+
label: 'Create group',
|
|
157
|
+
toolName: 'create_contact_group',
|
|
158
|
+
description: 'Create a new contact group (label).',
|
|
159
|
+
parameters: [p('name', 'Group name as displayed in Contacts.')],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
operation: 'updateContactGroup',
|
|
163
|
+
label: 'Rename group',
|
|
164
|
+
toolName: 'update_contact_group',
|
|
165
|
+
description: 'Rename an existing contact group.',
|
|
166
|
+
parameters: [
|
|
167
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
168
|
+
p('name', 'New name for the group.'),
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
operation: 'deleteContactGroup',
|
|
173
|
+
label: 'Delete group',
|
|
174
|
+
toolName: 'delete_contact_group',
|
|
175
|
+
description: 'Delete a contact group. Pass `deleteContacts: true` to also delete the contacts in the group (otherwise contacts stay, just lose this membership). Irreversible — confirm with the user before calling.',
|
|
176
|
+
parameters: [
|
|
177
|
+
p('resourceName', 'Group id, e.g. "contactGroups/12345".'),
|
|
178
|
+
p('deleteContacts', 'When true, also delete contacts in the group. Default false.', false, 'boolean'),
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
/** Las herramientas por nombre, para despachar una llamada del modelo. */
|
|
183
|
+
exports.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(exports.GOOGLE_CONTACTS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
|
package/dist/index.d.ts
CHANGED
|
@@ -76,7 +76,9 @@ export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgr
|
|
|
76
76
|
export type { PostgresToolkitSpec, PostgresToolkitParameter, } from './postgres-toolkit.js';
|
|
77
77
|
export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
|
|
78
78
|
export type { ClaseDeHerramienta } from './clase-de-herramienta.js';
|
|
79
|
-
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
|
|
79
|
+
export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, TIPOS_CON_TOOLKIT, tieneModoToolkit, admiteBloqueo, nombresDeHerramientas, bloqueadasDe, herramientaBloqueadaEn, MENSAJE_SIN_HERRAMIENTA, mensajeDeHerramientaBloqueada, motivoParaNoCorrer, } from './toolkits.js';
|
|
80
|
+
export { GOOGLE_CONTACTS_TOOLKIT_SPECS, GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME, } from './google-contacts-toolkit.js';
|
|
81
|
+
export type { GoogleContactsToolkitSpec, GoogleContactsToolkitParameter, } from './google-contacts-toolkit.js';
|
|
80
82
|
export type { OperacionDeToolkit } from './toolkits.js';
|
|
81
83
|
export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
|
|
82
84
|
export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
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_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.versionCatalogErrors = exports.fieldsLost = exports.fieldsLostBetween = exports.currentVersion = exports.versionSpec = exports.versionsOf = exports.isVersioned = 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.pasaLoQueRecibe = exports.getNodeDispatchConfig = exports.getAllNodeCollections = exports.NODE_DISPATCH = exports.todosLosOutputGroups = exports.outputGroupsFor = exports.esEtiquetaDeSentimiento = exports.SENTIMENT_PORT_SUMMARY = exports.SENTIMENT_PORT_LABEL = exports.SENTIMENT_PORTS = exports.SENTIMENT_UNCLEAR = exports.SENTIMENT_LABELS = 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
4
|
exports.isGithubOperation = exports.GITHUB_ITERABLE_OPERATIONS = exports.GITHUB_DROPDOWN_OPERATIONS = exports.GITHUB_OPERATION_SPECS = exports.GITHUB_OPERATIONS = exports.scopesQueFaltanEnShopify = exports.isShopifyOperation = exports.SHOPIFY_PRODUCT_STATUSES = exports.SHOPIFY_CANCEL_REASONS = exports.SHOPIFY_SEARCHABLE_RESOURCES = exports.SHOPIFY_TAGGABLE_RESOURCES = exports.SHOPIFY_OPERATION_SCOPES = exports.SHOPIFY_OPERATION_SPECS = exports.SHOPIFY_OPERATIONS = exports.isMailchimpOperation = exports.MAILCHIMP_CONTACT_STATUSES = exports.MAILCHIMP_OPERATION_SPECS = exports.MAILCHIMP_OPERATIONS = exports.herramientasDeDiscordPara = exports.DISCORD_TOOLKIT_BY_TOOL_NAME = exports.DISCORD_TOOLKIT_SPECS = exports.isDiscordOperation = exports.camposDeDiscordNoDisponibles = exports.discordPuedeEjecutar = exports.operacionesDeDiscordPara = exports.DISCORD_CAPACIDADES_POR_CREDENCIAL = exports.DISCORD_OPERATION_SPECS = exports.DISCORD_OPERATIONS = exports.camposNoDisponiblesPara = exports.puedeEjecutar = exports.operacionesPara = 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.resolveGmailSendFields = void 0;
|
|
5
5
|
exports.NOTION_OPERATION_SPECS = exports.NOTION_OPERATIONS = exports.isGoogleAnalyticsOperation = exports.GOOGLE_ANALYTICS_DROPDOWN_OPERATIONS = exports.GOOGLE_ANALYTICS_OPERATION_SPECS = exports.GOOGLE_ANALYTICS_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.herramientasDeSlackPara = exports.SLACK_TOOLKIT_BY_TOOL_NAME = exports.SLACK_TOOLKIT_SPECS = exports.isSlackOperation = exports.camposDeSlackNoDisponibles = exports.slackPuedeEjecutar = exports.operacionesDeSlackPara = exports.SLACK_CAPACIDADES_POR_CREDENCIAL = exports.SLACK_OPERATION_SPECS = exports.SLACK_OPERATIONS = exports.isAirtableOperation = exports.AIRTABLE_ITERABLE_OPERATIONS = exports.AIRTABLE_OPERATION_SPECS = exports.AIRTABLE_OPERATIONS = exports.isCalendlyOperation = exports.CALENDLY_ITERABLE_OPERATIONS = exports.CALENDLY_OPERATION_SPECS = exports.CALENDLY_OPERATIONS = exports.isApifyOperation = exports.APIFY_ITERABLE_OPERATIONS = exports.APIFY_OPERATION_SPECS = exports.APIFY_OPERATIONS = exports.isBucketOperation = exports.BUCKET_ITERABLE_OPERATIONS = exports.BUCKET_OPERATION_SPECS = exports.BUCKET_OPERATIONS = exports.isJiraOperation = exports.JIRA_ITERABLE_OPERATIONS = exports.JIRA_DROPDOWN_OPERATIONS = exports.JIRA_OPERATION_SPECS = exports.JIRA_OPERATIONS = void 0;
|
|
6
|
-
exports.
|
|
6
|
+
exports.isCredentialType = exports.getCredentialType = exports.credentialTypeValues = exports.CREDENTIAL_TYPE_VALUES = exports.CREDENTIAL_TYPES = exports.ventanaDeContextoDeOpenRouter = exports.opcionesDeModelosDeOpenRouter = exports.URL_DE_MODELOS_DE_OPENROUTER = exports.getModelLabel = exports.getDefaultModel = exports.getModelsFor = exports.MODEL_CONTEXT_WINDOWS = exports.LLM_MODELS = exports.LLM_PROVIDERS = exports.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME = exports.GOOGLE_CONTACTS_TOOLKIT_SPECS = exports.motivoParaNoCorrer = exports.mensajeDeHerramientaBloqueada = exports.MENSAJE_SIN_HERRAMIENTA = exports.herramientaBloqueadaEn = exports.bloqueadasDe = exports.nombresDeHerramientas = exports.admiteBloqueo = exports.tieneModoToolkit = exports.TIPOS_CON_TOOLKIT = exports.esOperacionDestructiva = exports.operacionDeToolkit = exports.TOOLKITS_POR_TIPO = exports.marcarDestructivas = exports.esHerramientaDestructiva = exports.claseDeHerramienta = exports.POSTGRES_TOOLKIT_BY_TOOL_NAME = exports.POSTGRES_TOOLKIT_SPECS = exports.MONGO_TOOLKIT_BY_TOOL_NAME = exports.MONGO_TOOLKIT_SPECS = 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.isNotionOperation = exports.NOTION_DROPDOWN_OPERATIONS = void 0;
|
|
7
|
+
exports.armarRespuestaDelSync = exports.RESPUESTA_DEL_SYNC_POR_DEFECTO = exports.TOPE_DE_ESPERA_DEL_SYNC = exports.TOPES_DE_ESTADO = exports.SOBRES_DE_RESPUESTA = exports.MODOS_DE_PAYLOAD = exports.NIVELES_DE_DETALLE = exports.RESULTADOS_DEL_VALIDADOR = exports.contestaEnSync = exports.NODOS_QUE_CONTESTAN_EN_SYNC = void 0;
|
|
7
8
|
var types_js_1 = require("./types.js");
|
|
8
9
|
Object.defineProperty(exports, "singleMeta", { enumerable: true, get: function () { return types_js_1.singleMeta; } });
|
|
9
10
|
Object.defineProperty(exports, "iterableMeta", { enumerable: true, get: function () { return types_js_1.iterableMeta; } });
|
|
@@ -239,6 +240,21 @@ var toolkits_js_1 = require("./toolkits.js");
|
|
|
239
240
|
Object.defineProperty(exports, "TOOLKITS_POR_TIPO", { enumerable: true, get: function () { return toolkits_js_1.TOOLKITS_POR_TIPO; } });
|
|
240
241
|
Object.defineProperty(exports, "operacionDeToolkit", { enumerable: true, get: function () { return toolkits_js_1.operacionDeToolkit; } });
|
|
241
242
|
Object.defineProperty(exports, "esOperacionDestructiva", { enumerable: true, get: function () { return toolkits_js_1.esOperacionDestructiva; } });
|
|
243
|
+
Object.defineProperty(exports, "TIPOS_CON_TOOLKIT", { enumerable: true, get: function () { return toolkits_js_1.TIPOS_CON_TOOLKIT; } });
|
|
244
|
+
Object.defineProperty(exports, "tieneModoToolkit", { enumerable: true, get: function () { return toolkits_js_1.tieneModoToolkit; } });
|
|
245
|
+
Object.defineProperty(exports, "admiteBloqueo", { enumerable: true, get: function () { return toolkits_js_1.admiteBloqueo; } });
|
|
246
|
+
Object.defineProperty(exports, "nombresDeHerramientas", { enumerable: true, get: function () { return toolkits_js_1.nombresDeHerramientas; } });
|
|
247
|
+
Object.defineProperty(exports, "bloqueadasDe", { enumerable: true, get: function () { return toolkits_js_1.bloqueadasDe; } });
|
|
248
|
+
Object.defineProperty(exports, "herramientaBloqueadaEn", { enumerable: true, get: function () { return toolkits_js_1.herramientaBloqueadaEn; } });
|
|
249
|
+
Object.defineProperty(exports, "MENSAJE_SIN_HERRAMIENTA", { enumerable: true, get: function () { return toolkits_js_1.MENSAJE_SIN_HERRAMIENTA; } });
|
|
250
|
+
Object.defineProperty(exports, "mensajeDeHerramientaBloqueada", { enumerable: true, get: function () { return toolkits_js_1.mensajeDeHerramientaBloqueada; } });
|
|
251
|
+
Object.defineProperty(exports, "motivoParaNoCorrer", { enumerable: true, get: function () { return toolkits_js_1.motivoParaNoCorrer; } });
|
|
252
|
+
/* Contacts se escribía a mano en la api y en el dashboard; ahora una vez. Se
|
|
253
|
+
llama `…_TOOLKIT_SPECS` porque `GOOGLE_CONTACTS_OPERATION_SPECS` ya es el
|
|
254
|
+
formulario del nodo — la misma pareja que Discord y Slack. */
|
|
255
|
+
var google_contacts_toolkit_js_1 = require("./google-contacts-toolkit.js");
|
|
256
|
+
Object.defineProperty(exports, "GOOGLE_CONTACTS_TOOLKIT_SPECS", { enumerable: true, get: function () { return google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_SPECS; } });
|
|
257
|
+
Object.defineProperty(exports, "GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME", { enumerable: true, get: function () { return google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_BY_TOOL_NAME; } });
|
|
242
258
|
var llm_models_js_1 = require("./llm-models.js");
|
|
243
259
|
Object.defineProperty(exports, "LLM_PROVIDERS", { enumerable: true, get: function () { return llm_models_js_1.LLM_PROVIDERS; } });
|
|
244
260
|
Object.defineProperty(exports, "LLM_MODELS", { enumerable: true, get: function () { return llm_models_js_1.LLM_MODELS; } });
|
package/dist/toolkits.d.ts
CHANGED
|
@@ -14,11 +14,30 @@
|
|
|
14
14
|
*
|
|
15
15
|
* ## Lo que falta aquí, a propósito
|
|
16
16
|
*
|
|
17
|
-
* WhatsApp y
|
|
18
|
-
*
|
|
19
|
-
* registro
|
|
20
|
-
* devuelve `null` para ellos, que
|
|
21
|
-
* destructiva».
|
|
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`.
|
|
22
41
|
*/
|
|
23
42
|
/** Lo mínimo de una herramienta de toolkit que hace falta para decidir. */
|
|
24
43
|
export interface OperacionDeToolkit {
|
|
@@ -45,3 +64,48 @@ export declare function operacionDeToolkit(nodeType: string | undefined | null,
|
|
|
45
64
|
* las herramientas que no son de toolkit (MCP, HTTP…).
|
|
46
65
|
*/
|
|
47
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;
|
package/dist/toolkits.js
CHANGED
|
@@ -15,16 +15,42 @@
|
|
|
15
15
|
*
|
|
16
16
|
* ## Lo que falta aquí, a propósito
|
|
17
17
|
*
|
|
18
|
-
* WhatsApp y
|
|
19
|
-
*
|
|
20
|
-
* registro
|
|
21
|
-
* devuelve `null` para ellos, que
|
|
22
|
-
* destructiva».
|
|
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`.
|
|
23
42
|
*/
|
|
24
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.TOOLKITS_POR_TIPO = void 0;
|
|
44
|
+
exports.MENSAJE_SIN_HERRAMIENTA = exports.TIPOS_CON_TOOLKIT = exports.TOOLKITS_POR_TIPO = void 0;
|
|
26
45
|
exports.operacionDeToolkit = operacionDeToolkit;
|
|
27
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;
|
|
28
54
|
const gmail_operations_js_1 = require("./gmail-operations.js");
|
|
29
55
|
const calendar_toolkit_js_1 = require("./calendar-toolkit.js");
|
|
30
56
|
const drive_toolkit_js_1 = require("./drive-toolkit.js");
|
|
@@ -35,6 +61,7 @@ const sheets_toolkit_js_1 = require("./sheets-toolkit.js");
|
|
|
35
61
|
const docs_toolkit_js_1 = require("./docs-toolkit.js");
|
|
36
62
|
const mongo_toolkit_js_1 = require("./mongo-toolkit.js");
|
|
37
63
|
const postgres_toolkit_js_1 = require("./postgres-toolkit.js");
|
|
64
|
+
const google_contacts_toolkit_js_1 = require("./google-contacts-toolkit.js");
|
|
38
65
|
exports.TOOLKITS_POR_TIPO = Object.freeze({
|
|
39
66
|
gmailAction: gmail_operations_js_1.GMAIL_ALL_TOOLKIT_SPECS,
|
|
40
67
|
emailAction: gmail_operations_js_1.NATIVE_EMAIL_TOOLKIT_SPECS,
|
|
@@ -47,6 +74,7 @@ exports.TOOLKITS_POR_TIPO = Object.freeze({
|
|
|
47
74
|
docsAction: docs_toolkit_js_1.DOCS_TOOLKIT_SPECS,
|
|
48
75
|
mongoAction: mongo_toolkit_js_1.MONGO_TOOLKIT_SPECS,
|
|
49
76
|
postgresAction: postgres_toolkit_js_1.POSTGRES_TOOLKIT_SPECS,
|
|
77
|
+
googleContactsAction: google_contacts_toolkit_js_1.GOOGLE_CONTACTS_TOOLKIT_SPECS,
|
|
50
78
|
});
|
|
51
79
|
/**
|
|
52
80
|
* La operación de un toolkit, buscada por su nombre interno (`updateRow`) O
|
|
@@ -77,3 +105,86 @@ function esOperacionDestructiva(nodeType, operacionONombre) {
|
|
|
77
105
|
const op = operacionDeToolkit(nodeType, operacionONombre);
|
|
78
106
|
return op ? op.destructive === true : null;
|
|
79
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