@purecore/one-jwt-4-all 1.2.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.
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Self-Healing Agentic Conversational System
3
+ *
4
+ * Sistema onde dois agentes se identificam usando JWTs do mesmo servidor
5
+ * e regeneram automaticamente seus tokens quando expiram, mantendo a
6
+ * conversa contínua sem interrupção.
7
+ */
8
+
9
+ import { SignJWT, jwtVerify, generateKeyPair } from '../src/index';
10
+ import * as crypto from 'node:crypto';
11
+
12
+ // ============================================================================
13
+ // Configuração do Servidor de Autoridade (Token Issuer)
14
+ // ============================================================================
15
+
16
+ interface AgentIdentity {
17
+ agentId: string;
18
+ agentType: 'primary' | 'secondary';
19
+ capabilities: string[];
20
+ }
21
+
22
+ class TokenAuthority {
23
+ private privateKey: crypto.KeyObject;
24
+ public publicKey: crypto.KeyObject;
25
+ private issuer = 'urn:agentic-system:authority';
26
+ private audience = 'urn:agentic-system:agents';
27
+
28
+ constructor() {
29
+ const keys = generateKeyPair();
30
+ this.privateKey = crypto.createPrivateKey(keys.privateKey);
31
+ this.publicKey = crypto.createPublicKey(keys.publicKey);
32
+ }
33
+
34
+ /**
35
+ * Emite um token para um agente com contexto de conversa
36
+ */
37
+ async issueAgentToken(
38
+ agentId: string,
39
+ agentType: 'primary' | 'secondary',
40
+ conversationId: string,
41
+ capabilities: string[] = []
42
+ ): Promise<string> {
43
+ return await new SignJWT({
44
+ agentId,
45
+ agentType,
46
+ conversationId,
47
+ capabilities,
48
+ issuedAt: Date.now()
49
+ })
50
+ .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT' })
51
+ .setIssuedAt()
52
+ .setIssuer(this.issuer)
53
+ .setAudience(this.audience)
54
+ .setSubject(agentId)
55
+ .setExpirationTime('5m') // Tokens curtos para segurança
56
+ .sign(this.privateKey);
57
+ }
58
+
59
+ /**
60
+ * Verifica e renova um token mantendo o contexto da conversa
61
+ */
62
+ async renewToken(oldToken: string): Promise<string> {
63
+ try {
64
+ // Verifica o token antigo (pode estar expirado, mas ainda válido para renovação)
65
+ const { payload } = await jwtVerify(oldToken, this.publicKey, {
66
+ issuer: this.issuer,
67
+ audience: this.audience,
68
+ // Não validamos exp aqui para permitir renovação de tokens expirados
69
+ }).catch(() => {
70
+ // Se falhar, tenta decodificar sem verificar assinatura para extrair contexto
71
+ const parts = oldToken.split('.');
72
+ if (parts.length !== 3) throw new Error('Token inválido');
73
+ const decoded = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
74
+ return { payload: decoded };
75
+ });
76
+
77
+ // Renova mantendo o contexto da conversa
78
+ return await this.issueAgentToken(
79
+ payload.agentId as string,
80
+ payload.agentType as 'primary' | 'secondary',
81
+ payload.conversationId as string,
82
+ payload.capabilities as string[]
83
+ );
84
+ } catch (error) {
85
+ throw new Error(`Falha ao renovar token: ${error}`);
86
+ }
87
+ }
88
+ }
89
+
90
+ // ============================================================================
91
+ // Classe Base para Agentes Auto-Recuperáveis
92
+ // ============================================================================
93
+
94
+ interface AgentMessage {
95
+ from: string;
96
+ to: string;
97
+ content: string;
98
+ timestamp: number;
99
+ messageId: string;
100
+ }
101
+
102
+ class SelfHealingAgent {
103
+ private agentId: string;
104
+ private agentType: 'primary' | 'secondary';
105
+ private conversationId: string;
106
+ private capabilities: string[];
107
+ private token: string | null = null;
108
+ private tokenExpiry: number = 0;
109
+ private renewalThreshold: number = 60; // Renovar 60s antes de expirar
110
+ private authority: TokenAuthority;
111
+ private messageHistory: AgentMessage[] = [];
112
+
113
+ constructor(
114
+ agentId: string,
115
+ agentType: 'primary' | 'secondary',
116
+ authority: TokenAuthority,
117
+ capabilities: string[] = []
118
+ ) {
119
+ this.agentId = agentId;
120
+ this.agentType = agentType;
121
+ this.authority = authority;
122
+ this.capabilities = capabilities;
123
+ this.conversationId = `conv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
124
+ }
125
+
126
+ /**
127
+ * Inicializa o agente obtendo seu primeiro token
128
+ */
129
+ async initialize(): Promise<void> {
130
+ this.token = await this.authority.issueAgentToken(
131
+ this.agentId,
132
+ this.agentType,
133
+ this.conversationId,
134
+ this.capabilities
135
+ );
136
+ this.tokenExpiry = Date.now() + (5 * 60 * 1000); // 5 minutos
137
+ console.log(`🤖 [${this.agentId}] Agente inicializado com token válido até ${new Date(this.tokenExpiry).toISOString()}`);
138
+ }
139
+
140
+ /**
141
+ * Verifica e renova o token se necessário (self-healing)
142
+ */
143
+ private async ensureValidToken(): Promise<void> {
144
+ const now = Date.now();
145
+ const timeUntilExpiry = this.tokenExpiry - now;
146
+
147
+ // Se o token está próximo de expirar ou já expirou, renova
148
+ if (!this.token || timeUntilExpiry < this.renewalThreshold * 1000) {
149
+ if (this.token) {
150
+ console.log(`🔄 [${this.agentId}] Token próximo de expirar, renovando...`);
151
+ } else {
152
+ console.log(`🔄 [${this.agentId}] Gerando novo token...`);
153
+ }
154
+
155
+ try {
156
+ if (this.token) {
157
+ // Tenta renovar mantendo contexto
158
+ this.token = await this.authority.renewToken(this.token);
159
+ } else {
160
+ // Primeira inicialização
161
+ await this.initialize();
162
+ return;
163
+ }
164
+
165
+ this.tokenExpiry = Date.now() + (5 * 60 * 1000);
166
+ console.log(`✅ [${this.agentId}] Token renovado com sucesso. Válido até ${new Date(this.tokenExpiry).toISOString()}`);
167
+ } catch (error) {
168
+ console.error(`❌ [${this.agentId}] Erro ao renovar token:`, error);
169
+ // Fallback: reinicializa completamente
170
+ await this.initialize();
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Verifica a identidade de outro agente
177
+ */
178
+ async verifyPeerIdentity(peerToken: string): Promise<{ agentId: string; conversationId: string; valid: boolean }> {
179
+ try {
180
+ const { payload } = await jwtVerify(peerToken, this.authority.publicKey, {
181
+ issuer: 'urn:agentic-system:authority',
182
+ audience: 'urn:agentic-system:agents'
183
+ });
184
+
185
+ // Verifica se está na mesma conversa
186
+ const sameConversation = payload.conversationId === this.conversationId;
187
+
188
+ return {
189
+ agentId: payload.agentId as string,
190
+ conversationId: payload.conversationId as string,
191
+ valid: sameConversation
192
+ };
193
+ } catch (error) {
194
+ return {
195
+ agentId: 'unknown',
196
+ conversationId: 'unknown',
197
+ valid: false
198
+ };
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Envia uma mensagem para outro agente
204
+ */
205
+ async sendMessage(to: SelfHealingAgent, content: string): Promise<void> {
206
+ // Garante que o token está válido antes de enviar
207
+ await this.ensureValidToken();
208
+
209
+ if (!this.token) {
210
+ throw new Error('Agente não possui token válido');
211
+ }
212
+
213
+ // Verifica a identidade do destinatário
214
+ const peerIdentity = await this.verifyPeerIdentity(to.getToken());
215
+
216
+ if (!peerIdentity.valid) {
217
+ throw new Error(`Agente ${peerIdentity.agentId} não está na mesma conversa`);
218
+ }
219
+
220
+ const message: AgentMessage = {
221
+ from: this.agentId,
222
+ to: peerIdentity.agentId,
223
+ content,
224
+ timestamp: Date.now(),
225
+ messageId: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
226
+ };
227
+
228
+ this.messageHistory.push(message);
229
+ console.log(`📤 [${this.agentId}] → [${peerIdentity.agentId}]: ${content}`);
230
+
231
+ // Envia para o destinatário
232
+ await to.receiveMessage(message, this.token);
233
+ }
234
+
235
+ /**
236
+ * Recebe uma mensagem de outro agente
237
+ */
238
+ async receiveMessage(message: AgentMessage, senderToken: string): Promise<void> {
239
+ // Garante que o token está válido antes de receber
240
+ await this.ensureValidToken();
241
+
242
+ // Verifica a identidade do remetente
243
+ const senderIdentity = await this.verifyPeerIdentity(senderToken);
244
+
245
+ if (!senderIdentity.valid || senderIdentity.agentId !== message.from) {
246
+ throw new Error(`Mensagem de agente não autorizado: ${senderIdentity.agentId}`);
247
+ }
248
+
249
+ this.messageHistory.push(message);
250
+ console.log(`📥 [${this.agentId}] ← [${message.from}]: ${message.content}`);
251
+ }
252
+
253
+ /**
254
+ * Retorna o token atual (para verificação de identidade)
255
+ */
256
+ getToken(): string {
257
+ if (!this.token) {
258
+ throw new Error('Agente não possui token');
259
+ }
260
+ return this.token;
261
+ }
262
+
263
+ /**
264
+ * Retorna o histórico da conversa
265
+ */
266
+ getConversationHistory(): AgentMessage[] {
267
+ return [...this.messageHistory];
268
+ }
269
+
270
+ /**
271
+ * Inicia monitoramento automático de renovação de token
272
+ */
273
+ startAutoRenewal(intervalMs: number = 30000): void {
274
+ setInterval(async () => {
275
+ await this.ensureValidToken();
276
+ }, intervalMs);
277
+
278
+ console.log(`🔄 [${this.agentId}] Auto-renovação de token ativada (verifica a cada ${intervalMs}ms)`);
279
+ }
280
+ }
281
+
282
+ // ============================================================================
283
+ // Exemplo de Uso: Conversa Auto-Recuperável entre Dois Agentes
284
+ // ============================================================================
285
+
286
+ async function demonstrateSelfHealingAgents() {
287
+ console.log('🚀 Iniciando demonstração de Self-Healing Agentic Conversational System\n');
288
+
289
+ // 1. Criar autoridade de tokens
290
+ const authority = new TokenAuthority();
291
+ console.log('✅ Autoridade de tokens criada\n');
292
+
293
+ // 2. Criar dois agentes
294
+ const agentA = new SelfHealingAgent(
295
+ 'agent-alpha',
296
+ 'primary',
297
+ authority,
298
+ ['reasoning', 'memory', 'planning']
299
+ );
300
+
301
+ const agentB = new SelfHealingAgent(
302
+ 'agent-beta',
303
+ 'secondary',
304
+ authority,
305
+ ['analysis', 'synthesis', 'validation']
306
+ );
307
+
308
+ // 3. Inicializar agentes
309
+ await agentA.initialize();
310
+ await agentB.initialize();
311
+ console.log('');
312
+
313
+ // 4. Ativar auto-renovação
314
+ agentA.startAutoRenewal(30000); // Verifica a cada 30 segundos
315
+ agentB.startAutoRenewal(30000);
316
+
317
+ // 5. Simular conversa longa (que ultrapassa expiração de token)
318
+ console.log('💬 Iniciando conversa entre agentes...\n');
319
+
320
+ // Primeira troca de mensagens
321
+ await agentA.sendMessage(agentB, 'Olá! Sou o Agente Alpha. Como você está?');
322
+ await new Promise(resolve => setTimeout(resolve, 1000));
323
+
324
+ await agentB.sendMessage(agentA, 'Olá Alpha! Sou o Agente Beta. Estou funcionando perfeitamente!');
325
+ await new Promise(resolve => setTimeout(resolve, 1000));
326
+
327
+ // Simular espera até próximo da expiração
328
+ console.log('\n⏳ Simulando espera de 4 minutos (tokens expiram em 5 minutos)...\n');
329
+ await new Promise(resolve => setTimeout(resolve, 1000)); // Simulação rápida
330
+
331
+ // Continuar conversa após possível renovação automática
332
+ await agentA.sendMessage(agentB, 'Perfeito! Vamos trabalhar juntos neste problema complexo.');
333
+ await new Promise(resolve => setTimeout(resolve, 1000));
334
+
335
+ await agentB.sendMessage(agentA, 'Excelente! Estou pronto para colaborar. Meus tokens foram renovados automaticamente.');
336
+ await new Promise(resolve => setTimeout(resolve, 1000));
337
+
338
+ // Mostrar histórico
339
+ console.log('\n📜 Histórico da conversa:');
340
+ const history = agentA.getConversationHistory();
341
+ history.forEach(msg => {
342
+ const time = new Date(msg.timestamp).toLocaleTimeString();
343
+ console.log(`[${time}] ${msg.from} → ${msg.to}: ${msg.content}`);
344
+ });
345
+
346
+ console.log('\n✅ Demonstração concluída! Os agentes mantiveram a conversa mesmo com renovação automática de tokens.');
347
+ }
348
+
349
+ // Executar demonstração
350
+ if (import.meta.url === `file://${process.argv[1]}`) {
351
+ demonstrateSelfHealingAgents().catch(console.error);
352
+ }
353
+
354
+ export { SelfHealingAgent, TokenAuthority, AgentMessage };
355
+