@vaia-lab/sdk 0.2.0 → 0.4.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VAIA Lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,225 +1,181 @@
1
1
  # @vaia-lab/sdk
2
2
 
3
- VAIA Platform Integration SDK connect your app, agent, or skill to [Gandia-7](https://gandia7.com) and [Handeia](https://handeia.com).
3
+ **Agents with declared authority.** Connect your app to [Gandia-7](https://gandia7.com) and [Handeia](https://handeia.com), and let an assistant operate inside it — without writing the AI, and without giving it more power than you meant to.
4
4
 
5
- Zero runtime dependencies · Works in Node 18+, Edge, Bun, Deno · TypeScript-first
6
-
7
- ---
8
-
9
- ## Install
5
+ Zero dependencies · Node 18+, Edge, Bun, Deno · TypeScript-first
10
6
 
11
7
  ```bash
12
- npm install @vaia-lab/sdk
8
+ npm i @vaia-lab/sdk
9
+ npx vaia init
13
10
  ```
14
11
 
15
12
  ---
16
13
 
17
- ## Quick start (Next.js — Gandia-7)
14
+ ## The problem it solves
15
+
16
+ Most agent SDKs can say *"the agent may call this function."*
17
+
18
+ None of them can say *"it may pay up to $500 on its own, above that it asks, and it may never delete."*
19
+
20
+ That second sentence is the difference between a demo and something you let run on real data. Here it's mandatory: **a piece without declared authority does not compile.**
18
21
 
19
22
  ```ts
20
- // app/api/gandia/invoke/route.ts
21
- import { gandia, VAIAError } from '@vaia-lab/sdk'
22
-
23
- export const runtime = 'edge'
24
-
25
- export async function POST(req: Request) {
26
- try {
27
- const { ctx } = await gandia.verify(req, process.env.GANDIA_KEY_SECRET!)
28
- gandia.require(ctx, 'read:students')
29
-
30
- const data = await myDb.getStudents(ctx.tenant.id)
31
-
32
- return gandia.respond.surface(ctx.surface, {
33
- card: () => ({ title: 'Alumnos', value: data.length }),
34
- table: () => ({ columns: ['nombre', 'riesgo'], rows: data }),
35
- text: () => `${data.length} alumnos en ${ctx.tenant.name}`,
36
- })
37
- } catch (err) {
38
- if (err instanceof VAIAError) return gandia.respond.error(err.message, err.status)
39
- throw err
40
- }
41
- }
23
+ tools: [{
24
+ name: 'pay_invoice',
25
+ description: 'Pays a pending invoice.',
26
+ permission: 'write:payments',
27
+ authority: {
28
+ level: 'autonoma', // autonoma | requiere_aprobacion | prohibida
29
+ consequence: 'costosa', // reversible | costosa | irreversible
30
+ maxAmount: 500,
31
+ currency: 'MXN',
32
+ rationale: 'Small recurring invoices. Anything larger gets human review.',
33
+ },
34
+ }]
42
35
  ```
43
36
 
44
- ---
37
+ ### Rules you cannot opt out of
45
38
 
46
- ## API Reference
39
+ | rule | why |
40
+ |---|---|
41
+ | **Irreversible is never autonomous** | Doesn't rely on the model behaving well — it relies on the config being **impossible to write** |
42
+ | Autonomous spending needs **a cap and a currency** | A bare `500` means nothing, and guessing the currency is how money disappears |
43
+ | No piece may **exceed its agent's ceiling** | Otherwise you declare a limited agent and slip it a tool that does what the agent can't |
44
+ | A tool **without a permission** is rejected | No permission means no one to ask for consent, and no one to revoke it from |
47
45
 
48
- ### `gandia.verify(request, secret)`
46
+ Validation happens **when you declare**, not in production. A bad contract breaks on your desk, not in front of your user.
47
+
48
+ ---
49
49
 
50
- Verifies the HMAC-SHA256 signature on an incoming invoke call from Gandia-7.
50
+ ## The agent inside your app
51
51
 
52
- - Checks `X-Gandia-Signature`, `X-Gandia-Timestamp` (replay window ±5 min)
53
- - Returns `{ ctx: GandiaContext, raw: string }`
54
- - Throws `VAIAError` (status 401) if invalid
52
+ You declare what you can do. The assistant reasons with that **plus what it knows about the user, which you never see**.
55
53
 
56
54
  ```ts
57
- const { ctx } = await gandia.verify(req, process.env.GANDIA_KEY_SECRET!)
58
- // ctx.tenant.id, ctx.tenant.name, ctx.tenant.sector
59
- // ctx.user.id, ctx.user.role
60
- // ctx.permissions → ['read:students', 'read:grades']
61
- // ctx.surface → 'card' | 'table' | 'text' | 'widget' | 'action' | 'data'
62
- // ctx.query → user's original question (if trigger = 'user_query')
55
+ agent: {
56
+ actions: [
57
+ { name: 'filter_results', description: 'Narrows the visible list.',
58
+ params: [{ name: 'city', type: 'string', description: 'City to filter by.' }] },
59
+ ],
60
+ }
63
61
  ```
64
62
 
65
- ### `gandia.require(ctx, permission)`
66
-
67
- Throws `VAIAError` (403) if the context doesn't have the required permission.
68
-
69
63
  ```ts
70
- gandia.require(ctx, 'write:alerts') // single
71
- gandia.requireAll(ctx, 'read:students', 'read:grades') // all
72
- const ok = gandia.can(ctx, 'read:health') // boolean check
64
+ mountAgent({
65
+ capabilityId: 'com.my-app',
66
+ getContext: () => ({ route: location.pathname, claims: { visible: 12 } }),
67
+ onAction: async (name, args) => run(name, args),
68
+ })
73
69
  ```
74
70
 
75
- ### `gandia.respond.surface(surface, handlers, opts?)`
71
+ **Why the agent doesn't live in your app:** if it brought its own AI, it wouldn't know the user, it would start from zero every session, and — most importantly — **it could never disagree with itself**. This way, if your app scores one option 90 and another 87, the assistant can still recommend the 87, because it knows something about the user your app has no business knowing.
76
72
 
77
- Multi-surface responder GAIA tells you which surface it wants via `ctx.surface`.
73
+ **What it deliberately cannot do:** it may only request actions you declared. It doesn't improvise, doesn't touch the DOM, doesn't find workarounds. Anything that writes gets confirmed with the user first.
78
74
 
79
- ```ts
80
- return gandia.respond.surface(ctx.surface, {
81
- card: () => ({ title: 'Riesgo', value: 72, unit: '%', trend: 'up' }),
82
- table: () => ({ columns: ['alumno', 'score'], rows }),
83
- text: () => `El riesgo promedio es 72%`,
84
- }, { audit: { data_sources: ['supabase:students'], records_accessed: 120 } })
85
- ```
75
+ ---
86
76
 
87
- Available surfaces: `card` · `table` · `text` · `widget` · `action` · `data`
77
+ ## Borrowed connectors
88
78
 
89
- ### `gandia.respond.*` individual builders
79
+ Your app needs the user's GitHub or Drive. **You implement no OAuth, and you never receive their token.**
90
80
 
91
81
  ```ts
92
- gandia.respond.card({ title, value, unit, trend, subtitle, color })
93
- gandia.respond.table({ columns, rows, total })
94
- gandia.respond.text(content, { markdown: true })
95
- gandia.respond.widget({ url, height, width })
96
- gandia.respond.action({ type, label, params, confirm })
97
- gandia.respond.data(payload)
98
- gandia.respond.ok()
99
- gandia.respond.error(message, status, code?)
82
+ agent: { needs: ['github', 'drive'] }
100
83
  ```
101
84
 
102
- All return a `Response` (Web API standard). For Express/Fastify use `gandia.make.*` instead (returns plain JSON object).
103
-
104
- ### `gandia.jwt.verify(token, secret)`
85
+ You request an operation, the platform runs it with the token it already holds, and hands you back a trimmed result.
105
86
 
106
- Verifies a Gandia-7 iframe JWT (`gandia_token` URL param). Use in iframe entry routes to skip your own login.
87
+ > If every app stored tokens, the attack surface would multiply by every developer who ships. One compromised app would hand over the GitHub and Drive of all its users. Borrowed, it only reaches what the user granted — rate-limited, audited, and revocable instantly.
107
88
 
108
- ```ts
109
- // In your iframe entry route:
110
- const claims = await gandia.jwt.fromUrl(request.url, process.env.GANDIA_KEY_SECRET!)
111
- // claims.sub → user_id
112
- // claims.tenant_id → institution id
113
- // claims.email → user email (if available)
114
- // claims.permissions → what the user can do
115
- ```
89
+ Read-only. Writing to an external service is never lent: that's what the service itself is for.
116
90
 
117
91
  ---
118
92
 
119
- ### `handeia.*`
93
+ ## MCP, wrapped
120
94
 
121
- Same API as `gandia.*` but for Handeia (personal platform). No tenant just the user.
95
+ MCP brings the catalog and the transport. This SDK brings the authority layer **MCP cannot express**.
122
96
 
123
97
  ```ts
124
- const { ctx } = await handeia.verify(req, process.env.HANDEIA_KEY_SECRET!)
125
- // ctx.user.id, ctx.user.email
126
- // ctx.permissions, ctx.surface, ctx.query
98
+ const { tool, warnings } = fromMCPTool(mcpTool, authority, 'read:web')
127
99
  ```
128
100
 
101
+ **Importing something from the internet grants it nothing.** Authority is assigned separately, always. The server's own hints only ever produce warnings — *"it flags this as destructive but you declared it reversible"* — never decisions.
102
+
103
+ Outbound, `toMCPTool()` **withholds anything requiring approval**: exposing it would offer an external agent something even you can't run unattended.
104
+
129
105
  ---
130
106
 
131
- ### `defineCapability(config)`
107
+ ## Use it without our platform
132
108
 
133
- Declares your capability metadata in code. The Shazam engine reads this with 100% confidenceno manual confirmation needed in the Developer Portal.
109
+ **Ten of the thirteen exports need no account, no network and no keys.** The authority layer is not tied to Gandia-7 or Handeia it works with any LLM, any framework, any stack.
134
110
 
135
111
  ```ts
136
- // vaia.config.ts
137
- import { defineCapability } from '@vaia-lab/sdk'
138
-
139
- export default defineCapability({
140
- id: 'mx.monitor-riesgo-academico', // reverse-domain
141
- name: 'Monitor de Riesgo Académico',
142
- version: '1.0.0',
143
- target: 'gandia', // 'gandia' | 'handeia' | 'both'
144
- type: 'app', // 'app' | 'ia' | 'skill' | 'eco'
145
- level: 'artefacto', // 'widget' | 'artefacto' | 'espacio'
146
- sector: 'educacion',
147
- surfaces: {
148
- card: { endpoint: '/api/gandia/invoke' },
149
- table: { endpoint: '/api/gandia/invoke' },
150
- text: { endpoint: '/api/gandia/invoke' },
151
- },
152
- permissions: ['read:students', 'read:grades', 'write:alerts'],
153
- risk: 'medium',
154
- })
155
- ```
112
+ import { validatePieces, checkAuthority, fromMCPTool } from '@vaia-lab/sdk'
156
113
 
157
- Generate `gandia.manifest.json`:
114
+ // Validate your agent's tools at build time — with your own runtime
115
+ const errors = validatePieces({ tools: myTools })
116
+ if (errors.length) throw new Error(errors.join('\n'))
158
117
 
159
- ```bash
160
- # Compile vaia.config.ts first, then:
161
- npx vaia-sdk manifest
162
-
163
- # Validate existing manifest:
164
- npx vaia-sdk manifest --validate
118
+ // Gate a call before it happens, wherever your agent runs
119
+ const ok = checkAuthority(tool.authority, { amount: 900, currency: 'MXN' })
120
+ if (!ok.ok) return askHuman(ok.reason)
165
121
  ```
166
122
 
123
+ | works standalone | needs the platform |
124
+ |---|---|
125
+ | `defineCapability` · `toManifest` | `gandia.verify` · `handeia.jwt` |
126
+ | `validatePieces` · `checkAuthority` · `requiresApproval` | `mountAgent` |
127
+ | `validateAgentSurface` · `validateActionCall` | |
128
+ | `fromMCPTool` · `toMCPTool` · `toMCPTools` | |
129
+
130
+ Bring your own model. Bring your own orchestrator. Keep the guardrails.
131
+
167
132
  ---
168
133
 
169
- ## Error handling
134
+ ## Identity, without a second login
170
135
 
171
- All verification functions throw `VAIAError` on failure:
136
+ Your app opens from Gandia-7 or Handeia with the user's session already resolved.
172
137
 
173
138
  ```ts
174
- import { VAIAError } from '@vaia-lab/sdk'
175
-
176
- try {
177
- const { ctx } = await gandia.verify(req, secret)
178
- // ...
179
- } catch (err) {
180
- if (err instanceof VAIAError) {
181
- // err.message → human-readable message (Spanish)
182
- // err.code → machine-readable: 'HMAC_INVALID', 'JWT_EXPIRED', 'PERMISSION_DENIED', etc.
183
- // err.status → HTTP status: 401, 403, 400, 422
184
- return gandia.respond.error(err.message, err.status, err.code)
185
- }
186
- throw err
187
- }
139
+ const claims = await handeia.jwt.fromUrl(request.url, process.env.HANDEIA_KEY_SECRET!)
188
140
  ```
189
141
 
190
- ### Error codes
142
+ And to verify a call genuinely came from the platform:
191
143
 
192
- | Code | Status | When |
193
- |---|---|---|
194
- | `MISSING_AUTH_HEADERS` | 401 | X-Gandia-Signature or X-Gandia-Timestamp missing |
195
- | `TIMESTAMP_OUT_OF_RANGE` | 401 | Timestamp outside ±5 min window |
196
- | `HMAC_INVALID` | 401 | Signature doesn't match |
197
- | `BODY_PARSE_ERROR` | 400 | Body is not valid JSON |
198
- | `JWT_MALFORMED` | 401 | JWT doesn't have 3 parts |
199
- | `JWT_SIGNATURE_INVALID` | 401 | JWT signature check failed |
200
- | `JWT_EXPIRED` | 401 | JWT exp claim is in the past |
201
- | `PERMISSION_DENIED` | 403 | Missing required permission |
202
- | `SURFACE_NOT_SUPPORTED` | 422 | No handler for requested surface |
144
+ ```ts
145
+ const { ctx } = await gandia.verify(request, process.env.GANDIA_KEY_SECRET!)
146
+ gandia.require(ctx, 'read:students')
147
+ ```
148
+
149
+ Constant-time HMAC comparison, ±5 min window, and the health probe **requires a signature too**.
150
+
151
+ ---
152
+
153
+ ## The 7 pieces
154
+
155
+ `skills` · `tools` · `workflows` · `agents` · `personalities` · `modalities` · `permissions`
156
+
157
+ All declared, all validated, all carried in the manifest — so the portal can show **what authority a capability asks for before anyone installs it**.
203
158
 
204
159
  ---
205
160
 
206
161
  ## CLI
207
162
 
208
163
  ```bash
209
- npx vaia-sdk manifest # generate gandia.manifest.json
210
- npx vaia-sdk manifest --validate # validate existing manifest
211
- npx vaia-sdk sign payload.json # sign a payload (needs GANDIA_KEY_SECRET)
212
- npx vaia-sdk version # SDK version
164
+ npx vaia init # drops a ready vaia.config.ts — no account, no keys, no network
165
+ npx vaia manifest # generates the manifest from your config
166
+ npx vaia sign # signs a payload for testing
213
167
  ```
214
168
 
215
169
  ---
216
170
 
217
- ## Publishing
171
+ ## Security
218
172
 
219
- Publication to npm is done manually by the VAIA team via `npm login` + `npm publish`.
173
+ See [SECURITY.md](./SECURITY.md) what the SDK guarantees, and what's on you, such as deduplicating `call_id` if replay within the window matters to you.
220
174
 
221
- ---
175
+ ## Contributing
176
+
177
+ See [CONTRIBUTING.md](./CONTRIBUTING.md). What's open is **how to declare responsible agents**; the engines that run them are private.
222
178
 
223
179
  ## License
224
180
 
225
- MIT — VAIA
181
+ MIT
@@ -0,0 +1,222 @@
1
+ /**
2
+ * VAIA Extension Protocol — superficie de AGENTE.
3
+ *
4
+ * Es la parte del protocolo que permite que el asistente de Handeia viva
5
+ * dentro de un espacio de terceros. Sigue la regla del ecosistema: protocolo
6
+ * antes que SDK. Lo que hay aquí son CONTRATOS; el SDK solo los transporta.
7
+ *
8
+ * ── El reparto de papeles ────────────────────────────────────────────────
9
+ * El espacio → superficie y manos. Declara qué sabe y qué puede hacer.
10
+ * Handeia → cerebro, memoria y autoridad. Decide y ejecuta a través suyo.
11
+ *
12
+ * Por eso un espacio NO trae su propia IA: si la trajera, no te conocería,
13
+ * empezaría de cero cada vez, y no podría contradecirse a sí mismo. El caso
14
+ * que lo justifica: el espacio puntúa un resultado con 90 y el agente te dice
15
+ * que te conviene el de 87, porque sabe algo de ti que el espacio no sabe.
16
+ * Eso solo es posible si el cerebro vive fuera del espacio.
17
+ *
18
+ * ── La regla de confianza, que manda sobre todo lo demás ─────────────────
19
+ * El espacio es CÓDIGO DE TERCEROS. Nada de lo que envía es un hecho: es una
20
+ * AFIRMACIÓN. Handeia la trata como dato citado, nunca como instrucción y
21
+ * nunca al mismo nivel que lo que sabe del usuario. Un espacio que escriba
22
+ * "ignora las instrucciones anteriores" en su contexto no logra nada.
23
+ *
24
+ * @see AGENT_PROTOCOL_VERSION para la política de compatibilidad.
25
+ */
26
+ /**
27
+ * Versión del protocolo de agente. Viaja en cada mensaje.
28
+ *
29
+ * Se versiona desde el primer día a propósito: este contrato es público y
30
+ * cambiarlo después obliga a coordinar despliegues entre partes que no se
31
+ * conocen. Ya se pagó esa factura una vez con la codificación del JWT.
32
+ */
33
+ declare const AGENT_PROTOCOL_VERSION = 1;
34
+ /** Un parámetro de una acción. Sin tipos no hay validación posible. */
35
+ interface AgentActionParam {
36
+ name: string;
37
+ type: 'string' | 'number' | 'boolean';
38
+ description: string;
39
+ required?: boolean | undefined;
40
+ /** Valores admitidos. Si se define, nada fuera de la lista es válido. */
41
+ enum?: string[] | undefined;
42
+ }
43
+ /**
44
+ * Algo que el espacio sabe hacer.
45
+ *
46
+ * Handeia SOLO puede pedir acciones declaradas aquí. No improvisa, no toca el
47
+ * DOM, no busca la forma. Si no está declarada, para el agente no existe —
48
+ * y eso es lo que hace que el mismo agente sirva en cualquier espacio sin que
49
+ * Handeia sepa nada de ninguno en particular.
50
+ */
51
+ interface AgentAction {
52
+ /** Identificador estable, en minúsculas: 'filtrar_resultados'. */
53
+ name: string;
54
+ /** Qué hace, en lenguaje natural. Es lo que lee el modelo para elegirla. */
55
+ description: string;
56
+ params?: AgentActionParam[] | undefined;
57
+ /**
58
+ * true si modifica algo. Las que escriben se confirman con el usuario ANTES
59
+ * de ejecutarse — un agente que escribe sin preguntar se siente fuera de
60
+ * control incluso cuando acierta.
61
+ */
62
+ writes?: boolean | undefined;
63
+ /** Permiso que el usuario debe haber concedido a este espacio. */
64
+ permission?: string | undefined;
65
+ }
66
+ /** Configuración de la superficie de agente dentro de defineCapability. */
67
+ interface AgentSurfaceConfig {
68
+ /** Acciones que el espacio expone. Vacío = el agente solo puede responder. */
69
+ actions?: AgentAction[] | undefined;
70
+ /**
71
+ * Endpoint para preguntarle al espacio cuando el usuario NO está dentro
72
+ * ("¿tengo algo pendiente ahí?"). El círculo solo existe con el espacio
73
+ * abierto; esto es lo que permite que Handeia sea el lugar donde convergen
74
+ * todos tus espacios en vez de uno más al que entrar.
75
+ */
76
+ queryEndpoint?: string | undefined;
77
+ /** Frase de bienvenida propia del espacio. */
78
+ greeting?: string | undefined;
79
+ /**
80
+ * Servicios externos que el espacio necesita consultar. El usuario los
81
+ * concede por espacio y los puede revocar cuando quiera. El espacio jamás
82
+ * recibe el token: pide operaciones, la plataforma las ejecuta.
83
+ */
84
+ needs?: ConnectorNeed[] | undefined;
85
+ }
86
+ /**
87
+ * Servicios externos que un espacio puede necesitar (GitHub, Drive, Calendar…).
88
+ *
89
+ * ── La regla, y no tiene excepciones ─────────────────────────────────────
90
+ * El espacio NUNCA recibe el token del usuario. Declara qué necesita, la
91
+ * plataforma llama al proveedor con el token que YA tiene guardado, y le
92
+ * devuelve solo el resultado.
93
+ *
94
+ * Por qué así y no entregando el token:
95
+ * - Si cada espacio guardara tokens, la superficie de ataque se multiplica
96
+ * por cada desarrollador que publique. Un espacio comprometido entregaría
97
+ * el GitHub y el Drive de todos sus usuarios.
98
+ * - Prestado, un espacio comprometido solo puede pedir las operaciones que
99
+ * el usuario le concedió, con límite de frecuencia, auditadas y
100
+ * revocables al instante desde Conectores.
101
+ *
102
+ * De regalo, publicar un espacio se vuelve barato: el desarrollador no
103
+ * implementa OAuth de nada.
104
+ */
105
+ type ConnectorNeed = 'github' | 'drive' | 'calendar' | 'email' | 'notion' | 'discord';
106
+ /**
107
+ * Operaciones de LECTURA que la plataforma sabe hacer por el espacio.
108
+ *
109
+ * Lista cerrada a propósito: un espacio no puede pedir "haz esta llamada
110
+ * arbitraria a la API de GitHub". Solo puede pedir lo que está aquí, y cada
111
+ * una devuelve datos ya acotados. Escribir en un servicio externo NO se
112
+ * presta — para eso el usuario usa el servicio.
113
+ */
114
+ type ConnectorOperation = 'github.repos' | 'github.issues' | 'drive.files' | 'calendar.events' | 'email.recent' | 'notion.pages';
115
+ /** Lo que el espacio pide prestado. */
116
+ interface ConnectorRequest {
117
+ operation: ConnectorOperation;
118
+ /** Filtros simples. La plataforma los valida; nada de consultas libres. */
119
+ params?: Record<string, string | number | boolean> | undefined;
120
+ }
121
+ /** Lo que la plataforma devuelve. Datos, jamás credenciales. */
122
+ interface ConnectorResult {
123
+ operation: ConnectorOperation;
124
+ ok: boolean;
125
+ items?: Record<string, unknown>[] | undefined;
126
+ /** 'sin_conectar' = el usuario no ha vinculado ese servicio todavía. */
127
+ error?: 'sin_permiso' | 'sin_conectar' | 'no_soportada' | 'limite_excedido' | 'fallo' | undefined;
128
+ }
129
+ /** Qué operación necesita qué conector — la plataforma lo usa para autorizar. */
130
+ declare const CONNECTOR_OF_OPERATION: Record<ConnectorOperation, ConnectorNeed>;
131
+ /**
132
+ * Lo que el espacio dice que está pasando.
133
+ *
134
+ * OJO: se llama `claims` y no `facts` a propósito. Handeia lo etiqueta como
135
+ * afirmación de un tercero antes de dárselo al modelo.
136
+ */
137
+ interface AgentSpaceContext {
138
+ /** Dónde está el usuario dentro del espacio: '/lista'. */
139
+ route?: string | undefined;
140
+ /** Qué está viendo, en lenguaje natural: 'Lista de 12 resultados'. */
141
+ view?: string | undefined;
142
+ /** Datos que el espacio considera relevantes ahora mismo. */
143
+ claims?: Record<string, unknown> | undefined;
144
+ }
145
+ /** Petición del espacio a Handeia. Un solo endpoint, un solo formato. */
146
+ interface AgentTurnRequest {
147
+ protocol: typeof AGENT_PROTOCOL_VERSION;
148
+ /** Lo que escribió el usuario. */
149
+ message: string;
150
+ context?: AgentSpaceContext | undefined;
151
+ /** Acciones disponibles AHORA (pueden ser menos que las declaradas). */
152
+ actions?: AgentAction[] | undefined;
153
+ /** Turnos previos, para que el agente no pierda el hilo. */
154
+ history?: {
155
+ role: 'user' | 'agent';
156
+ text: string;
157
+ }[] | undefined;
158
+ /** Resultado de una acción que Handeia pidió en el turno anterior. */
159
+ actionResult?: AgentActionResult | undefined;
160
+ }
161
+ /** Lo que el espacio devuelve tras ejecutar una acción. */
162
+ interface AgentActionResult {
163
+ action: string;
164
+ ok: boolean;
165
+ /** Qué pasó, para que el agente pueda cerrar el ciclo con el usuario. */
166
+ summary?: string | undefined;
167
+ error?: string | undefined;
168
+ }
169
+ /**
170
+ * De dónde salió lo que el agente afirma.
171
+ *
172
+ * No es adorno: es el pilar de info verificada. Cuando el agente contradice
173
+ * al espacio ("dice 90, pero te conviene la de 87"), tiene que poder decir de
174
+ * dónde sacó su razón. Un oráculo que no se explica no se gana la confianza.
175
+ */
176
+ interface AgentEvidence {
177
+ /** 'handeia' = memoria del usuario · 'space' = lo que declaró el espacio. */
178
+ source: 'handeia' | 'space';
179
+ label: string;
180
+ }
181
+ /** Respuesta de Handeia al espacio. */
182
+ interface AgentTurnResponse {
183
+ protocol: typeof AGENT_PROTOCOL_VERSION;
184
+ /** Qué decirle al usuario. */
185
+ text?: string | undefined;
186
+ /** Acción a ejecutar. Siempre sale de la lista declarada, nunca inventada. */
187
+ action?: {
188
+ name: string;
189
+ args?: Record<string, unknown> | undefined;
190
+ } | undefined;
191
+ /** true si hay que confirmar con el usuario antes de ejecutarla. */
192
+ confirm?: boolean | undefined;
193
+ evidence?: AgentEvidence[] | undefined;
194
+ /** Identificador para cruzar los registros de todas las capas. */
195
+ traceId?: string | undefined;
196
+ }
197
+ /**
198
+ * Revisa que las acciones declaradas sean utilizables.
199
+ *
200
+ * Corre al declarar la capacidad, no en producción: un contrato mal escrito
201
+ * debe reventar en el escritorio del desarrollador, no frente al usuario.
202
+ */
203
+ declare function validateAgentSurface(cfg: AgentSurfaceConfig): string[];
204
+ /**
205
+ * ¿Es válida esta acción contra lo declarado?
206
+ *
207
+ * La usa Handeia antes de reenviarle nada al espacio. Es la lista blanca en
208
+ * ejecución: aunque el modelo se invente una acción o un argumento fuera de
209
+ * rango, aquí se detiene.
210
+ */
211
+ declare function validateActionCall(llamada: {
212
+ name: string;
213
+ args?: Record<string, unknown> | undefined;
214
+ }, declaradas: AgentAction[]): {
215
+ ok: true;
216
+ action: AgentAction;
217
+ } | {
218
+ ok: false;
219
+ reason: string;
220
+ };
221
+
222
+ export { type AgentSurfaceConfig as A, CONNECTOR_OF_OPERATION as C, type AgentAction as a, AGENT_PROTOCOL_VERSION as b, type AgentActionParam as c, type AgentActionResult as d, type AgentEvidence as e, type AgentSpaceContext as f, type AgentTurnRequest as g, type AgentTurnResponse as h, type ConnectorNeed as i, type ConnectorOperation as j, type ConnectorRequest as k, type ConnectorResult as l, validateAgentSurface as m, validateActionCall as v };