@saulwade/swl-ses 2.6.1 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/CLAUDE.md +14 -2
  2. package/README.md +65 -18
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/bin/swl-ses.js +10 -0
  6. package/comandos/swl/brainstorm.md +1 -0
  7. package/comandos/swl/briefing.md +119 -119
  8. package/comandos/swl/contribuir.md +233 -233
  9. package/comandos/swl/deuda-codigo.md +97 -97
  10. package/comandos/swl/mcp-status.md +1 -0
  11. package/gateway/lib/event-channel.js +191 -191
  12. package/habilidades/agent-deep-links/SKILL.md +148 -148
  13. package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
  14. package/habilidades/backend-error-design/SKILL.md +221 -221
  15. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  16. package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
  17. package/habilidades/calidad-contract-testing/SKILL.md +165 -165
  18. package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
  19. package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
  20. package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
  21. package/habilidades/css-moderno/SKILL.md +7 -1
  22. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  23. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  24. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
  25. package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
  26. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  27. package/habilidades/harness-claude-code/SKILL.md +3 -2
  28. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  29. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  30. package/habilidades/perfil-usuario/SKILL.md +200 -200
  31. package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
  32. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  33. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  34. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
  35. package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
  36. package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
  37. package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
  38. package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
  39. package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
  40. package/habilidades/proceso-modular-split/SKILL.md +256 -256
  41. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  42. package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
  43. package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
  44. package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
  45. package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
  46. package/hooks/calidad-pre-commit.js +159 -10
  47. package/hooks/ciclo-evolucion-subagente.js +26 -26
  48. package/hooks/ciclo-evolucion.js +26 -26
  49. package/hooks/contexto-subagente.js +68 -68
  50. package/hooks/lib/auto-consolidator.js +335 -335
  51. package/hooks/lib/ciclo-evolucion.js +47 -47
  52. package/hooks/lib/deep-links.js +185 -185
  53. package/hooks/lib/error-classifier.js +308 -308
  54. package/hooks/lib/notificacion-formato.js +45 -11
  55. package/hooks/lib/provenance-tracker.js +191 -191
  56. package/hooks/lib/raiz-proyecto.js +35 -4
  57. package/hooks/lib/resource-quota.js +122 -122
  58. package/hooks/lib/retry-jitter.js +165 -165
  59. package/hooks/lib/security-net.js +201 -201
  60. package/hooks/lib/skill-auditor.js +588 -588
  61. package/hooks/lib/sync-status.js +228 -228
  62. package/hooks/lib/taint-tracker.js +107 -107
  63. package/hooks/lib/text-similarity.js +241 -241
  64. package/hooks/lib/toon-compressor.js +245 -245
  65. package/hooks/notificacion-telegram.js +5 -11
  66. package/hooks/session-briefing.js +12 -4
  67. package/instintos/autonomia.yaml +27 -27
  68. package/instintos/prompt-appendices.yaml +57 -57
  69. package/llms.txt +1 -1
  70. package/manifiestos/agent-output-schemas.json +57 -57
  71. package/manifiestos/canonical-hashes.json +662 -0
  72. package/manifiestos/harness-ir.json +47536 -0
  73. package/manifiestos/hooks-config.json +469 -469
  74. package/manifiestos/invariantes-criticos.json +30 -30
  75. package/manifiestos/policy-bundle.json +2065 -0
  76. package/manifiestos/policy-corpus-w2.json +3926 -0
  77. package/manifiestos/runtime-adapters-core3.json +208 -0
  78. package/manifiestos/runtime-conformance.json +139 -0
  79. package/manifiestos/skills-lock.json +43 -43
  80. package/package.json +2 -2
  81. package/plantillas/auditor-veto-template.md +105 -105
  82. package/plantillas/github-workflows/release-please.yml +44 -44
  83. package/plantillas/github-workflows/swl-ci.yml +107 -107
  84. package/plantillas/github-workflows/swl-security.yml +51 -51
  85. package/plugin.json +2 -2
  86. package/reglas/accesibilidad.md +10 -10
  87. package/reglas/auditorias-documentales-estructurales.md +7 -7
  88. package/reglas/cloud-infra.md +8 -8
  89. package/reglas/consultar-vault-primero.md +195 -195
  90. package/reglas/git-workflow.md +1 -0
  91. package/reglas/hooks.md +6 -6
  92. package/reglas/intent-engineering.md +218 -218
  93. package/reglas/markitdown.md +8 -8
  94. package/reglas/monitor-ci.md +12 -0
  95. package/reglas/patrones.md +6 -6
  96. package/reglas/testing.md +7 -7
  97. package/reglas/tests-cleanup.md +224 -224
  98. package/schemas/agent-message.schema.json +73 -73
  99. package/schemas/agent-output-implementacion.schema.json +114 -114
  100. package/schemas/agent-output-planificacion.schema.json +150 -150
  101. package/schemas/agent-output-review.schema.json +98 -98
  102. package/schemas/diary-entry.schema.json +112 -112
  103. package/schemas/gate-state.schema.json +76 -0
  104. package/schemas/harness-ir.schema.json +369 -0
  105. package/schemas/hook-profiles.schema.json +54 -54
  106. package/schemas/hooks-config.schema.json +89 -89
  107. package/schemas/legacy-gates.schema.json +45 -0
  108. package/schemas/modulos.schema.json +38 -38
  109. package/schemas/perfiles.schema.json +36 -36
  110. package/schemas/plugin.schema.json +77 -77
  111. package/schemas/policy-bundle.schema.json +140 -0
  112. package/schemas/policy-enforcement.schema.json +117 -0
  113. package/schemas/policy-operation.schema.json +261 -0
  114. package/schemas/runtime-adapter.schema.json +176 -0
  115. package/schemas/runtime-build-attestation.schema.json +100 -0
  116. package/schemas/runtime-conformance.schema.json +239 -0
  117. package/schemas/runtime-diagnostic.schema.json +395 -0
  118. package/schemas/skill-evals.schema.json +119 -119
  119. package/schemas/skill-frontmatter.schema.json +245 -245
  120. package/schemas/w4-certification-request.schema.json +72 -0
  121. package/schemas/w4-certification-verdict.schema.json +224 -0
  122. package/schemas/w4-corpus.schema.json +172 -0
  123. package/schemas/w4-mutation-report.schema.json +116 -0
  124. package/schemas/w4-replay-result.schema.json +164 -0
  125. package/schemas/w4-scoring-report.schema.json +89 -0
  126. package/scripts/audit-tools/audit-history.js +330 -330
  127. package/scripts/audit-tools/bundle-tracker.js +290 -290
  128. package/scripts/audit-tools/canary-monitor.js +352 -352
  129. package/scripts/audit-tools/code-profiler.js +605 -605
  130. package/scripts/audit-tools/dep-doctor.js +320 -320
  131. package/scripts/audit-tools/env-validator.js +206 -206
  132. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  133. package/scripts/audit-tools/lib/output.js +23 -23
  134. package/scripts/audit-tools/migration-checker.js +392 -392
  135. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  136. package/scripts/bootstrap-instintos.js +3 -0
  137. package/scripts/cli/aprobar-plan.js +73 -73
  138. package/scripts/cli/briefing.js +23 -23
  139. package/scripts/cli/ciclo-evolucion.js +26 -26
  140. package/scripts/cli/derivar-feature-list.js +25 -25
  141. package/scripts/cli/detectar-host.js +27 -27
  142. package/scripts/cli/diary-entry.js +69 -69
  143. package/scripts/cli/execution-state.js +18 -18
  144. package/scripts/cli/gateway-notify.js +41 -41
  145. package/scripts/cli/liberar-fase.js +42 -42
  146. package/scripts/cli/mark-evolved.js +56 -56
  147. package/scripts/cli/metricas-dora.js +26 -26
  148. package/scripts/cli/near-duplicate.js +55 -55
  149. package/scripts/cli/notificaciones.js +123 -123
  150. package/scripts/cli/propose-step.js +29 -29
  151. package/scripts/cli/schedule-parse.js +19 -19
  152. package/scripts/cli/sugerir-modelo.js +20 -20
  153. package/scripts/cli/verificar-plan.js +36 -36
  154. package/scripts/cli/verificar-trazabilidad.js +35 -35
  155. package/scripts/comandos/install-asistido.js +8 -7
  156. package/scripts/configurar-branch-protection.js +418 -418
  157. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  158. package/scripts/doctor.js +61 -36
  159. package/scripts/generar-checklists-consolidados.js +273 -273
  160. package/scripts/generar-claims-runtime.js +1342 -0
  161. package/scripts/generar-harness-ir.js +257 -0
  162. package/scripts/generar-inventario.js +52 -54
  163. package/scripts/generar-policy-bundle.js +202 -0
  164. package/scripts/instalador.js +26 -7
  165. package/scripts/lib/approval-receipts.js +190 -0
  166. package/scripts/lib/artefactos-python.js +43 -43
  167. package/scripts/lib/benchmark-metrics.js +160 -160
  168. package/scripts/lib/budget-enforcer.js +252 -252
  169. package/scripts/lib/certificacion-loop-state.js +421 -0
  170. package/scripts/lib/ci-reader.js +193 -193
  171. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
  172. package/scripts/lib/clasificar-directorio.js +92 -0
  173. package/scripts/lib/contadores-inventario.js +217 -217
  174. package/scripts/lib/detectar-host-swl.js +175 -175
  175. package/scripts/lib/detectar-runtime.js +29 -20
  176. package/scripts/lib/detectar-stack-detallado.js +307 -307
  177. package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
  178. package/scripts/lib/detector-reglas-duplicadas.js +220 -220
  179. package/scripts/lib/eval-metrics-store.js +218 -218
  180. package/scripts/lib/eval-quality.js +171 -171
  181. package/scripts/lib/eval-schemas.js +144 -144
  182. package/scripts/lib/eval-self-correct.js +106 -106
  183. package/scripts/lib/eval-validator.js +185 -185
  184. package/scripts/lib/evidence-verifier.js +192 -0
  185. package/scripts/lib/evidencia-release.js +322 -322
  186. package/scripts/lib/frontmatter-canonico.js +509 -0
  187. package/scripts/lib/gate-engine.js +871 -0
  188. package/scripts/lib/gate-hooks-requires.js +249 -249
  189. package/scripts/lib/gate-licencias.js +212 -212
  190. package/scripts/lib/git-config-preflight.js +48 -0
  191. package/scripts/lib/git-metricas.js +257 -257
  192. package/scripts/lib/harness-ir.js +778 -0
  193. package/scripts/lib/harness-source-snapshot.js +309 -0
  194. package/scripts/lib/integrity-ledger.js +1147 -0
  195. package/scripts/lib/jaccard-similarity.js +98 -98
  196. package/scripts/lib/legacy-gate-migration.js +324 -0
  197. package/scripts/lib/limpiar-basura-global.js +45 -2
  198. package/scripts/lib/longmemeval-runner.js +125 -125
  199. package/scripts/lib/metricas-dora.js +204 -204
  200. package/scripts/lib/notificaciones-telegram.js +1 -0
  201. package/scripts/lib/npm-version.js +1 -0
  202. package/scripts/lib/paquetes-conocidos.js +50 -50
  203. package/scripts/lib/plan-lock.js +61 -13
  204. package/scripts/lib/policy-broker.js +338 -0
  205. package/scripts/lib/policy-bundle.js +342 -0
  206. package/scripts/lib/policy-context-provider.js +310 -0
  207. package/scripts/lib/policy-contract.js +479 -0
  208. package/scripts/lib/policy-verifier-utils.js +65 -0
  209. package/scripts/lib/pr-analyzer.js +399 -399
  210. package/scripts/lib/principal-verifier.js +178 -0
  211. package/scripts/lib/prompt-builder.js +264 -264
  212. package/scripts/lib/resolver-plan-fase.js +37 -37
  213. package/scripts/lib/rrf-fusion.js +175 -175
  214. package/scripts/lib/runtime-adapter-contract.js +267 -0
  215. package/scripts/lib/runtime-artifact-verifier.js +426 -0
  216. package/scripts/lib/runtime-build-attestation.js +127 -0
  217. package/scripts/lib/runtime-bundle-installer.js +586 -0
  218. package/scripts/lib/runtime-compiler.js +327 -0
  219. package/scripts/lib/runtime-conformance.js +202 -0
  220. package/scripts/lib/runtime-doctor-core3.js +567 -0
  221. package/scripts/lib/runtime-doctor-input.js +59 -0
  222. package/scripts/lib/runtime-operation-adapter.js +267 -0
  223. package/scripts/lib/schema-version.js +164 -164
  224. package/scripts/lib/semantic-search.js +252 -252
  225. package/scripts/lib/signed-envelope.js +545 -0
  226. package/scripts/lib/single-use-store.js +359 -0
  227. package/scripts/lib/skills-externas.js +31 -0
  228. package/scripts/lib/transformadores/codex.js +15 -8
  229. package/scripts/lib/transformadores/gemini.js +79 -5
  230. package/scripts/lib/w4-attestation-adapter.js +158 -0
  231. package/scripts/lib/w4-canario.js +337 -0
  232. package/scripts/lib/w4-claims.js +182 -0
  233. package/scripts/lib/w4-corpus-generador.js +542 -0
  234. package/scripts/lib/w4-gate-c5.js +115 -0
  235. package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
  236. package/scripts/lib/w4-matriz-combos.js +55 -0
  237. package/scripts/lib/w4-motor-mutacion.js +1348 -0
  238. package/scripts/lib/w4-motor-replay.js +735 -0
  239. package/scripts/lib/w4-pin-origen.js +54 -0
  240. package/scripts/lib/w4-publicar-request.js +132 -0
  241. package/scripts/lib/w4-revocacion.js +62 -0
  242. package/scripts/lib/w4-runtimes-core3.js +38 -0
  243. package/scripts/lib/w4-scorer-certificacion.js +692 -0
  244. package/scripts/lib/w4-superficie-candidato.js +49 -0
  245. package/scripts/lib/w4-veredicto.js +452 -0
  246. package/scripts/lib/w4-verificar-veredicto.js +302 -0
  247. package/scripts/limpiar-artefactos-python.js +131 -131
  248. package/scripts/migrar-csv-a-array.js +168 -168
  249. package/scripts/migrar-fase-dominio.js +200 -200
  250. package/scripts/migrar-gates-legacy.js +108 -0
  251. package/scripts/publicar-certification-request.js +115 -0
  252. package/scripts/runtime-doctor.js +107 -0
  253. package/scripts/tui/componentes/selector-multi.js +189 -189
  254. package/scripts/tui/componentes/selector-unico.js +158 -158
  255. package/scripts/tui/ejecutores.js +375 -375
  256. package/scripts/tui/lib/colores.js +129 -129
  257. package/scripts/tui/lib/render.js +264 -264
  258. package/scripts/tui/lib/teclas.js +113 -113
  259. package/scripts/tui/pantallas/install-wizard.js +12 -7
  260. package/scripts/tui/pantallas/menu-principal.js +52 -52
  261. package/scripts/tui/pantallas/progreso.js +274 -274
  262. package/scripts/tui/pantallas/resumen.js +132 -132
  263. package/scripts/validar-userland-vacio.js +110 -110
  264. package/scripts/verificar-aislamiento-swl-eval.js +87 -0
  265. package/scripts/verificar-empaquetado-downstream.js +375 -0
  266. package/scripts/verificar-loop-constructor.js +215 -0
  267. package/scripts/verificar-trazabilidad.js +13 -6
  268. package/scripts/verificar-veredicto-real.js +84 -0
