@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.
- package/CHANGELOG.md +87 -0
- package/LICENSE-COGFULNESS +117 -0
- package/examples/MTLS_AGENTS.md +288 -0
- package/examples/SELF_HEALING_AGENTS.md +269 -0
- package/examples/SIGNAL_E2EE.md +457 -0
- package/examples/mtls-agents.ts +539 -0
- package/examples/self-healing-agents.ts +355 -0
- package/examples/signal-e2ee-agents.ts +827 -0
- package/package.json +20 -0
- package/readme.md +507 -0
- package/reports/22-12-2024_01-50.md +165 -0
- package/src/examples.mcps.ts +86 -0
- package/src/examples.ts +81 -0
- package/src/index.ts +286 -0
- package/tsconfig.json +24 -0
|
@@ -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
|
+
|