@saulwade/swl-ses 1.3.7 → 1.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/CLAUDE.md +12 -4
- package/README.md +1 -1
- package/bin/swl-mcp-server.js +187 -187
- package/bin/swl-webhook-server.js +198 -0
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/adoptar-proyecto.md +21 -1
- package/comandos/swl/claudemd.md +14 -1
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/exportar-vault.md +207 -7
- package/comandos/swl/nuevo-proyecto.md +24 -2
- package/gateway/adapters/base.js +109 -0
- package/gateway/adapters/discord.js +167 -0
- package/gateway/adapters/email.js +221 -0
- package/gateway/adapters/slack.js +192 -0
- package/gateway/adapters/telegram.js +183 -0
- package/gateway/adapters/webhook.js +113 -0
- package/gateway/adapters/whatsapp.js +214 -0
- package/gateway/agent-executor.js +322 -0
- package/gateway/command-relay.js +271 -0
- package/gateway/cron/jobs.js +263 -0
- package/gateway/cron/scheduler.js +322 -0
- package/gateway/cron/store.js +335 -0
- package/gateway/index.js +320 -0
- package/gateway/lib/event-channel.js +191 -0
- package/gateway/session.js +131 -0
- package/gateway/webhook-server.js +324 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/build-errors-nextjs/SKILL.md +55 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/extractor-de-aprendizajes/SKILL.md +24 -10
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nextjs-testing/SKILL.md +89 -5
- package/habilidades/node-experto/SKILL.md +37 -1
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/react-experto/SKILL.md +45 -4
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/swl-claudemd/SKILL.md +15 -1
- package/habilidades/tdd-workflow/SKILL.md +36 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/inyeccion-contexto.js +8 -3
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-ip.js +177 -0
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/lib/webhook-dedup.js +184 -0
- package/hooks/lib/webhook-verify.js +123 -0
- package/hooks/proteccion-rutas.js +120 -15
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +1 -0
- package/manifiestos/skills-lock.json +37 -37
- package/package.json +5 -3
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +1 -1
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/usar-context7.md +226 -226
- package/reglas/usar-sistema-swl.md +251 -0
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/comandos/skills.js +251 -2
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-server/README.md +128 -128
- package/scripts/mcp-server/handlers.js +206 -206
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- package/scripts/validar-manifest.js +195 -195
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-release.js +110 -0
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* eval-self-correct.js — Loop de auto-corrección para outputs invalidos.
|
|
5
|
-
*
|
|
6
|
-
* Patrón adoptado de `temp/agentmemory-main/src/eval/self-correct.ts`.
|
|
7
|
-
* Adaptado a swl-ses: en lugar de un MemoryProvider que llama a un LLM, este
|
|
8
|
-
* módulo expone un loop genérico que:
|
|
9
|
-
*
|
|
10
|
-
* 1. Ejecuta una función productora.
|
|
11
|
-
* 2. Valida el output con un validador.
|
|
12
|
-
* 3. Si falla, re-ejecuta con el prompt ampliado con un sufijo más estricto.
|
|
13
|
-
* 4. Repite hasta `maxRetries` o éxito.
|
|
14
|
-
*
|
|
15
|
-
* El productor es agnóstico: puede ser una llamada a Claude (Skill o Bash),
|
|
16
|
-
* o cualquier función async que produzca un output basado en un prompt.
|
|
17
|
-
*
|
|
18
|
-
* No intenta automatizar llamadas LLM — el caller pasa la función concreta.
|
|
19
|
-
* Esto mantiene swl-ses zero-deps.
|
|
20
|
-
*
|
|
21
|
-
* @module scripts/lib/eval-self-correct
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const SUFIJO_ESTRICTO = `
|
|
25
|
-
|
|
26
|
-
IMPORTANTE: Tu respuesta anterior fue inválida. Por favor asegúrate de que tu
|
|
27
|
-
output siga ESTRICTAMENTE el formato requerido. Cada campo obligatorio debe
|
|
28
|
-
estar presente con valores válidos.
|
|
29
|
-
|
|
30
|
-
Errores detectados en el intento anterior:
|
|
31
|
-
{{ERRORS}}
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Ejecuta un loop de auto-corrección sobre una función productora.
|
|
36
|
-
*
|
|
37
|
-
* @param {object} opts
|
|
38
|
-
* @param {function(string, string): Promise<string>} opts.productor - Recibe (sysPrompt, userPrompt) y devuelve output.
|
|
39
|
-
* @param {function(string): {valid: boolean, errors?: string[]}} opts.validador
|
|
40
|
-
* @param {string} opts.sysPrompt
|
|
41
|
-
* @param {string} opts.userPrompt
|
|
42
|
-
* @param {number} [opts.maxRetries=1] - Número máximo de reintentos tras fallo inicial.
|
|
43
|
-
* @returns {Promise<{ output: string, valid: boolean, retried: boolean, intentos: number, errors?: string[] }>}
|
|
44
|
-
*/
|
|
45
|
-
async function compresseConReintento(opts) {
|
|
46
|
-
const { productor, validador, sysPrompt, userPrompt, maxRetries = 1 } = opts;
|
|
47
|
-
|
|
48
|
-
if (typeof productor !== 'function') {
|
|
49
|
-
throw new Error('productor debe ser una función async (sysPrompt, userPrompt) => Promise<string>');
|
|
50
|
-
}
|
|
51
|
-
if (typeof validador !== 'function') {
|
|
52
|
-
throw new Error('validador debe ser una función (output) => {valid, errors?}');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Intento 1
|
|
56
|
-
const primerOutput = await productor(sysPrompt, userPrompt);
|
|
57
|
-
const primerResultado = validador(primerOutput);
|
|
58
|
-
if (primerResultado.valid) {
|
|
59
|
-
return {
|
|
60
|
-
output: primerOutput,
|
|
61
|
-
valid: true,
|
|
62
|
-
retried: false,
|
|
63
|
-
intentos: 1,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let ultimoOutput = primerOutput;
|
|
68
|
-
let ultimosErrores = primerResultado.errors || [];
|
|
69
|
-
|
|
70
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
71
|
-
const erroresFmt = ultimosErrores.length > 0
|
|
72
|
-
? ultimosErrores.map(e => ` - ${e}`).join('\n')
|
|
73
|
-
: ' (sin detalle)';
|
|
74
|
-
const sysAmpliado = sysPrompt + SUFIJO_ESTRICTO.replace('{{ERRORS}}', erroresFmt);
|
|
75
|
-
|
|
76
|
-
const reintentoOutput = await productor(sysAmpliado, userPrompt);
|
|
77
|
-
const reintentoResultado = validador(reintentoOutput);
|
|
78
|
-
|
|
79
|
-
if (reintentoResultado.valid) {
|
|
80
|
-
return {
|
|
81
|
-
output: reintentoOutput,
|
|
82
|
-
valid: true,
|
|
83
|
-
retried: true,
|
|
84
|
-
intentos: i + 2,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
ultimoOutput = reintentoOutput;
|
|
89
|
-
ultimosErrores = reintentoResultado.errors || [];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
output: ultimoOutput,
|
|
94
|
-
valid: false,
|
|
95
|
-
retried: maxRetries > 0,
|
|
96
|
-
intentos: maxRetries + 1,
|
|
97
|
-
errors: ultimosErrores,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// ── exports ───────────────────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
module.exports = {
|
|
104
|
-
compresseConReintento,
|
|
105
|
-
SUFIJO_ESTRICTO,
|
|
106
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* eval-self-correct.js — Loop de auto-corrección para outputs invalidos.
|
|
5
|
+
*
|
|
6
|
+
* Patrón adoptado de `temp/agentmemory-main/src/eval/self-correct.ts`.
|
|
7
|
+
* Adaptado a swl-ses: en lugar de un MemoryProvider que llama a un LLM, este
|
|
8
|
+
* módulo expone un loop genérico que:
|
|
9
|
+
*
|
|
10
|
+
* 1. Ejecuta una función productora.
|
|
11
|
+
* 2. Valida el output con un validador.
|
|
12
|
+
* 3. Si falla, re-ejecuta con el prompt ampliado con un sufijo más estricto.
|
|
13
|
+
* 4. Repite hasta `maxRetries` o éxito.
|
|
14
|
+
*
|
|
15
|
+
* El productor es agnóstico: puede ser una llamada a Claude (Skill o Bash),
|
|
16
|
+
* o cualquier función async que produzca un output basado en un prompt.
|
|
17
|
+
*
|
|
18
|
+
* No intenta automatizar llamadas LLM — el caller pasa la función concreta.
|
|
19
|
+
* Esto mantiene swl-ses zero-deps.
|
|
20
|
+
*
|
|
21
|
+
* @module scripts/lib/eval-self-correct
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const SUFIJO_ESTRICTO = `
|
|
25
|
+
|
|
26
|
+
IMPORTANTE: Tu respuesta anterior fue inválida. Por favor asegúrate de que tu
|
|
27
|
+
output siga ESTRICTAMENTE el formato requerido. Cada campo obligatorio debe
|
|
28
|
+
estar presente con valores válidos.
|
|
29
|
+
|
|
30
|
+
Errores detectados en el intento anterior:
|
|
31
|
+
{{ERRORS}}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ejecuta un loop de auto-corrección sobre una función productora.
|
|
36
|
+
*
|
|
37
|
+
* @param {object} opts
|
|
38
|
+
* @param {function(string, string): Promise<string>} opts.productor - Recibe (sysPrompt, userPrompt) y devuelve output.
|
|
39
|
+
* @param {function(string): {valid: boolean, errors?: string[]}} opts.validador
|
|
40
|
+
* @param {string} opts.sysPrompt
|
|
41
|
+
* @param {string} opts.userPrompt
|
|
42
|
+
* @param {number} [opts.maxRetries=1] - Número máximo de reintentos tras fallo inicial.
|
|
43
|
+
* @returns {Promise<{ output: string, valid: boolean, retried: boolean, intentos: number, errors?: string[] }>}
|
|
44
|
+
*/
|
|
45
|
+
async function compresseConReintento(opts) {
|
|
46
|
+
const { productor, validador, sysPrompt, userPrompt, maxRetries = 1 } = opts;
|
|
47
|
+
|
|
48
|
+
if (typeof productor !== 'function') {
|
|
49
|
+
throw new Error('productor debe ser una función async (sysPrompt, userPrompt) => Promise<string>');
|
|
50
|
+
}
|
|
51
|
+
if (typeof validador !== 'function') {
|
|
52
|
+
throw new Error('validador debe ser una función (output) => {valid, errors?}');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Intento 1
|
|
56
|
+
const primerOutput = await productor(sysPrompt, userPrompt);
|
|
57
|
+
const primerResultado = validador(primerOutput);
|
|
58
|
+
if (primerResultado.valid) {
|
|
59
|
+
return {
|
|
60
|
+
output: primerOutput,
|
|
61
|
+
valid: true,
|
|
62
|
+
retried: false,
|
|
63
|
+
intentos: 1,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let ultimoOutput = primerOutput;
|
|
68
|
+
let ultimosErrores = primerResultado.errors || [];
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
71
|
+
const erroresFmt = ultimosErrores.length > 0
|
|
72
|
+
? ultimosErrores.map(e => ` - ${e}`).join('\n')
|
|
73
|
+
: ' (sin detalle)';
|
|
74
|
+
const sysAmpliado = sysPrompt + SUFIJO_ESTRICTO.replace('{{ERRORS}}', erroresFmt);
|
|
75
|
+
|
|
76
|
+
const reintentoOutput = await productor(sysAmpliado, userPrompt);
|
|
77
|
+
const reintentoResultado = validador(reintentoOutput);
|
|
78
|
+
|
|
79
|
+
if (reintentoResultado.valid) {
|
|
80
|
+
return {
|
|
81
|
+
output: reintentoOutput,
|
|
82
|
+
valid: true,
|
|
83
|
+
retried: true,
|
|
84
|
+
intentos: i + 2,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ultimoOutput = reintentoOutput;
|
|
89
|
+
ultimosErrores = reintentoResultado.errors || [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
output: ultimoOutput,
|
|
94
|
+
valid: false,
|
|
95
|
+
retried: maxRetries > 0,
|
|
96
|
+
intentos: maxRetries + 1,
|
|
97
|
+
errors: ultimosErrores,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── exports ───────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
compresseConReintento,
|
|
105
|
+
SUFIJO_ESTRICTO,
|
|
106
|
+
};
|
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* eval-validator.js — Validador JSON Schema-lite para outputs SWL.
|
|
5
|
-
*
|
|
6
|
-
* Patrón adoptado de `temp/agentmemory-main/src/eval/validator.ts`. Adaptado a
|
|
7
|
-
* swl-ses: sin Zod (mantiene principio zero-deps). Implementación propia que
|
|
8
|
-
* cubre el subset de JSON Schema usado por `eval-schemas.js`:
|
|
9
|
-
* - type: object | array | string | number | integer | boolean
|
|
10
|
-
* - required, properties (object)
|
|
11
|
-
* - items, minItems (array)
|
|
12
|
-
* - minLength, maxLength (string)
|
|
13
|
-
* - minimum, maximum (number)
|
|
14
|
-
* - enum
|
|
15
|
-
*
|
|
16
|
-
* Funciones puras zero-deps. Backward compatible: schemas pueden agregar
|
|
17
|
-
* keywords nuevos en el futuro sin romper validaciones existentes.
|
|
18
|
-
*
|
|
19
|
-
* @module scripts/lib/eval-validator
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
function obtenerTipo(value) {
|
|
25
|
-
if (value === null) return 'null';
|
|
26
|
-
if (Array.isArray(value)) return 'array';
|
|
27
|
-
return typeof value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function esEntero(value) {
|
|
31
|
-
return typeof value === 'number' && Number.isInteger(value);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ── validadores por tipo ──────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
function validarString(value, schema, errors, ruta) {
|
|
37
|
-
if (typeof value !== 'string') {
|
|
38
|
-
errors.push(`${ruta}: esperado string, recibido ${obtenerTipo(value)}`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (typeof schema.minLength === 'number' && value.length < schema.minLength) {
|
|
42
|
-
errors.push(`${ruta}: longitud ${value.length} < minLength ${schema.minLength}`);
|
|
43
|
-
}
|
|
44
|
-
if (typeof schema.maxLength === 'number' && value.length > schema.maxLength) {
|
|
45
|
-
errors.push(`${ruta}: longitud ${value.length} > maxLength ${schema.maxLength}`);
|
|
46
|
-
}
|
|
47
|
-
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
48
|
-
errors.push(`${ruta}: valor "${value}" no en enum [${schema.enum.join(', ')}]`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function validarNumber(value, schema, errors, ruta) {
|
|
53
|
-
if (schema.type === 'integer') {
|
|
54
|
-
if (!esEntero(value)) {
|
|
55
|
-
errors.push(`${ruta}: esperado integer, recibido ${obtenerTipo(value)}`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
} else if (typeof value !== 'number') {
|
|
59
|
-
errors.push(`${ruta}: esperado number, recibido ${obtenerTipo(value)}`);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (typeof schema.minimum === 'number' && value < schema.minimum) {
|
|
63
|
-
errors.push(`${ruta}: valor ${value} < minimum ${schema.minimum}`);
|
|
64
|
-
}
|
|
65
|
-
if (typeof schema.maximum === 'number' && value > schema.maximum) {
|
|
66
|
-
errors.push(`${ruta}: valor ${value} > maximum ${schema.maximum}`);
|
|
67
|
-
}
|
|
68
|
-
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
69
|
-
errors.push(`${ruta}: valor ${value} no en enum`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function validarBoolean(value, schema, errors, ruta) {
|
|
74
|
-
if (typeof value !== 'boolean') {
|
|
75
|
-
errors.push(`${ruta}: esperado boolean, recibido ${obtenerTipo(value)}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function validarArray(value, schema, errors, ruta) {
|
|
80
|
-
if (!Array.isArray(value)) {
|
|
81
|
-
errors.push(`${ruta}: esperado array, recibido ${obtenerTipo(value)}`);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (typeof schema.minItems === 'number' && value.length < schema.minItems) {
|
|
85
|
-
errors.push(`${ruta}: ${value.length} items < minItems ${schema.minItems}`);
|
|
86
|
-
}
|
|
87
|
-
if (typeof schema.maxItems === 'number' && value.length > schema.maxItems) {
|
|
88
|
-
errors.push(`${ruta}: ${value.length} items > maxItems ${schema.maxItems}`);
|
|
89
|
-
}
|
|
90
|
-
if (schema.items) {
|
|
91
|
-
for (let i = 0; i < value.length; i++) {
|
|
92
|
-
validarValor(value[i], schema.items, errors, `${ruta}[${i}]`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function validarObject(value, schema, errors, ruta) {
|
|
98
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
99
|
-
errors.push(`${ruta}: esperado object, recibido ${obtenerTipo(value)}`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
if (Array.isArray(schema.required)) {
|
|
103
|
-
for (const key of schema.required) {
|
|
104
|
-
if (!(key in value)) {
|
|
105
|
-
errors.push(`${ruta}: campo requerido faltante: "${key}"`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (schema.properties && typeof schema.properties === 'object') {
|
|
110
|
-
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
111
|
-
if (key in value) {
|
|
112
|
-
validarValor(value[key], subSchema, errors, `${ruta}.${key}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function validarValor(value, schema, errors, ruta) {
|
|
119
|
-
if (!schema || typeof schema !== 'object') return;
|
|
120
|
-
|
|
121
|
-
switch (schema.type) {
|
|
122
|
-
case 'string': validarString(value, schema, errors, ruta); break;
|
|
123
|
-
case 'number':
|
|
124
|
-
case 'integer': validarNumber(value, schema, errors, ruta); break;
|
|
125
|
-
case 'boolean': validarBoolean(value, schema, errors, ruta); break;
|
|
126
|
-
case 'array': validarArray(value, schema, errors, ruta); break;
|
|
127
|
-
case 'object': validarObject(value, schema, errors, ruta); break;
|
|
128
|
-
default:
|
|
129
|
-
// Sin type explícito: validar enum si aplica, sino aceptar
|
|
130
|
-
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
131
|
-
errors.push(`${ruta}: valor no en enum`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ── API pública ───────────────────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Valida un valor contra un schema JSON-lite.
|
|
140
|
-
* @param {*} value
|
|
141
|
-
* @param {object} schema
|
|
142
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
143
|
-
*/
|
|
144
|
-
function validar(value, schema) {
|
|
145
|
-
const errors = [];
|
|
146
|
-
validarValor(value, schema, errors, '$');
|
|
147
|
-
return { valid: errors.length === 0, errors };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Valida input contra schema, devolviendo formato compatible con eval results.
|
|
152
|
-
* @param {object} schema
|
|
153
|
-
* @param {*} data
|
|
154
|
-
* @param {string} functionId
|
|
155
|
-
* @returns {object} { valid, data?, result? }
|
|
156
|
-
*/
|
|
157
|
-
function validarInput(schema, data, functionId) {
|
|
158
|
-
const result = validar(data, schema);
|
|
159
|
-
if (result.valid) {
|
|
160
|
-
return { valid: true, data };
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
valid: false,
|
|
164
|
-
result: {
|
|
165
|
-
valid: false,
|
|
166
|
-
errors: result.errors,
|
|
167
|
-
qualityScore: 0,
|
|
168
|
-
latencyMs: 0,
|
|
169
|
-
functionId: functionId || 'unknown',
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Conveniencia: valida output (alias de validarInput).
|
|
176
|
-
*/
|
|
177
|
-
const validarOutput = validarInput;
|
|
178
|
-
|
|
179
|
-
// ── exports ───────────────────────────────────────────────────────────────────
|
|
180
|
-
|
|
181
|
-
module.exports = {
|
|
182
|
-
validar,
|
|
183
|
-
validarInput,
|
|
184
|
-
validarOutput,
|
|
185
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* eval-validator.js — Validador JSON Schema-lite para outputs SWL.
|
|
5
|
+
*
|
|
6
|
+
* Patrón adoptado de `temp/agentmemory-main/src/eval/validator.ts`. Adaptado a
|
|
7
|
+
* swl-ses: sin Zod (mantiene principio zero-deps). Implementación propia que
|
|
8
|
+
* cubre el subset de JSON Schema usado por `eval-schemas.js`:
|
|
9
|
+
* - type: object | array | string | number | integer | boolean
|
|
10
|
+
* - required, properties (object)
|
|
11
|
+
* - items, minItems (array)
|
|
12
|
+
* - minLength, maxLength (string)
|
|
13
|
+
* - minimum, maximum (number)
|
|
14
|
+
* - enum
|
|
15
|
+
*
|
|
16
|
+
* Funciones puras zero-deps. Backward compatible: schemas pueden agregar
|
|
17
|
+
* keywords nuevos en el futuro sin romper validaciones existentes.
|
|
18
|
+
*
|
|
19
|
+
* @module scripts/lib/eval-validator
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
function obtenerTipo(value) {
|
|
25
|
+
if (value === null) return 'null';
|
|
26
|
+
if (Array.isArray(value)) return 'array';
|
|
27
|
+
return typeof value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function esEntero(value) {
|
|
31
|
+
return typeof value === 'number' && Number.isInteger(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── validadores por tipo ──────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
function validarString(value, schema, errors, ruta) {
|
|
37
|
+
if (typeof value !== 'string') {
|
|
38
|
+
errors.push(`${ruta}: esperado string, recibido ${obtenerTipo(value)}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (typeof schema.minLength === 'number' && value.length < schema.minLength) {
|
|
42
|
+
errors.push(`${ruta}: longitud ${value.length} < minLength ${schema.minLength}`);
|
|
43
|
+
}
|
|
44
|
+
if (typeof schema.maxLength === 'number' && value.length > schema.maxLength) {
|
|
45
|
+
errors.push(`${ruta}: longitud ${value.length} > maxLength ${schema.maxLength}`);
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
48
|
+
errors.push(`${ruta}: valor "${value}" no en enum [${schema.enum.join(', ')}]`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validarNumber(value, schema, errors, ruta) {
|
|
53
|
+
if (schema.type === 'integer') {
|
|
54
|
+
if (!esEntero(value)) {
|
|
55
|
+
errors.push(`${ruta}: esperado integer, recibido ${obtenerTipo(value)}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
} else if (typeof value !== 'number') {
|
|
59
|
+
errors.push(`${ruta}: esperado number, recibido ${obtenerTipo(value)}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (typeof schema.minimum === 'number' && value < schema.minimum) {
|
|
63
|
+
errors.push(`${ruta}: valor ${value} < minimum ${schema.minimum}`);
|
|
64
|
+
}
|
|
65
|
+
if (typeof schema.maximum === 'number' && value > schema.maximum) {
|
|
66
|
+
errors.push(`${ruta}: valor ${value} > maximum ${schema.maximum}`);
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
69
|
+
errors.push(`${ruta}: valor ${value} no en enum`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function validarBoolean(value, schema, errors, ruta) {
|
|
74
|
+
if (typeof value !== 'boolean') {
|
|
75
|
+
errors.push(`${ruta}: esperado boolean, recibido ${obtenerTipo(value)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validarArray(value, schema, errors, ruta) {
|
|
80
|
+
if (!Array.isArray(value)) {
|
|
81
|
+
errors.push(`${ruta}: esperado array, recibido ${obtenerTipo(value)}`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (typeof schema.minItems === 'number' && value.length < schema.minItems) {
|
|
85
|
+
errors.push(`${ruta}: ${value.length} items < minItems ${schema.minItems}`);
|
|
86
|
+
}
|
|
87
|
+
if (typeof schema.maxItems === 'number' && value.length > schema.maxItems) {
|
|
88
|
+
errors.push(`${ruta}: ${value.length} items > maxItems ${schema.maxItems}`);
|
|
89
|
+
}
|
|
90
|
+
if (schema.items) {
|
|
91
|
+
for (let i = 0; i < value.length; i++) {
|
|
92
|
+
validarValor(value[i], schema.items, errors, `${ruta}[${i}]`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function validarObject(value, schema, errors, ruta) {
|
|
98
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
99
|
+
errors.push(`${ruta}: esperado object, recibido ${obtenerTipo(value)}`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(schema.required)) {
|
|
103
|
+
for (const key of schema.required) {
|
|
104
|
+
if (!(key in value)) {
|
|
105
|
+
errors.push(`${ruta}: campo requerido faltante: "${key}"`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (schema.properties && typeof schema.properties === 'object') {
|
|
110
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
111
|
+
if (key in value) {
|
|
112
|
+
validarValor(value[key], subSchema, errors, `${ruta}.${key}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validarValor(value, schema, errors, ruta) {
|
|
119
|
+
if (!schema || typeof schema !== 'object') return;
|
|
120
|
+
|
|
121
|
+
switch (schema.type) {
|
|
122
|
+
case 'string': validarString(value, schema, errors, ruta); break;
|
|
123
|
+
case 'number':
|
|
124
|
+
case 'integer': validarNumber(value, schema, errors, ruta); break;
|
|
125
|
+
case 'boolean': validarBoolean(value, schema, errors, ruta); break;
|
|
126
|
+
case 'array': validarArray(value, schema, errors, ruta); break;
|
|
127
|
+
case 'object': validarObject(value, schema, errors, ruta); break;
|
|
128
|
+
default:
|
|
129
|
+
// Sin type explícito: validar enum si aplica, sino aceptar
|
|
130
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
131
|
+
errors.push(`${ruta}: valor no en enum`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── API pública ───────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Valida un valor contra un schema JSON-lite.
|
|
140
|
+
* @param {*} value
|
|
141
|
+
* @param {object} schema
|
|
142
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
143
|
+
*/
|
|
144
|
+
function validar(value, schema) {
|
|
145
|
+
const errors = [];
|
|
146
|
+
validarValor(value, schema, errors, '$');
|
|
147
|
+
return { valid: errors.length === 0, errors };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Valida input contra schema, devolviendo formato compatible con eval results.
|
|
152
|
+
* @param {object} schema
|
|
153
|
+
* @param {*} data
|
|
154
|
+
* @param {string} functionId
|
|
155
|
+
* @returns {object} { valid, data?, result? }
|
|
156
|
+
*/
|
|
157
|
+
function validarInput(schema, data, functionId) {
|
|
158
|
+
const result = validar(data, schema);
|
|
159
|
+
if (result.valid) {
|
|
160
|
+
return { valid: true, data };
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
valid: false,
|
|
164
|
+
result: {
|
|
165
|
+
valid: false,
|
|
166
|
+
errors: result.errors,
|
|
167
|
+
qualityScore: 0,
|
|
168
|
+
latencyMs: 0,
|
|
169
|
+
functionId: functionId || 'unknown',
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Conveniencia: valida output (alias de validarInput).
|
|
176
|
+
*/
|
|
177
|
+
const validarOutput = validarInput;
|
|
178
|
+
|
|
179
|
+
// ── exports ───────────────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
validar,
|
|
183
|
+
validarInput,
|
|
184
|
+
validarOutput,
|
|
185
|
+
};
|