@@ -0,0 +1,359 @@
1
+ 'use strict';
2
+
3
+ /** Fachada single-use compuesta sobre transacciones privadas del ledger. */
4
+
5
+ const crypto = require('node:crypto');
6
+
7
+ const { assertApprovalCurrent, isVerifiedApproval } = require('./approval-receipts');
8
+ const { assertPrincipalCurrent, isVerifiedPrincipal } = require('./principal-verifier');
9
+ const {
10
+ exactKeys, fail, freezeDeep, getContractValidators, plainObject,
11
+ } = require('./policy-verifier-utils');
12
+ const { canonicalizarIJson, validarTimestampIso } = require('./signed-envelope');
13
+
14
+ const GRANT_OWNERS = new WeakMap();
15
+ const CLAIM_RECEIPT_OWNERS = new WeakMap();
16
+ const SHA256 = /^[a-f0-9]{64}$/u;
17
+ const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
18
+ const TASK = /^T-[0-9]{2}$/u;
19
+
20
+ function hitFault(point) {
21
+ if (process.env.SWL_SINGLE_USE_FAULT !== point) return;
22
+ if (process.env.NODE_ENV !== 'test') fail('FAULT_CONFIG_INVALID', 'NODE_ENV inválido');
23
+ if (process.env.SWL_INTEGRITY_TEST_MODE !== '1') {
24
+ fail('FAULT_CONFIG_INVALID', 'modo de integridad inválido');
25
+ }
26
+ fail('FAULT_INJECTED', `falla inyectada: ${point}`);
27
+ }
28
+
29
+ function validatePrincipalOperation(principal, operation) {
30
+ if (!isVerifiedPrincipal(principal)) fail('SINGLE_USE_PRINCIPAL_INVALID', 'principal inválido');
31
+ if (!getContractValidators().isVerifiedOperation(operation)) {
32
+ fail('SINGLE_USE_OPERATION_INVALID', 'operación inválida');
33
+ }
34
+ if (principal.operationDigest !== operation.operationDigest) {
35
+ fail('SINGLE_USE_BINDING_INVALID', 'principal ligado a otra operación');
36
+ }
37
+ }
38
+
39
+ function validateApprovalBinding(approval, principal, operation, authority) {
40
+ if (!isVerifiedApproval(approval)) fail('APPROVAL_INVALID', 'approval no verificada');
41
+ assertPrincipalCurrent(principal, authority);
42
+ assertApprovalCurrent(approval, authority);
43
+ const expected = {
44
+ principalDigest: principal.principalDigest, operationDigest: operation.operationDigest,
45
+ sessionId: principal.sessionId, runtimeId: principal.runtimeId,
46
+ projectId: principal.projectId, worktreeDigest: principal.worktreeDigest,
47
+ capability: operation.capability, effectKind: operation.effectKind,
48
+ scopeDigest: operation.scope.digest, fsmVersion: operation.fsmVersion,
49
+ trustEpoch: authority.trustEpoch, trustDigest: authority.trustDigest,
50
+ };
51
+ if (Object.entries(expected).some(([key, value]) => approval[key] !== value)) {
52
+ fail('APPROVAL_BINDING_INVALID', 'approval prestada o stale');
53
+ }
54
+ validarTimestampIso(approval.expiresAt, 'approval expiresAt');
55
+ }
56
+
57
+ function grantIdFor(approval) {
58
+ return `grant:${approval.approvalDigest}`;
59
+ }
60
+
61
+ function grantClaims(approval, authority) {
62
+ const base = {
63
+ kind: 'authorization-grant', schemaVersion: '1.0.0', grantId: grantIdFor(approval),
64
+ approvalDigest: approval.approvalDigest,
65
+ principalDigest: approval.principalDigest, operationDigest: approval.operationDigest,
66
+ capability: approval.capability, effectKind: approval.effectKind,
67
+ scopeDigest: approval.scopeDigest, planDigest: approval.planDigest,
68
+ fsmVersion: approval.fsmVersion, issuedAt: authority.observedAt,
69
+ expiresAt: approval.expiresAt, trustEpoch: authority.trustEpoch,
70
+ trustDigest: authority.trustDigest, rootKeyDigest: authority.rootKeyDigest,
71
+ status: 'issued',
72
+ };
73
+ let claims = base;
74
+ if (approval.schemaVersion !== '1.0.0') {
75
+ claims = {
76
+ ...base, schemaVersion: approval.schemaVersion,
77
+ phaseId: approval.phaseId, taskId: approval.taskId,
78
+ };
79
+ }
80
+ if (approval.schemaVersion === '1.2.0') {
81
+ claims.g1ApprovalDigest = approval.g1ApprovalDigest;
82
+ }
83
+ return { ...claims, grantDigest: crypto.createHash('sha256')
84
+ .update(canonicalizarIJson(claims), 'utf8').digest('hex') };
85
+ }
86
+
87
+ function policyGrantBase(state, authority, options) {
88
+ const seed = canonicalizarIJson({
89
+ gateAuthorizationDigest: options.gate.gateAuthorizationDigest,
90
+ operationDigest: options.operation.operationDigest,
91
+ sequence: state.sequence + 1,
92
+ });
93
+ const base = {
94
+ kind: 'authorization-grant', schemaVersion: options.gate.g1ApprovalDigest
95
+ ? '1.2.0' : '1.1.0',
96
+ grantId: `grant:${crypto.createHash('sha256').update(seed).digest('hex')}`,
97
+ approvalDigest: options.gate.g1ApprovalDigest
98
+ || options.gate.gateAuthorizationDigest,
99
+ principalDigest: options.principal.principalDigest,
100
+ operationDigest: options.operation.operationDigest,
101
+ capability: options.operation.capability, effectKind: options.operation.effectKind,
102
+ scopeDigest: options.operation.scope.digest, planDigest: options.gate.planDigest,
103
+ fsmVersion: options.operation.fsmVersion, phaseId: options.operation.phaseId,
104
+ taskId: options.operation.taskId,
105
+ issuedAt: authority.observedAt, expiresAt: options.expiresAt,
106
+ trustEpoch: authority.trustEpoch, trustDigest: authority.trustDigest,
107
+ rootKeyDigest: authority.rootKeyDigest, status: 'issued',
108
+ };
109
+ return base;
110
+ }
111
+
112
+ function policyGrantClaims(state, authority, options) {
113
+ validatePrincipalOperation(options.principal, options.operation);
114
+ assertPrincipalCurrent(options.principal, authority);
115
+ validarTimestampIso(options.expiresAt, 'policy grant expiresAt');
116
+ if (options.expiresAt < authority.observedAt) {
117
+ fail('GRANT_EXPIRED', 'policy grant expirada');
118
+ }
119
+ const base = policyGrantBase(state, authority, options);
120
+ if (options.gate.g1ApprovalDigest) {
121
+ base.g1ApprovalDigest = options.gate.g1ApprovalDigest;
122
+ }
123
+ return { ...base, grantDigest: crypto.createHash('sha256')
124
+ .update(canonicalizarIJson(base), 'utf8').digest('hex') };
125
+ }
126
+
127
+ function createGrant(claims, owner) {
128
+ const grant = freezeDeep(structuredClone(claims));
129
+ GRANT_OWNERS.set(grant, owner);
130
+ return grant;
131
+ }
132
+
133
+ function committedEvents(state) {
134
+ return state.events.filter(({ eventType }) => eventType === 'authorization.committed');
135
+ }
136
+
137
+ function claimedEvents(state) {
138
+ return state.events.filter(({ eventType }) => eventType === 'grant.claimed');
139
+ }
140
+
141
+ function assertApprovalUnused(state, approval) {
142
+ const used = committedEvents(state).some(({ payload }) => (
143
+ payload.approvalClaim?.approvalDigest === approval.approvalDigest
144
+ || payload.approvalClaim?.approvalId === approval.approvalId
145
+ || payload.approvalClaim?.nonce === approval.nonce
146
+ ));
147
+ if (used) fail('APPROVAL_ALREADY_USED', 'approval ya reclamada');
148
+ }
149
+
150
+ function validateRedeemOptions(options) {
151
+ plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones redeem');
152
+ exactKeys(options, ['approval', 'principal', 'operation'],
153
+ 'SINGLE_USE_OPTIONS_INVALID', 'opciones redeem');
154
+ validatePrincipalOperation(options.principal, options.operation);
155
+ }
156
+
157
+ function authorizationPayload(approval, grant) {
158
+ return {
159
+ schemaVersion: grant.schemaVersion,
160
+ approvalClaim: {
161
+ approvalId: approval.approvalId, approvalDigest: approval.approvalDigest,
162
+ nonce: approval.nonce, signerPublicKeyDigest: approval.signerPublicKeyDigest,
163
+ },
164
+ decision: { outcome: 'allow', reasonCode: 'APPROVAL_ACCEPTED' },
165
+ grant: structuredClone(grant),
166
+ };
167
+ }
168
+
169
+ function issueApprovalGrant(state, authority, owner, options) {
170
+ validateRedeemOptions(options);
171
+ validateApprovalBinding(options.approval, options.principal, options.operation, authority);
172
+ assertApprovalUnused(state, options.approval);
173
+ const grant = createGrant(grantClaims(options.approval, authority), owner);
174
+ return Object.freeze({
175
+ approvalClaim: Object.freeze({
176
+ approvalId: options.approval.approvalId,
177
+ approvalDigest: options.approval.approvalDigest,
178
+ nonce: options.approval.nonce,
179
+ signerPublicKeyDigest: options.approval.signerPublicKeyDigest,
180
+ }),
181
+ grant,
182
+ });
183
+ }
184
+
185
+ function issuePolicyGrant(state, authority, owner, options) {
186
+ plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones policy grant');
187
+ exactKeys(options, ['expiresAt', 'gate', 'principal', 'operation'],
188
+ 'SINGLE_USE_OPTIONS_INVALID', 'opciones policy grant');
189
+ return createGrant(policyGrantClaims(state, authority, options), owner);
190
+ }
191
+
192
+ function redeemApproval(writer, owner, options) {
193
+ validateRedeemOptions(options);
194
+ let grant;
195
+ writer.transact((state, authority) => {
196
+ validateApprovalBinding(options.approval, options.principal, options.operation, authority);
197
+ assertApprovalUnused(state, options.approval);
198
+ hitFault('after-approval-claim-before-decision');
199
+ grant = createGrant(grantClaims(options.approval, authority), owner);
200
+ hitFault('after-decision-before-grant');
201
+ return {
202
+ expectedSequence: state.sequence, expectedHeadHash: state.headHash,
203
+ eventType: 'authorization.committed',
204
+ payload: authorizationPayload(options.approval, grant),
205
+ };
206
+ });
207
+ return grant;
208
+ }
209
+
210
+ function validateStoredGrant(grant) {
211
+ plainObject(grant, 'GRANT_INVALID', 'grant persistida');
212
+ const baseKeys = [
213
+ 'kind', 'schemaVersion', 'grantId', 'approvalDigest', 'principalDigest',
214
+ 'operationDigest', 'capability', 'effectKind', 'scopeDigest', 'planDigest',
215
+ 'fsmVersion', 'issuedAt', 'expiresAt', 'trustEpoch', 'trustDigest', 'status',
216
+ 'rootKeyDigest', 'grantDigest',
217
+ ];
218
+ const scoped = ['1.1.0', '1.2.0'].includes(grant.schemaVersion);
219
+ const keys = scoped ? [...baseKeys, 'phaseId', 'taskId'] : baseKeys;
220
+ if (grant.schemaVersion === '1.2.0') keys.push('g1ApprovalDigest');
221
+ exactKeys(grant, keys, 'GRANT_INVALID', 'grant persistida');
222
+ const unsigned = { ...grant };
223
+ delete unsigned.grantDigest;
224
+ const digest = crypto.createHash('sha256')
225
+ .update(canonicalizarIJson(unsigned), 'utf8').digest('hex');
226
+ if (grant.kind !== 'authorization-grant'
227
+ || !['1.0.0', '1.1.0', '1.2.0'].includes(grant.schemaVersion)
228
+ || grant.status !== 'issued' || !IDENTIFIER.test(grant.grantId)
229
+ || !SHA256.test(grant.grantDigest) || grant.grantDigest !== digest) {
230
+ fail('GRANT_INVALID', 'grant persistida inválida');
231
+ }
232
+ if (scoped && (!IDENTIFIER.test(grant.phaseId) || !TASK.test(grant.taskId))) {
233
+ fail('GRANT_INVALID', 'alcance de gate persistido inválido');
234
+ }
235
+ if (grant.schemaVersion === '1.2.0' && !SHA256.test(grant.g1ApprovalDigest)) {
236
+ fail('GRANT_INVALID', 'época G1 persistida inválida');
237
+ }
238
+ }
239
+
240
+ function validateGrantBinding(grant, principal, operation, authority) {
241
+ assertPrincipalCurrent(principal, authority);
242
+ const expected = {
243
+ principalDigest: principal.principalDigest, operationDigest: operation.operationDigest,
244
+ capability: operation.capability, effectKind: operation.effectKind,
245
+ scopeDigest: operation.scope.digest, trustEpoch: authority.trustEpoch,
246
+ trustDigest: authority.trustDigest, rootKeyDigest: authority.rootKeyDigest,
247
+ };
248
+ if (Object.entries(expected).some(([key, value]) => grant[key] !== value)) {
249
+ fail('GRANT_BINDING_INVALID', 'grant prestada o stale');
250
+ }
251
+ validarTimestampIso(grant.expiresAt, 'grant expiresAt');
252
+ if (grant.expiresAt < authority.observedAt || principal.expiresAt < authority.observedAt) {
253
+ fail('GRANT_EXPIRED', 'grant o principal expirado');
254
+ }
255
+ }
256
+
257
+ function findGrant(state, grantId) {
258
+ const matches = committedEvents(state).filter(({ payload }) => (
259
+ payload.grant?.grantId === grantId
260
+ ));
261
+ if (matches.length !== 1) fail('GRANT_NOT_FOUND', 'grant ausente o duplicada');
262
+ const grant = matches[0].payload.grant;
263
+ validateStoredGrant(grant);
264
+ return grant;
265
+ }
266
+
267
+ function validateLoadOptions(options) {
268
+ plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones load');
269
+ exactKeys(options, ['grantId', 'principal', 'operation'],
270
+ 'SINGLE_USE_OPTIONS_INVALID', 'opciones load');
271
+ if (!IDENTIFIER.test(options.grantId)) fail('GRANT_INVALID', 'grantId inválido');
272
+ validatePrincipalOperation(options.principal, options.operation);
273
+ }
274
+
275
+ function loadGrant(writer, owner, options) {
276
+ validateLoadOptions(options);
277
+ return writer.inspect((state, authority) => {
278
+ const stored = findGrant(state, options.grantId);
279
+ validateGrantBinding(stored, options.principal, options.operation, authority);
280
+ return createGrant(stored, owner);
281
+ });
282
+ }
283
+
284
+ function validateClaimOptions(options, owner) {
285
+ plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones claim');
286
+ exactKeys(options, ['grant', 'principal', 'operation'],
287
+ 'SINGLE_USE_OPTIONS_INVALID', 'opciones claim');
288
+ if (GRANT_OWNERS.get(options.grant) !== owner) {
289
+ fail('GRANT_INVALID', 'grant no verificada por esta store');
290
+ }
291
+ validatePrincipalOperation(options.principal, options.operation);
292
+ }
293
+
294
+ function claimGrant(writer, owner, options) {
295
+ validateClaimOptions(options, owner);
296
+ let receipt;
297
+ const appendReceipt = writer.transact((state, authority) => {
298
+ const stored = findGrant(state, options.grant.grantId);
299
+ validateGrantBinding(stored, options.principal, options.operation, authority);
300
+ if (stored.grantDigest !== options.grant.grantDigest) {
301
+ fail('GRANT_BINDING_INVALID', 'grant no coincide con autoridad durable');
302
+ }
303
+ if (claimedEvents(state).some(({ payload }) => payload.grantDigest === stored.grantDigest)) {
304
+ fail('GRANT_ALREADY_CLAIMED', 'grant ya reclamada');
305
+ }
306
+ const payload = {
307
+ schemaVersion: stored.schemaVersion, grantId: stored.grantId,
308
+ grantDigest: stored.grantDigest, principalDigest: stored.principalDigest,
309
+ operationDigest: stored.operationDigest, claimedAt: authority.observedAt,
310
+ };
311
+ if (stored.schemaVersion !== '1.0.0') {
312
+ payload.planDigest = stored.planDigest;
313
+ payload.phaseId = stored.phaseId;
314
+ payload.taskId = stored.taskId;
315
+ }
316
+ if (stored.schemaVersion === '1.2.0') {
317
+ payload.g1ApprovalDigest = stored.g1ApprovalDigest;
318
+ }
319
+ return {
320
+ expectedSequence: state.sequence, expectedHeadHash: state.headHash,
321
+ eventType: 'grant.claimed', payload,
322
+ };
323
+ });
324
+ receipt = freezeDeep({
325
+ kind: 'grant-claim-receipt', grantId: options.grant.grantId,
326
+ grantDigest: options.grant.grantDigest,
327
+ planDigest: options.grant.planDigest,
328
+ g1ApprovalDigest: options.grant.g1ApprovalDigest || null,
329
+ phaseId: options.grant.phaseId || null, taskId: options.grant.taskId || null,
330
+ sequence: appendReceipt.sequence, headHash: appendReceipt.headHash,
331
+ });
332
+ CLAIM_RECEIPT_OWNERS.set(receipt, owner);
333
+ return receipt;
334
+ }
335
+
336
+ function createSingleUseStore(writer) {
337
+ const owner = Object.freeze({});
338
+ const publicStore = Object.freeze({
339
+ claimGrant: (options) => claimGrant(writer, owner, options),
340
+ isAuthorizationGrant: (value) => GRANT_OWNERS.get(value) === owner,
341
+ isGrantClaimReceipt: (value) => CLAIM_RECEIPT_OWNERS.get(value) === owner,
342
+ loadGrant: (options) => loadGrant(writer, owner, options),
343
+ });
344
+ const authorizationStore = Object.freeze({
345
+ redeemApproval: (options) => redeemApproval(writer, owner, options),
346
+ });
347
+ const authorizationGrantIssuer = Object.freeze({
348
+ isVerifiedApproval,
349
+ issueApprovalGrant: (state, authority, options) => (
350
+ issueApprovalGrant(state, authority, owner, options)
351
+ ),
352
+ issuePolicyGrant: (state, authority, options) => (
353
+ issuePolicyGrant(state, authority, owner, options)
354
+ ),
355
+ });
356
+ return Object.freeze({ authorizationGrantIssuer, authorizationStore, publicStore });
357
+ }
358
+
359
+ module.exports = { createSingleUseStore };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ /** Catálogo cerrado de referencias opt-in que no viven en habilidades/. */
4
+ const SKILLS_EXTERNAS = Object.freeze([
5
+ 'pdf',
6
+ 'docx',
7
+ 'xlsx',
8
+ 'pptx',
9
+ 'frontend-design',
10
+ 'web-artifacts-builder',
11
+ 'canvas-design',
12
+ 'algorithmic-art',
13
+ 'theme-factory',
14
+ 'slack-gif-creator',
15
+ 'claude-api',
16
+ 'mcp-builder',
17
+ 'webapp-testing',
18
+ 'doc-coauthoring',
19
+ 'internal-comms',
20
+ 'brand-guidelines',
21
+ 'skill-creator',
22
+ ]);
23
+
24
+ const SKILLS_EXTERNAS_SET = new Set(SKILLS_EXTERNAS);
25
+ const CENTINELA_TODOS = 'todos';
26
+
27
+ module.exports = {
28
+ CENTINELA_TODOS,
29
+ SKILLS_EXTERNAS,
30
+ SKILLS_EXTERNAS_SET,
31
+ };
@@ -24,6 +24,8 @@ const path = require('path');
24
24
  const TransformadorBase = require('./base');
