@saulwade/swl-ses 1.4.1 → 1.4.2
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 +1 -1
- package/README.md +1 -1
- package/agentes/nemesis-auditor-swl.md +161 -161
- package/bin/swl-mcp-server.js +187 -187
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +122 -122
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- 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/feynman-auditor-swl/SKILL.md +123 -123
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- 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/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/release-semver/.evolved.json +8 -8
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/claudemd-bloat-detector.js +161 -161
- 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-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- 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/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 +11 -6
- package/manifiestos/perfiles.json +2 -1
- package/manifiestos/skills-lock.json +1114 -1114
- package/package.json +1 -1
- 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 +9 -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/schemas/diary-entry.schema.json +80 -80
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- 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/manifiestos.js +42 -1
- 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 +231 -195
- package/scripts/validar-userland-vacio.js +110 -110
package/comandos/swl/nemesis.md
CHANGED
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: swl:nemesis
|
|
3
|
-
description: >
|
|
4
|
-
Auditoría iterativa Feynman + State Inconsistency (loop hasta convergencia).
|
|
5
|
-
Invoca el agente nemesis-auditor-swl con los 2 skills subordinados.
|
|
6
|
-
Persiste hallazgos en .planning/audit/findings/.
|
|
7
|
-
allowed-tools: [Read, Grep, Glob, Bash, Write, Agent]
|
|
8
|
-
argument-hint: "[--pass1 | --pass2 | --continue | --modulo <ruta>]"
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# /swl:nemesis — Auditoría iterativa de profundidad
|
|
12
|
-
|
|
13
|
-
Dos auditores en loop: Feynman interroga suposiciones línea por línea; State
|
|
14
|
-
Inconsistency mapea pares de estado acoplado y encuentra mutaciones
|
|
15
|
-
incompletas. Cada pasada alimenta la siguiente hasta que no aparecen hallazgos
|
|
16
|
-
nuevos o se llega al máximo de 6 pasadas.
|
|
17
|
-
|
|
18
|
-
## Cuándo invocar
|
|
19
|
-
|
|
20
|
-
- El módulo pasó `/swl:revisar` con score ≥ 9.0 pero hay sospechas de bugs
|
|
21
|
-
de lógica profunda que el revisor de código no captura (suposiciones
|
|
22
|
-
incorrectas, invariantes rotos, condiciones de carrera sutiles).
|
|
23
|
-
- Antes de un release de componente crítico: auth, pagos, contratos, permisos.
|
|
24
|
-
- Cuando un bug en producción se atribuye a lógica aparentemente correcta —
|
|
25
|
-
usar Nemesis para encontrar el patrón de fallo subyacente.
|
|
26
|
-
- Para módulos legacy con alta deuda técnica donde los tests no cubren los
|
|
27
|
-
caminos de estado.
|
|
28
|
-
|
|
29
|
-
## Cuándo NO invocar
|
|
30
|
-
|
|
31
|
-
- El módulo tiene menos de 200 LOC y es CRUD simple sin lógica de negocio.
|
|
32
|
-
- Se necesita revisión de estilo, naming o cobertura de tests — usar
|
|
33
|
-
`/swl:revisar` en su lugar.
|
|
34
|
-
- El proyecto no tiene `agentes/nemesis-auditor-swl.md` instalado — verificar
|
|
35
|
-
con `ls agentes/ | grep nemesis` antes de invocar.
|
|
36
|
-
- La tarea urgente es un hotfix en producción — Nemesis es deliberado (8-20
|
|
37
|
-
turnos); en incidente activo, usar `/swl:verificar` acotado.
|
|
38
|
-
|
|
39
|
-
## Modos disponibles
|
|
40
|
-
|
|
41
|
-
| Modo | Flag | Descripción |
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| Loop completo (default) | (sin flag) | Pasada 1 (Feynman) + Pasada 2 (State) + N pasadas alternadas hasta convergencia, máximo 6 en total |
|
|
44
|
-
| Solo Feynman | `--pass1` | Una pasada Feynman exhaustiva; no ejecuta State |
|
|
45
|
-
| Solo State | `--pass2` | Una pasada State Inconsistency; puede alimentarse con findings previos de Feynman si existen en `.planning/audit/findings/` |
|
|
46
|
-
| Retomar | `--continue` | Continúa desde la última pasada registrada en `.planning/audit/findings/`; útil si la sesión se interrumpió |
|
|
47
|
-
| Acotar módulo | `--modulo <ruta>` | Limita el scope a un archivo o subdirectorio específico; se puede combinar con cualquier otro flag |
|
|
48
|
-
|
|
49
|
-
## Qué produce
|
|
50
|
-
|
|
51
|
-
Todos los archivos se crean en `.planning/audit/findings/`:
|
|
52
|
-
|
|
53
|
-
| Archivo | Cuándo se genera |
|
|
54
|
-
|---|---|
|
|
55
|
-
| `feynman-pass[N].md` | Al completar cada pasada Feynman (N = número de pasada) |
|
|
56
|
-
| `state-pass[N].md` | Al completar cada pasada State Inconsistency |
|
|
57
|
-
| `nemesis-verified.md` | Al final del loop completo; consolida todos los hallazgos con estado de verificación |
|
|
58
|
-
|
|
59
|
-
Si el directorio `.planning/audit/findings/` no existe, el agente lo crea antes
|
|
60
|
-
de la primera escritura.
|
|
61
|
-
|
|
62
|
-
## Cómo interpretar el reporte
|
|
63
|
-
|
|
64
|
-
Cada hallazgo en los archivos de pasada usa este formato:
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
## [SEVERIDAD] Título del hallazgo
|
|
68
|
-
|
|
69
|
-
**Discovery path**: Feynman-only | State-only | Cross-feed Pass N → Pass M
|
|
70
|
-
**Verification**: Code trace | PoC test | Both
|
|
71
|
-
**Líneas afectadas**: archivo:L1-L2
|
|
72
|
-
|
|
73
|
-
Descripción del problema y por qué es un bug y no solo código sospechoso.
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Severidades**:
|
|
77
|
-
|
|
78
|
-
| Nivel | Criterio |
|
|
79
|
-
|---|---|
|
|
80
|
-
| CRITICAL | Explotable sin precondiciones; pérdida de datos o control total |
|
|
81
|
-
| HIGH | Explotable bajo condiciones razonables; impacto severo |
|
|
82
|
-
| MEDIUM | Requiere condiciones específicas; impacto moderado |
|
|
83
|
-
| LOW | Impacto menor o difícil de explotar; documentar para trazabilidad |
|
|
84
|
-
|
|
85
|
-
**Discovery path** indica cuándo se encontró:
|
|
86
|
-
|
|
87
|
-
- `Feynman-only`: Feynman lo detectó; State no lo confirmó ni refutó.
|
|
88
|
-
- `State-only`: State Inconsistency lo encontró analizando mutaciones.
|
|
89
|
-
- `Cross-feed Pass N → Pass M`: Feynman marcó un sospechoso en la pasada N;
|
|
90
|
-
State lo confirmó como bug real en la pasada M (o viceversa). Estos
|
|
91
|
-
hallazgos son los más confiables.
|
|
92
|
-
|
|
93
|
-
**Verification** indica la evidencia disponible:
|
|
94
|
-
|
|
95
|
-
- `Code trace`: el path al bug se puede seguir leyendo el código.
|
|
96
|
-
- `PoC test`: hay un caso de prueba que demuestra el comportamiento incorrecto.
|
|
97
|
-
- `Both`: lo más sólido; implica un test listo para agregar a la suite.
|
|
98
|
-
|
|
99
|
-
## Costo estimado
|
|
100
|
-
|
|
101
|
-
| Modo | Turnos aproximados |
|
|
102
|
-
|---|---|
|
|
103
|
-
| Loop completo, módulo pequeño (< 500 LOC) | 8-12 turnos |
|
|
104
|
-
| Loop completo, módulo mediano (500-2000 LOC) | 12-18 turnos |
|
|
105
|
-
| Loop completo, módulo grande (> 2000 LOC) | 18-30 turnos |
|
|
106
|
-
| Solo `--pass1` o `--pass2` | 4-8 turnos |
|
|
107
|
-
|
|
108
|
-
Para módulos mayores a 5000 LOC, usar `--modulo <ruta>` para acotar el scope.
|
|
109
|
-
Un loop sobre un módulo gigante puede tomar 30+ turnos sin garantía de que los
|
|
110
|
-
hallazgos sean más útiles que los de un subconjunto bien elegido.
|
|
111
|
-
|
|
112
|
-
## Ejemplos de invocación
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
/swl:nemesis # loop completo del proyecto
|
|
116
|
-
/swl:nemesis --modulo src/auth # acotado al módulo auth
|
|
117
|
-
/swl:nemesis --pass1 --modulo src/auth # solo Feynman sobre auth
|
|
118
|
-
/swl:nemesis --pass2 # solo State sobre findings existentes
|
|
119
|
-
/swl:nemesis --continue # retoma desde la última pasada guardada
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
<!-- Adaptado de nemesis-auditor-main bajo MIT License (transmissions11/nemesis-auditor) -->
|
|
1
|
+
---
|
|
2
|
+
name: swl:nemesis
|
|
3
|
+
description: >
|
|
4
|
+
Auditoría iterativa Feynman + State Inconsistency (loop hasta convergencia).
|
|
5
|
+
Invoca el agente nemesis-auditor-swl con los 2 skills subordinados.
|
|
6
|
+
Persiste hallazgos en .planning/audit/findings/.
|
|
7
|
+
allowed-tools: [Read, Grep, Glob, Bash, Write, Agent]
|
|
8
|
+
argument-hint: "[--pass1 | --pass2 | --continue | --modulo <ruta>]"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# /swl:nemesis — Auditoría iterativa de profundidad
|
|
12
|
+
|
|
13
|
+
Dos auditores en loop: Feynman interroga suposiciones línea por línea; State
|
|
14
|
+
Inconsistency mapea pares de estado acoplado y encuentra mutaciones
|
|
15
|
+
incompletas. Cada pasada alimenta la siguiente hasta que no aparecen hallazgos
|
|
16
|
+
nuevos o se llega al máximo de 6 pasadas.
|
|
17
|
+
|
|
18
|
+
## Cuándo invocar
|
|
19
|
+
|
|
20
|
+
- El módulo pasó `/swl:revisar` con score ≥ 9.0 pero hay sospechas de bugs
|
|
21
|
+
de lógica profunda que el revisor de código no captura (suposiciones
|
|
22
|
+
incorrectas, invariantes rotos, condiciones de carrera sutiles).
|
|
23
|
+
- Antes de un release de componente crítico: auth, pagos, contratos, permisos.
|
|
24
|
+
- Cuando un bug en producción se atribuye a lógica aparentemente correcta —
|
|
25
|
+
usar Nemesis para encontrar el patrón de fallo subyacente.
|
|
26
|
+
- Para módulos legacy con alta deuda técnica donde los tests no cubren los
|
|
27
|
+
caminos de estado.
|
|
28
|
+
|
|
29
|
+
## Cuándo NO invocar
|
|
30
|
+
|
|
31
|
+
- El módulo tiene menos de 200 LOC y es CRUD simple sin lógica de negocio.
|
|
32
|
+
- Se necesita revisión de estilo, naming o cobertura de tests — usar
|
|
33
|
+
`/swl:revisar` en su lugar.
|
|
34
|
+
- El proyecto no tiene `agentes/nemesis-auditor-swl.md` instalado — verificar
|
|
35
|
+
con `ls agentes/ | grep nemesis` antes de invocar.
|
|
36
|
+
- La tarea urgente es un hotfix en producción — Nemesis es deliberado (8-20
|
|
37
|
+
turnos); en incidente activo, usar `/swl:verificar` acotado.
|
|
38
|
+
|
|
39
|
+
## Modos disponibles
|
|
40
|
+
|
|
41
|
+
| Modo | Flag | Descripción |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Loop completo (default) | (sin flag) | Pasada 1 (Feynman) + Pasada 2 (State) + N pasadas alternadas hasta convergencia, máximo 6 en total |
|
|
44
|
+
| Solo Feynman | `--pass1` | Una pasada Feynman exhaustiva; no ejecuta State |
|
|
45
|
+
| Solo State | `--pass2` | Una pasada State Inconsistency; puede alimentarse con findings previos de Feynman si existen en `.planning/audit/findings/` |
|
|
46
|
+
| Retomar | `--continue` | Continúa desde la última pasada registrada en `.planning/audit/findings/`; útil si la sesión se interrumpió |
|
|
47
|
+
| Acotar módulo | `--modulo <ruta>` | Limita el scope a un archivo o subdirectorio específico; se puede combinar con cualquier otro flag |
|
|
48
|
+
|
|
49
|
+
## Qué produce
|
|
50
|
+
|
|
51
|
+
Todos los archivos se crean en `.planning/audit/findings/`:
|
|
52
|
+
|
|
53
|
+
| Archivo | Cuándo se genera |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `feynman-pass[N].md` | Al completar cada pasada Feynman (N = número de pasada) |
|
|
56
|
+
| `state-pass[N].md` | Al completar cada pasada State Inconsistency |
|
|
57
|
+
| `nemesis-verified.md` | Al final del loop completo; consolida todos los hallazgos con estado de verificación |
|
|
58
|
+
|
|
59
|
+
Si el directorio `.planning/audit/findings/` no existe, el agente lo crea antes
|
|
60
|
+
de la primera escritura.
|
|
61
|
+
|
|
62
|
+
## Cómo interpretar el reporte
|
|
63
|
+
|
|
64
|
+
Cada hallazgo en los archivos de pasada usa este formato:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
## [SEVERIDAD] Título del hallazgo
|
|
68
|
+
|
|
69
|
+
**Discovery path**: Feynman-only | State-only | Cross-feed Pass N → Pass M
|
|
70
|
+
**Verification**: Code trace | PoC test | Both
|
|
71
|
+
**Líneas afectadas**: archivo:L1-L2
|
|
72
|
+
|
|
73
|
+
Descripción del problema y por qué es un bug y no solo código sospechoso.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Severidades**:
|
|
77
|
+
|
|
78
|
+
| Nivel | Criterio |
|
|
79
|
+
|---|---|
|
|
80
|
+
| CRITICAL | Explotable sin precondiciones; pérdida de datos o control total |
|
|
81
|
+
| HIGH | Explotable bajo condiciones razonables; impacto severo |
|
|
82
|
+
| MEDIUM | Requiere condiciones específicas; impacto moderado |
|
|
83
|
+
| LOW | Impacto menor o difícil de explotar; documentar para trazabilidad |
|
|
84
|
+
|
|
85
|
+
**Discovery path** indica cuándo se encontró:
|
|
86
|
+
|
|
87
|
+
- `Feynman-only`: Feynman lo detectó; State no lo confirmó ni refutó.
|
|
88
|
+
- `State-only`: State Inconsistency lo encontró analizando mutaciones.
|
|
89
|
+
- `Cross-feed Pass N → Pass M`: Feynman marcó un sospechoso en la pasada N;
|
|
90
|
+
State lo confirmó como bug real en la pasada M (o viceversa). Estos
|
|
91
|
+
hallazgos son los más confiables.
|
|
92
|
+
|
|
93
|
+
**Verification** indica la evidencia disponible:
|
|
94
|
+
|
|
95
|
+
- `Code trace`: el path al bug se puede seguir leyendo el código.
|
|
96
|
+
- `PoC test`: hay un caso de prueba que demuestra el comportamiento incorrecto.
|
|
97
|
+
- `Both`: lo más sólido; implica un test listo para agregar a la suite.
|
|
98
|
+
|
|
99
|
+
## Costo estimado
|
|
100
|
+
|
|
101
|
+
| Modo | Turnos aproximados |
|
|
102
|
+
|---|---|
|
|
103
|
+
| Loop completo, módulo pequeño (< 500 LOC) | 8-12 turnos |
|
|
104
|
+
| Loop completo, módulo mediano (500-2000 LOC) | 12-18 turnos |
|
|
105
|
+
| Loop completo, módulo grande (> 2000 LOC) | 18-30 turnos |
|
|
106
|
+
| Solo `--pass1` o `--pass2` | 4-8 turnos |
|
|
107
|
+
|
|
108
|
+
Para módulos mayores a 5000 LOC, usar `--modulo <ruta>` para acotar el scope.
|
|
109
|
+
Un loop sobre un módulo gigante puede tomar 30+ turnos sin garantía de que los
|
|
110
|
+
hallazgos sean más útiles que los de un subconjunto bien elegido.
|
|
111
|
+
|
|
112
|
+
## Ejemplos de invocación
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
/swl:nemesis # loop completo del proyecto
|
|
116
|
+
/swl:nemesis --modulo src/auth # acotado al módulo auth
|
|
117
|
+
/swl:nemesis --pass1 --modulo src/auth # solo Feynman sobre auth
|
|
118
|
+
/swl:nemesis --pass2 # solo State sobre findings existentes
|
|
119
|
+
/swl:nemesis --continue # retoma desde la última pasada guardada
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
<!-- Adaptado de nemesis-auditor-main bajo MIT License (transmissions11/nemesis-auditor) -->
|
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* event-channel.js
|
|
5
|
-
*
|
|
6
|
-
* Pub/sub event channel para CommandRelay y otros componentes del gateway.
|
|
7
|
-
*
|
|
8
|
-
* Patrón adaptado de `temp/obsidian-agent-client-master/src/acp/acp-handler.ts`
|
|
9
|
-
* (Set<listeners> con callback unsubscribe). Diferencias:
|
|
10
|
-
* - Zero-deps Node.js (sin RxJS, sin EventEmitter de node — Set nativo).
|
|
11
|
-
* - Backward compat: si no hay listeners, comportamiento idéntico al actual.
|
|
12
|
-
* - Tipos de evento explícitos para evitar typos.
|
|
13
|
-
*
|
|
14
|
-
* Casos de uso en SWL:
|
|
15
|
-
* - CommandRelay emite eventos al recibir/aceptar/rechazar/procesar comandos.
|
|
16
|
-
* - Múltiples adaptadores (Telegram, Discord) pueden suscribirse simultáneamente.
|
|
17
|
-
* - El consumidor /swl:inbox puede mostrar progreso sin polling.
|
|
18
|
-
*
|
|
19
|
-
* Uso:
|
|
20
|
-
* const channel = new EventChannel();
|
|
21
|
-
* const off = channel.on('cmd:queued', (event) => console.log(event));
|
|
22
|
-
* channel.emit({ type: 'cmd:queued', commandId: 'cmd-abc', userId: '123' });
|
|
23
|
-
* off(); // unsubscribe
|
|
24
|
-
*
|
|
25
|
-
* @module gateway/lib/event-channel
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
// ── tipos de evento ──────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Tipos válidos de evento. Se usan strings para serializar fácilmente
|
|
32
|
-
* a JSONL si se requiere persistencia (no obligatorio).
|
|
33
|
-
*/
|
|
34
|
-
const EVENTS = Object.freeze({
|
|
35
|
-
// Lifecycle de comandos en CommandRelay
|
|
36
|
-
CMD_RECEIVED: 'cmd:received', // mensaje llegó al relay
|
|
37
|
-
CMD_REJECTED: 'cmd:rejected', // rechazado por validación (auth/rate/dedup)
|
|
38
|
-
CMD_QUEUED: 'cmd:queued', // encolado en .planning/inbox/
|
|
39
|
-
CMD_PROCESSED: 'cmd:processed', // marcado como procesado por consumidor
|
|
40
|
-
|
|
41
|
-
// Lifecycle de notificaciones
|
|
42
|
-
NOTIFICATION_SENT: 'notification:sent',
|
|
43
|
-
NOTIFICATION_FAILED: 'notification:failed',
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// ── implementación ────────────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Canal de eventos pub/sub con escucha por tipo o wildcard.
|
|
50
|
-
*
|
|
51
|
-
* Mantiene dos estructuras:
|
|
52
|
-
* - listenersByType: Map<eventType, Set<callback>>
|
|
53
|
-
* - wildcardListeners: Set<callback> (escuchan TODOS los eventos)
|
|
54
|
-
*
|
|
55
|
-
* Una excepción en un listener NO interrumpe la propagación a los demás —
|
|
56
|
-
* patrón de aislamiento del mismo origen (acp-handler.ts:47-50).
|
|
57
|
-
*/
|
|
58
|
-
class EventChannel {
|
|
59
|
-
constructor() {
|
|
60
|
-
this.listenersByType = new Map();
|
|
61
|
-
this.wildcardListeners = new Set();
|
|
62
|
-
this.errorHandler = null; // opcional: callback para errores en listeners
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Suscribe un listener a un tipo de evento (o '*' para todos).
|
|
67
|
-
*
|
|
68
|
-
* @param {string} eventType - tipo de evento o '*'
|
|
69
|
-
* @param {function} callback - recibe el objeto event
|
|
70
|
-
* @returns {function} función de unsubscribe (idempotente)
|
|
71
|
-
*/
|
|
72
|
-
on(eventType, callback) {
|
|
73
|
-
if (typeof callback !== 'function') {
|
|
74
|
-
throw new TypeError('callback debe ser función');
|
|
75
|
-
}
|
|
76
|
-
if (typeof eventType !== 'string' || !eventType) {
|
|
77
|
-
throw new TypeError('eventType debe ser string no vacío');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (eventType === '*') {
|
|
81
|
-
this.wildcardListeners.add(callback);
|
|
82
|
-
return () => this.wildcardListeners.delete(callback);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!this.listenersByType.has(eventType)) {
|
|
86
|
-
this.listenersByType.set(eventType, new Set());
|
|
87
|
-
}
|
|
88
|
-
const set = this.listenersByType.get(eventType);
|
|
89
|
-
set.add(callback);
|
|
90
|
-
return () => set.delete(callback);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Suscribe un listener que se invoca UNA sola vez y luego se desuscribe.
|
|
95
|
-
*/
|
|
96
|
-
once(eventType, callback) {
|
|
97
|
-
const off = this.on(eventType, (event) => {
|
|
98
|
-
off();
|
|
99
|
-
callback(event);
|
|
100
|
-
});
|
|
101
|
-
return off;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Emite un evento a todos los listeners suscriptos al tipo + wildcards.
|
|
106
|
-
* El evento debe tener al menos `type`. Se enriquece con `ts` (ISO).
|
|
107
|
-
*
|
|
108
|
-
* @param {object} event - objeto con `type` y datos arbitrarios
|
|
109
|
-
* @returns {number} cantidad de listeners notificados
|
|
110
|
-
*/
|
|
111
|
-
emit(event) {
|
|
112
|
-
if (!event || typeof event !== 'object') {
|
|
113
|
-
throw new TypeError('event debe ser objeto');
|
|
114
|
-
}
|
|
115
|
-
if (typeof event.type !== 'string' || !event.type) {
|
|
116
|
-
throw new TypeError('event.type es obligatorio');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const enriched = { ts: new Date().toISOString(), ...event };
|
|
120
|
-
let notified = 0;
|
|
121
|
-
|
|
122
|
-
// Listeners específicos del tipo
|
|
123
|
-
const typedSet = this.listenersByType.get(enriched.type);
|
|
124
|
-
if (typedSet) {
|
|
125
|
-
for (const cb of typedSet) {
|
|
126
|
-
notified++;
|
|
127
|
-
this._safeInvoke(cb, enriched);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Listeners wildcard
|
|
132
|
-
for (const cb of this.wildcardListeners) {
|
|
133
|
-
notified++;
|
|
134
|
-
this._safeInvoke(cb, enriched);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return notified;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Cuenta de listeners para un tipo (o total si no se pasa tipo).
|
|
142
|
-
*/
|
|
143
|
-
listenerCount(eventType) {
|
|
144
|
-
if (eventType == null) {
|
|
145
|
-
let total = this.wildcardListeners.size;
|
|
146
|
-
for (const set of this.listenersByType.values()) total += set.size;
|
|
147
|
-
return total;
|
|
148
|
-
}
|
|
149
|
-
if (eventType === '*') return this.wildcardListeners.size;
|
|
150
|
-
const set = this.listenersByType.get(eventType);
|
|
151
|
-
return (set ? set.size : 0) + this.wildcardListeners.size;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Limpia todos los listeners. Útil para tests.
|
|
156
|
-
*/
|
|
157
|
-
clear() {
|
|
158
|
-
this.listenersByType.clear();
|
|
159
|
-
this.wildcardListeners.clear();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Asigna un handler para errores en listeners.
|
|
164
|
-
* Si no se asigna, los errores se silencian (no rompen la propagación).
|
|
165
|
-
*/
|
|
166
|
-
onError(handler) {
|
|
167
|
-
if (typeof handler !== 'function') {
|
|
168
|
-
throw new TypeError('handler debe ser función');
|
|
169
|
-
}
|
|
170
|
-
this.errorHandler = handler;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ── internos ────────────────────────────────────────────────────────────────
|
|
174
|
-
|
|
175
|
-
_safeInvoke(cb, event) {
|
|
176
|
-
try {
|
|
177
|
-
cb(event);
|
|
178
|
-
} catch (err) {
|
|
179
|
-
if (this.errorHandler) {
|
|
180
|
-
try { this.errorHandler(err, event); } catch (_) { /* silencioso */ }
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ── exports ───────────────────────────────────────────────────────────────────
|
|
187
|
-
|
|
188
|
-
module.exports = {
|
|
189
|
-
EventChannel,
|
|
190
|
-
EVENTS,
|
|
191
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* event-channel.js
|
|
5
|
+
*
|
|
6
|
+
* Pub/sub event channel para CommandRelay y otros componentes del gateway.
|
|
7
|
+
*
|
|
8
|
+
* Patrón adaptado de `temp/obsidian-agent-client-master/src/acp/acp-handler.ts`
|
|
9
|
+
* (Set<listeners> con callback unsubscribe). Diferencias:
|
|
10
|
+
* - Zero-deps Node.js (sin RxJS, sin EventEmitter de node — Set nativo).
|
|
11
|
+
* - Backward compat: si no hay listeners, comportamiento idéntico al actual.
|
|
12
|
+
* - Tipos de evento explícitos para evitar typos.
|
|
13
|
+
*
|
|
14
|
+
* Casos de uso en SWL:
|
|
15
|
+
* - CommandRelay emite eventos al recibir/aceptar/rechazar/procesar comandos.
|
|
16
|
+
* - Múltiples adaptadores (Telegram, Discord) pueden suscribirse simultáneamente.
|
|
17
|
+
* - El consumidor /swl:inbox puede mostrar progreso sin polling.
|
|
18
|
+
*
|
|
19
|
+
* Uso:
|
|
20
|
+
* const channel = new EventChannel();
|
|
21
|
+
* const off = channel.on('cmd:queued', (event) => console.log(event));
|
|
22
|
+
* channel.emit({ type: 'cmd:queued', commandId: 'cmd-abc', userId: '123' });
|
|
23
|
+
* off(); // unsubscribe
|
|
24
|
+
*
|
|
25
|
+
* @module gateway/lib/event-channel
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── tipos de evento ──────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Tipos válidos de evento. Se usan strings para serializar fácilmente
|
|
32
|
+
* a JSONL si se requiere persistencia (no obligatorio).
|
|
33
|
+
*/
|
|
34
|
+
const EVENTS = Object.freeze({
|
|
35
|
+
// Lifecycle de comandos en CommandRelay
|
|
36
|
+
CMD_RECEIVED: 'cmd:received', // mensaje llegó al relay
|
|
37
|
+
CMD_REJECTED: 'cmd:rejected', // rechazado por validación (auth/rate/dedup)
|
|
38
|
+
CMD_QUEUED: 'cmd:queued', // encolado en .planning/inbox/
|
|
39
|
+
CMD_PROCESSED: 'cmd:processed', // marcado como procesado por consumidor
|
|
40
|
+
|
|
41
|
+
// Lifecycle de notificaciones
|
|
42
|
+
NOTIFICATION_SENT: 'notification:sent',
|
|
43
|
+
NOTIFICATION_FAILED: 'notification:failed',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ── implementación ────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Canal de eventos pub/sub con escucha por tipo o wildcard.
|
|
50
|
+
*
|
|
51
|
+
* Mantiene dos estructuras:
|
|
52
|
+
* - listenersByType: Map<eventType, Set<callback>>
|
|
53
|
+
* - wildcardListeners: Set<callback> (escuchan TODOS los eventos)
|
|
54
|
+
*
|
|
55
|
+
* Una excepción en un listener NO interrumpe la propagación a los demás —
|
|
56
|
+
* patrón de aislamiento del mismo origen (acp-handler.ts:47-50).
|
|
57
|
+
*/
|
|
58
|
+
class EventChannel {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.listenersByType = new Map();
|
|
61
|
+
this.wildcardListeners = new Set();
|
|
62
|
+
this.errorHandler = null; // opcional: callback para errores en listeners
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Suscribe un listener a un tipo de evento (o '*' para todos).
|
|
67
|
+
*
|
|
68
|
+
* @param {string} eventType - tipo de evento o '*'
|
|
69
|
+
* @param {function} callback - recibe el objeto event
|
|
70
|
+
* @returns {function} función de unsubscribe (idempotente)
|
|
71
|
+
*/
|
|
72
|
+
on(eventType, callback) {
|
|
73
|
+
if (typeof callback !== 'function') {
|
|
74
|
+
throw new TypeError('callback debe ser función');
|
|
75
|
+
}
|
|
76
|
+
if (typeof eventType !== 'string' || !eventType) {
|
|
77
|
+
throw new TypeError('eventType debe ser string no vacío');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (eventType === '*') {
|
|
81
|
+
this.wildcardListeners.add(callback);
|
|
82
|
+
return () => this.wildcardListeners.delete(callback);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!this.listenersByType.has(eventType)) {
|
|
86
|
+
this.listenersByType.set(eventType, new Set());
|
|
87
|
+
}
|
|
88
|
+
const set = this.listenersByType.get(eventType);
|
|
89
|
+
set.add(callback);
|
|
90
|
+
return () => set.delete(callback);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Suscribe un listener que se invoca UNA sola vez y luego se desuscribe.
|
|
95
|
+
*/
|
|
96
|
+
once(eventType, callback) {
|
|
97
|
+
const off = this.on(eventType, (event) => {
|
|
98
|
+
off();
|
|
99
|
+
callback(event);
|
|
100
|
+
});
|
|
101
|
+
return off;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Emite un evento a todos los listeners suscriptos al tipo + wildcards.
|
|
106
|
+
* El evento debe tener al menos `type`. Se enriquece con `ts` (ISO).
|
|
107
|
+
*
|
|
108
|
+
* @param {object} event - objeto con `type` y datos arbitrarios
|
|
109
|
+
* @returns {number} cantidad de listeners notificados
|
|
110
|
+
*/
|
|
111
|
+
emit(event) {
|
|
112
|
+
if (!event || typeof event !== 'object') {
|
|
113
|
+
throw new TypeError('event debe ser objeto');
|
|
114
|
+
}
|
|
115
|
+
if (typeof event.type !== 'string' || !event.type) {
|
|
116
|
+
throw new TypeError('event.type es obligatorio');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const enriched = { ts: new Date().toISOString(), ...event };
|
|
120
|
+
let notified = 0;
|
|
121
|
+
|
|
122
|
+
// Listeners específicos del tipo
|
|
123
|
+
const typedSet = this.listenersByType.get(enriched.type);
|
|
124
|
+
if (typedSet) {
|
|
125
|
+
for (const cb of typedSet) {
|
|
126
|
+
notified++;
|
|
127
|
+
this._safeInvoke(cb, enriched);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Listeners wildcard
|
|
132
|
+
for (const cb of this.wildcardListeners) {
|
|
133
|
+
notified++;
|
|
134
|
+
this._safeInvoke(cb, enriched);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return notified;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Cuenta de listeners para un tipo (o total si no se pasa tipo).
|
|
142
|
+
*/
|
|
143
|
+
listenerCount(eventType) {
|
|
144
|
+
if (eventType == null) {
|
|
145
|
+
let total = this.wildcardListeners.size;
|
|
146
|
+
for (const set of this.listenersByType.values()) total += set.size;
|
|
147
|
+
return total;
|
|
148
|
+
}
|
|
149
|
+
if (eventType === '*') return this.wildcardListeners.size;
|
|
150
|
+
const set = this.listenersByType.get(eventType);
|
|
151
|
+
return (set ? set.size : 0) + this.wildcardListeners.size;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Limpia todos los listeners. Útil para tests.
|
|
156
|
+
*/
|
|
157
|
+
clear() {
|
|
158
|
+
this.listenersByType.clear();
|
|
159
|
+
this.wildcardListeners.clear();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Asigna un handler para errores en listeners.
|
|
164
|
+
* Si no se asigna, los errores se silencian (no rompen la propagación).
|
|
165
|
+
*/
|
|
166
|
+
onError(handler) {
|
|
167
|
+
if (typeof handler !== 'function') {
|
|
168
|
+
throw new TypeError('handler debe ser función');
|
|
169
|
+
}
|
|
170
|
+
this.errorHandler = handler;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── internos ────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
_safeInvoke(cb, event) {
|
|
176
|
+
try {
|
|
177
|
+
cb(event);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if (this.errorHandler) {
|
|
180
|
+
try { this.errorHandler(err, event); } catch (_) { /* silencioso */ }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── exports ───────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
module.exports = {
|
|
189
|
+
EventChannel,
|
|
190
|
+
EVENTS,
|
|
191
|
+
};
|