25
25
  const { upsertMcpServer } = require('../toml-merge');
26
26
 
27
+ const CONTROLES_TOML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/u;
28
+
27
29
  class TransformadorCodex extends TransformadorBase {
28
30
  /**
29
31
  * Sub-fase 11 v1.5.0: Codex CLI SÍ acepta agentes individuales en formato
@@ -34,7 +36,6 @@ class TransformadorCodex extends TransformadorBase {
34
36
  * - name (required)
35
37
  * - description (required)
36
38
  * - developer_instructions (required) — multi-line literal del cuerpo Markdown
37
- * - model (opcional) — heredado del frontmatter SWL si existe
38
39
  * - sandbox_mode (opcional) — mapeado desde permisosEscritura=false → "read-only"
39
40
  *
40
41
  * El cuerpo del .md SWL completo (después del frontmatter) se inserta como
@@ -64,7 +65,6 @@ class TransformadorCodex extends TransformadorBase {
64
65
  name,
65
66
  description,
66
67
  developer_instructions: developerInstructions,
67
- model: frontmatter.model || null,
68
68
  sandbox_mode: (frontmatter.permisosEscritura === false) ? 'read-only' : null,
69
69
  });
70
70
 
@@ -88,7 +88,6 @@ class TransformadorCodex extends TransformadorBase {
88
88
  lineas.push('');
89
89
  lineas.push(`name = ${this._tomlBasicString(campos.name)}`);
90
90
  lineas.push(`description = ${this._tomlBasicString(campos.description)}`);
91
- if (campos.model) lineas.push(`model = ${this._tomlBasicString(campos.model)}`);
92
91
  if (campos.sandbox_mode) lineas.push(`sandbox_mode = ${this._tomlBasicString(campos.sandbox_mode)}`);
93
92
  lineas.push('');
94
93
  lineas.push(`developer_instructions = ${this._tomlMultiline(campos.developer_instructions)}`);
@@ -96,7 +95,9 @@ class TransformadorCodex extends TransformadorBase {
96
95
  }
97
96
 
98
97
  _tomlBasicString(s) {
99
- const escapado = String(s)
98
+ const texto = String(s);
99
+ if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
100
+ const escapado = texto
100
101
  .replace(/\\/g, '\\\\')
101
102
  .replace(/"/g, '\\"')
102
103
  .replace(/\n/g, '\\n')
@@ -107,6 +108,7 @@ class TransformadorCodex extends TransformadorBase {
107
108
 
108
109
  _tomlMultiline(s) {
109
110
  const contenido = String(s);
111
+ if (CONTROLES_TOML.test(contenido)) throw new TypeError('Control TOML inválido');
110
112
  if (!contenido.includes("'''")) {
111
113
  // Literal multi-line: preserva contenido sin escapes.
112
114
  return `'''\n${contenido}\n'''`;
@@ -122,11 +124,13 @@ class TransformadorCodex extends TransformadorBase {
122
124
  // `.agents/skills/` como ubicaciones válidas — NO `~/.codex/skills/`.
123
125
  // El path `~/.codex/skills/` que apareció en Sub-fase 8 era de una sample
124
126
  // sub-doc del repo (no canónica). Ahora apunta al path correcto vía dirBase.
125
- const dirBaseGlobal = path.join(require('os').homedir(), '.agents');
127
+ const dirBase = metadatos.esGlobal
128
+ ? path.join(require('os').homedir(), '.agents')
129
+ : '.agents';
126
130
  return {
127
131
  tipo: 'directorio',
128
132
  rutaRelativa: `skills/${metadatos.nombreDirectorio}`,
129
- dirBase: dirBaseGlobal,
133
+ dirBase,
130
134
  };
131
135
  }
132
136
 
@@ -178,9 +182,12 @@ class TransformadorCodex extends TransformadorBase {
178
182
 
179
183
  for (const [hookFile, def] of Object.entries(hooksConfig)) {
180
184
  if (hookFilesFiltro && !hookFilesFiltro.has(hookFile)) continue;
181
- if (!def || !Array.isArray(def.events)) continue;
185
+ if (!def) continue;
186
+ const events = Array.isArray(def.events)
187
+ ? def.events
188
+ : (typeof def.event === 'string' ? [def.event] : []);
182
189
 
183
- for (const event of def.events) {
190
+ for (const event of events) {
184
191
  if (!EVENTOS_VALIDOS.has(event)) {
185
192
  descartados.push({ hookFile, event, motivo: 'evento no soportado por Codex' });
186
193
  continue;
@@ -2,6 +2,13 @@
2
2
 
3
3
  const TransformadorBase = require('./base');
4
4
 
5
+ const EVENTOS_GEMINI = Object.freeze({
6
+ PreToolUse: 'BeforeTool',
7
+ PostToolUse: 'AfterTool',
8
+ SessionStart: 'SessionStart',
9
+ });
10
+ const CONTROLES_TOML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/u;
11
+
5
12
  class TransformadorGemini extends TransformadorBase {
6
13
  transformarAgente(contenidoMd, metadatos) {
7
14
  // Gemini soporta frontmatter similar a Claude
@@ -12,7 +19,6 @@ class TransformadorGemini extends TransformadorBase {
12
19
  if (frontmatter.name) fmGemini.name = frontmatter.name;
13
20
  if (frontmatter.description) fmGemini.description = frontmatter.description;
14
21
  if (frontmatter.tools) fmGemini.tools = frontmatter.tools;
15
- if (frontmatter.model) fmGemini.model = frontmatter.model;
16
22
 
17
23
  return {
18
24
  contenido: this.construirMarkdown(fmGemini, cuerpo),
@@ -35,12 +41,36 @@ class TransformadorGemini extends TransformadorBase {
35
41
  }
36
42
 
37
43
  transformarComando(contenidoMd, metadatos) {
44
+ const { frontmatter, cuerpo } = this.parsearFrontmatter(contenidoMd);
45
+ const nombreArchivo = metadatos.nombreArchivo.replace(/\.md$/iu, '.toml');
46
+ const descripcion = this._tomlString(frontmatter.description || '');
47
+ const prompt = this._tomlMultiline(String(cuerpo || '').trim());
38
48
  return {
39
- contenido: contenidoMd,
40
- rutaRelativa: `commands/swl/${metadatos.nombreArchivo}`,
49
+ contenido: `description = ${descripcion}\nprompt = ${prompt}\n`,
50
+ rutaRelativa: `commands/swl/${nombreArchivo}`,
41
51
  };
42
52
  }
43
53
 
54
+ _tomlString(valor) {
55
+ const texto = String(valor);
56
+ if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
57
+ const escapado = texto
58
+ .replace(/\\/gu, '\\\\')
59
+ .replace(/"/gu, '\\"')
60
+ .replace(/\n/gu, '\\n')
61
+ .replace(/\r/gu, '\\r')
62
+ .replace(/\t/gu, '\\t');
63
+ return `"${escapado}"`;
64
+ }
65
+
66
+ _tomlMultiline(valor) {
67
+ const texto = String(valor);
68
+ if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
69
+ const escapado = texto.replace(/\r\n/gu, '\n')
70
+ .replace(/\\/gu, '\\\\').replace(/"""/gu, '\\"""');
71
+ return `"""\n${escapado}\n"""`;
72
+ }
73
+
44
74
  generarArchivoInstrucciones(contexto) {
45
75
  const lineas = [];
46
76
  lineas.push('# GEMINI.md — Sistema SWL');
@@ -86,8 +116,52 @@ class TransformadorGemini extends TransformadorBase {
86
116
  }
87
117
 
88
118
  transformarHooks(hooksConfig, opciones) {
89
- // Gemini usa settings.json con formato similar a Claude
90
- return { formato: 'gemini-settings', config: hooksConfig };
119
+ if (!hooksConfig || typeof hooksConfig !== 'object') return null;
120
+ const opts = opciones || {};
121
+ const filtro = Array.isArray(opts.hookFiles) ? new Set(opts.hookFiles) : null;
122
+ const rutaHooksDir = opts.rutaHooksDir || 'hooks';
123
+ const grupos = {};
124
+ const descartados = [];
125
+
126
+ for (const [hookFile, definicion] of Object.entries(hooksConfig)) {
127
+ const eventos = this._eventosHook(definicion);
128
+ if ((filtro && !filtro.has(hookFile)) || eventos.length === 0) continue;
129
+ for (const evento of eventos) {
130
+ const eventoGemini = EVENTOS_GEMINI[evento];
131
+ if (!eventoGemini) {
132
+ descartados.push({ hookFile, event: evento, motivo: 'evento no soportado por Gemini' });
133
+ continue;
134
+ }
135
+ if (!grupos[eventoGemini]) grupos[eventoGemini] = [];
136
+ const entrada = this._crearHookGemini(hookFile, definicion, rutaHooksDir);
137
+ grupos[eventoGemini].push(entrada);
138
+ }
139
+ }
140
+
141
+ return {
142
+ formato: 'gemini-hooks-json',
143
+ destino: 'hooks/hooks.json',
144
+ config: { hooks: grupos },
145
+ descartados,
146
+ };
147
+ }
148
+
149
+ _eventosHook(definicion) {
150
+ if (Array.isArray(definicion?.events)) return definicion.events;
151
+ return typeof definicion?.event === 'string' ? [definicion.event] : [];
152
+ }
153
+
154
+ _crearHookGemini(hookFile, definicion, rutaHooksDir) {
155
+ const hook = {
156
+ name: hookFile.replace(/\.js$/iu, ''),
157
+ type: 'command',
158
+ command: `node ${rutaHooksDir}/${hookFile}`,
159
+ ...(definicion.timeout ? { timeout: definicion.timeout } : {}),
160
+ };
161
+ return {
162
+ ...(definicion.matcher ? { matcher: definicion.matcher } : {}),
163
+ hooks: [hook],
164
+ };
91
165
  }
92
166
  }
93
167