agentic-api 2.0.645 → 2.0.684
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/dist/src/agents/prompts.d.ts +1 -1
- package/dist/src/agents/prompts.js +8 -9
- package/dist/src/agents/reducer.loaders.d.ts +71 -0
- package/dist/src/agents/reducer.loaders.js +108 -1
- package/dist/src/agents/reducer.types.d.ts +5 -1
- package/dist/src/agents/simulator.d.ts +30 -1
- package/dist/src/agents/simulator.executor.d.ts +7 -3
- package/dist/src/agents/simulator.executor.js +81 -50
- package/dist/src/agents/simulator.js +161 -7
- package/dist/src/agents/simulator.prompts.d.ts +18 -0
- package/dist/src/agents/simulator.prompts.js +81 -4
- package/dist/src/agents/simulator.types.d.ts +29 -0
- package/dist/src/agents/simulator.types.js +5 -0
- package/dist/src/execute/helpers.js +1 -1
- package/dist/src/execute/responses.js +4 -5
- package/dist/src/rag/rag.manager.js +23 -7
- package/dist/src/rag/types.d.ts +3 -1
- package/dist/src/rules/git/git.e2e.helper.js +48 -4
- package/dist/src/rules/git/git.health.js +6 -4
- package/dist/src/rules/git/index.d.ts +2 -2
- package/dist/src/rules/git/index.js +11 -2
- package/dist/src/rules/git/repo.d.ts +39 -0
- package/dist/src/rules/git/repo.js +155 -8
- package/dist/src/rules/git/repo.pr.d.ts +11 -18
- package/dist/src/rules/git/repo.pr.js +37 -81
- package/dist/src/rules/types.d.ts +1 -1
- package/dist/src/stategraph/stategraph.d.ts +2 -2
- package/dist/src/stategraph/stategraph.js +5 -6
- package/dist/src/stategraph/types.d.ts +2 -6
- package/dist/src/types.d.ts +1 -0
- package/package.json +1 -1
|
@@ -59,6 +59,127 @@ class AgentSimulator {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
// ============================================================================
|
|
62
|
+
// WORKER API : runJob(input) — exécution de mission autonome
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Exécuter un WorkerJob — boucle Worker LLM ↔ Agent LLM
|
|
66
|
+
*
|
|
67
|
+
* Parallèle à testCase() mais pour des missions de production :
|
|
68
|
+
* - Le Worker LLM formule des questions pour l'Agent
|
|
69
|
+
* - L'Agent répond avec ses tools (RAG, DB, emails, calendar...)
|
|
70
|
+
* - Le Worker LLM évalue et continue ou termine avec [WORKER_COMPLETE]
|
|
71
|
+
*
|
|
72
|
+
* @param input - Paramètres du job (query, maxIterations, agentContext)
|
|
73
|
+
* @returns WorkerResult
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const worker = new AgentSimulator({
|
|
78
|
+
* agents: [...], start: 'PR-knowledge', verbose: false,
|
|
79
|
+
* mode: 'worker',
|
|
80
|
+
* instructionEx: 'Consulter tous les mails du jour et résumer les actions'
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* const result = await worker.runJob({
|
|
84
|
+
* query: 'Commence par lister les mails du jour',
|
|
85
|
+
* maxIterations: 10,
|
|
86
|
+
* agentContext: realContext
|
|
87
|
+
* });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async runJob(input) {
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
const maxIterations = input.maxIterations ?? 10;
|
|
93
|
+
//
|
|
94
|
+
// Initialiser les contextes avec le contexte réel de l'agent
|
|
95
|
+
const context = await this.executor.initializeContexts({ goals: '', persona: '', result: '' }, input.agentContext);
|
|
96
|
+
const allMessages = [];
|
|
97
|
+
let exchangeCounter = 0;
|
|
98
|
+
try {
|
|
99
|
+
let currentUserQuery = input.query;
|
|
100
|
+
//
|
|
101
|
+
// Stocker le message initial
|
|
102
|
+
const initialMsg = { content: currentUserQuery, role: 'user' };
|
|
103
|
+
allMessages.push(initialMsg);
|
|
104
|
+
if (input.onMessage) {
|
|
105
|
+
input.onMessage(initialMsg);
|
|
106
|
+
}
|
|
107
|
+
//
|
|
108
|
+
// Boucle Worker — maxIterations échanges (parallèle à executeSimulation)
|
|
109
|
+
while (exchangeCounter < maxIterations) {
|
|
110
|
+
//
|
|
111
|
+
// Agent répond à la question
|
|
112
|
+
const agentResponse = await this.executor.executeAgent(context, currentUserQuery);
|
|
113
|
+
const agentMsg = { content: agentResponse, role: 'assistant' };
|
|
114
|
+
allMessages.push(agentMsg);
|
|
115
|
+
if (input.onMessage) {
|
|
116
|
+
input.onMessage(agentMsg);
|
|
117
|
+
}
|
|
118
|
+
exchangeCounter++;
|
|
119
|
+
//
|
|
120
|
+
// Worker LLM évalue la réponse de l'Agent
|
|
121
|
+
const workerResponse = await this.executor.executeSimulator(context, agentResponse);
|
|
122
|
+
//
|
|
123
|
+
// Vérifier [WORKER_COMPLETE]
|
|
124
|
+
if (this.isWorkerComplete(workerResponse)) {
|
|
125
|
+
const parsed = this.parseWorkerResult(workerResponse);
|
|
126
|
+
this.lastExecution = context.lastExecution;
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
deliverable: parsed.deliverable,
|
|
130
|
+
summary: parsed.summary,
|
|
131
|
+
confidence: parsed.confidence,
|
|
132
|
+
iterations: exchangeCounter,
|
|
133
|
+
maxIterations,
|
|
134
|
+
duration: Date.now() - startTime,
|
|
135
|
+
execution: context.lastExecution,
|
|
136
|
+
messages: allMessages
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//
|
|
140
|
+
// Pas terminé — extraire la prochaine question pour l'Agent
|
|
141
|
+
currentUserQuery = this.extractConversationalPart(workerResponse);
|
|
142
|
+
if (currentUserQuery) {
|
|
143
|
+
const workerMsg = { content: currentUserQuery, role: 'user' };
|
|
144
|
+
allMessages.push(workerMsg);
|
|
145
|
+
if (input.onMessage) {
|
|
146
|
+
input.onMessage(workerMsg);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//
|
|
151
|
+
// Timeout — maxIterations atteint sans [WORKER_COMPLETE]
|
|
152
|
+
this.lastExecution = context.lastExecution;
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
deliverable: '',
|
|
156
|
+
summary: `Max iterations reached (${maxIterations})`,
|
|
157
|
+
confidence: 0,
|
|
158
|
+
iterations: exchangeCounter,
|
|
159
|
+
maxIterations,
|
|
160
|
+
duration: Date.now() - startTime,
|
|
161
|
+
execution: context.lastExecution,
|
|
162
|
+
messages: allMessages,
|
|
163
|
+
error: 'Max iterations reached without [WORKER_COMPLETE]'
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.lastExecution = context.lastExecution;
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
deliverable: '',
|
|
171
|
+
summary: '',
|
|
172
|
+
confidence: 0,
|
|
173
|
+
iterations: exchangeCounter,
|
|
174
|
+
maxIterations,
|
|
175
|
+
duration: Date.now() - startTime,
|
|
176
|
+
execution: context.lastExecution,
|
|
177
|
+
messages: allMessages,
|
|
178
|
+
error: `Erreur d'exécution: ${error.message || error}`
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// ============================================================================
|
|
62
183
|
// ANCIENNE API : executeSimulation(options) - deprecated
|
|
63
184
|
// ============================================================================
|
|
64
185
|
/**
|
|
@@ -118,12 +239,18 @@ class AgentSimulator {
|
|
|
118
239
|
const parsed = this.parseSimulationResult(simulatorResult, expectedFormat, context);
|
|
119
240
|
this.lastExecution = context.lastExecution;
|
|
120
241
|
// Validation des tools si expectedTool est fourni
|
|
121
|
-
if (options.expectedTool) {
|
|
242
|
+
if (options.expectedTool && Object.keys(options.expectedTool).length > 0) {
|
|
122
243
|
const validation = this.validateExpectedTools(options.expectedTool);
|
|
244
|
+
//
|
|
245
|
+
// Toujours définir toolValidation pour inspection par les tests
|
|
246
|
+
parsed.toolValidation = validation;
|
|
123
247
|
if (!validation.passed) {
|
|
124
248
|
parsed.success = false;
|
|
125
|
-
|
|
126
|
-
|
|
249
|
+
//
|
|
250
|
+
// Préserver l'erreur LLM si présente, ajouter l'erreur de validation
|
|
251
|
+
const llmError = parsed.error || '';
|
|
252
|
+
const toolError = validation.errors.join('; ');
|
|
253
|
+
parsed.error = llmError ? `${llmError} | Tool validation: ${toolError}` : toolError;
|
|
127
254
|
}
|
|
128
255
|
}
|
|
129
256
|
// Ajouter l'historique des messages au résultat
|
|
@@ -149,12 +276,16 @@ class AgentSimulator {
|
|
|
149
276
|
const timeout = await this.generateTimeoutReport(context, expectedFormat);
|
|
150
277
|
this.lastExecution = context.lastExecution;
|
|
151
278
|
// Validation des tools même en cas de timeout
|
|
152
|
-
if (options.expectedTool) {
|
|
279
|
+
if (options.expectedTool && Object.keys(options.expectedTool).length > 0) {
|
|
153
280
|
const validation = this.validateExpectedTools(options.expectedTool);
|
|
281
|
+
//
|
|
282
|
+
// Toujours définir toolValidation pour inspection par les tests
|
|
283
|
+
timeout.toolValidation = validation;
|
|
154
284
|
if (!validation.passed) {
|
|
155
285
|
timeout.success = false;
|
|
156
|
-
timeout.error
|
|
157
|
-
|
|
286
|
+
const llmError = timeout.error || '';
|
|
287
|
+
const toolError = validation.errors.join('; ');
|
|
288
|
+
timeout.error = llmError ? `${llmError} | Tool validation: ${toolError}` : toolError;
|
|
158
289
|
}
|
|
159
290
|
}
|
|
160
291
|
return { ...timeout, exchangeCount: exchangeCounter, messages: allMessages }; // Utiliser notre compteur
|
|
@@ -222,6 +353,29 @@ class AgentSimulator {
|
|
|
222
353
|
isSimulationComplete(response) {
|
|
223
354
|
return response.includes('[DONE]') || response.includes('[SIMULATION_COMPLETE]') || response.includes('[TERMINE]');
|
|
224
355
|
}
|
|
356
|
+
isWorkerComplete(response) {
|
|
357
|
+
return response.includes('[WORKER_COMPLETE]');
|
|
358
|
+
}
|
|
359
|
+
parseWorkerResult(response) {
|
|
360
|
+
const match = response.match(/\[WORKER_COMPLETE\]\s*\n?([\s\S]*)/);
|
|
361
|
+
if (match) {
|
|
362
|
+
const content = match[1].trim();
|
|
363
|
+
try {
|
|
364
|
+
const parsed = JSON.parse(content);
|
|
365
|
+
return {
|
|
366
|
+
deliverable: parsed.deliverable || content,
|
|
367
|
+
summary: parsed.summary || '',
|
|
368
|
+
confidence: parsed.confidence ?? 0.5
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
//
|
|
373
|
+
// Fallback : contenu brut comme deliverable
|
|
374
|
+
return { deliverable: content, summary: '', confidence: 0.5 };
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return { deliverable: '', summary: '', confidence: 0 };
|
|
378
|
+
}
|
|
225
379
|
parseSimulationResult(response, expectedFormat, context) {
|
|
226
380
|
// Le simulateur produit directement le résultat et rapport
|
|
227
381
|
// Parsing pour détecter la fin et extraire le contenu JSON
|
|
@@ -286,7 +440,7 @@ class AgentSimulator {
|
|
|
286
440
|
extractConversationalPart(response) {
|
|
287
441
|
// Extraire la partie conversationnelle avant les tags d'évaluation ou d'observation
|
|
288
442
|
// Filtrer tous les tags système : [DONE], [OBSERVATEUR SILENCIEUX], [À NOTER], etc.
|
|
289
|
-
const tagIndex = response.search(/\[(DONE|SIMULATION_COMPLETE|TERMINE|BUG_|OBSERVATEUR|À NOTER|NOTE|ANALYSE)/i);
|
|
443
|
+
const tagIndex = response.search(/\[(DONE|SIMULATION_COMPLETE|TERMINE|WORKER_COMPLETE|BUG_|OBSERVATEUR|À NOTER|NOTE|ANALYSE)/i);
|
|
290
444
|
if (tagIndex !== -1) {
|
|
291
445
|
return response.substring(0, tagIndex).trim();
|
|
292
446
|
}
|
|
@@ -16,3 +16,21 @@ export declare function GENERIC_SIMULATOR_PROMPT(scenario: SimulationScenario, i
|
|
|
16
16
|
export declare const PERSONA_PATIENT = "Utilisateur patient et poli qui prend le temps d'expliquer sa situation";
|
|
17
17
|
export declare const PERSONA_PRESSE = "Utilisateur press\u00E9 qui veut une solution rapide, r\u00E9pond bri\u00E8vement";
|
|
18
18
|
export declare const PERSONA_ENERVE = "Utilisateur \u00E9nerv\u00E9 et frustr\u00E9, c'est son 3\u00E8me appel pour le m\u00EAme probl\u00E8me, ton direct et impatient";
|
|
19
|
+
/**
|
|
20
|
+
* Persona collaborateur professionnel et méthodique
|
|
21
|
+
*/
|
|
22
|
+
export declare const PERSONA_EMPLOYEE = "Collaborateur professionnel et m\u00E9thodique, tu formules des demandes claires et pr\u00E9cises, tu analyses les r\u00E9ponses de mani\u00E8re critique, et tu identifies les lacunes pour poser des questions de suivi.";
|
|
23
|
+
/**
|
|
24
|
+
* Prompt interne fixe du WorkerJob — system prompt pour le Worker LLM
|
|
25
|
+
*
|
|
26
|
+
* Parallèle à GENERIC_SIMULATOR_PROMPT : même pattern, différent rôle.
|
|
27
|
+
* - GENERIC_SIMULATOR_PROMPT → testeur automatisé, détecte [DONE]
|
|
28
|
+
* - WORKER_INTERNAL_PROMPT → collaborateur, détecte [WORKER_COMPLETE]
|
|
29
|
+
*
|
|
30
|
+
* Le brief est injecté une fois dans les instructions système.
|
|
31
|
+
* La mémoire est gérée par l'historique de conversation (comme le simulateur).
|
|
32
|
+
*
|
|
33
|
+
* @param brief - Le cahier des charges du worker (= instructionEx de SimulatorConfig)
|
|
34
|
+
* @param instructionEx - Instructions additionnelles (optionnel)
|
|
35
|
+
*/
|
|
36
|
+
export declare function WORKER_INTERNAL_PROMPT(brief: string, instructionEx?: string): string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PERSONA_ENERVE = exports.PERSONA_PRESSE = exports.PERSONA_PATIENT = void 0;
|
|
3
|
+
exports.PERSONA_EMPLOYEE = exports.PERSONA_ENERVE = exports.PERSONA_PRESSE = exports.PERSONA_PATIENT = void 0;
|
|
4
4
|
exports.GENERIC_SIMULATOR_PROMPT = GENERIC_SIMULATOR_PROMPT;
|
|
5
|
+
exports.WORKER_INTERNAL_PROMPT = WORKER_INTERNAL_PROMPT;
|
|
5
6
|
/**
|
|
6
7
|
* Génère le prompt du simulateur générique avec le scenario intégré
|
|
7
8
|
*
|
|
@@ -56,9 +57,16 @@ Exemple de conversation:
|
|
|
56
57
|
Tu retournes le trigger de fin: \`[DONE] {"success": true, "explain": "..."}\` ou \`[DONE] {"success": false, "error": "..."}\`.
|
|
57
58
|
|
|
58
59
|
### CONTEXT (\`<agent-context>\` invisible pour le CLIENT)
|
|
59
|
-
- Le tag \`<agent-context>\` contient
|
|
60
|
-
-
|
|
61
|
-
**
|
|
60
|
+
- Le tag \`<agent-context>\` contient les **APPELS RÉELS** des outils effectués par l'agent testé.
|
|
61
|
+
- Le champ \`tools\` liste les **noms exacts des outils appelés** pendant cet échange.
|
|
62
|
+
- **CRITIQUE**: Si un outil apparaît dans \`<agent-context>.tools\`, c'est qu'il a **réellement été exécuté** par l'agent.
|
|
63
|
+
- Ces informations sont **strictement réservées à toi l'observateur**.
|
|
64
|
+
- **AUTORISÉ** : les exploiter pour valider les objectifs ou décider de la fin du test.
|
|
65
|
+
|
|
66
|
+
Exemple avec validation d'outil:
|
|
67
|
+
> Agent répond avec une information
|
|
68
|
+
> \`<agent-context>{"tools": ["lookupKnowledge"], "exchangeCount": 1}</agent-context>\`
|
|
69
|
+
> → L'outil \`lookupKnowledge\` a été **réellement appelé** par l'agent ✅
|
|
62
70
|
|
|
63
71
|
Exemple avec sortie pour <simulation_goals>CONDITION DE FIN: L'agent demande si l'utilisateur souhaite chercher sur internet</simulation_goals>:
|
|
64
72
|
> Toi : "Quelle est la température du lac à Genève ?"
|
|
@@ -86,3 +94,72 @@ ${instructionEx ? `\n\n${instructionEx}` : ''}`;
|
|
|
86
94
|
exports.PERSONA_PATIENT = 'Utilisateur patient et poli qui prend le temps d\'expliquer sa situation';
|
|
87
95
|
exports.PERSONA_PRESSE = 'Utilisateur pressé qui veut une solution rapide, répond brièvement';
|
|
88
96
|
exports.PERSONA_ENERVE = 'Utilisateur énervé et frustré, c\'est son 3ème appel pour le même problème, ton direct et impatient';
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// WORKER PROMPT — Prompt interne fixe pour WorkerJob
|
|
99
|
+
// ============================================================================
|
|
100
|
+
/**
|
|
101
|
+
* Persona collaborateur professionnel et méthodique
|
|
102
|
+
*/
|
|
103
|
+
exports.PERSONA_EMPLOYEE = 'Collaborateur professionnel et méthodique, tu formules des demandes claires et précises, tu analyses les réponses de manière critique, et tu identifies les lacunes pour poser des questions de suivi.';
|
|
104
|
+
/**
|
|
105
|
+
* Prompt interne fixe du WorkerJob — system prompt pour le Worker LLM
|
|
106
|
+
*
|
|
107
|
+
* Parallèle à GENERIC_SIMULATOR_PROMPT : même pattern, différent rôle.
|
|
108
|
+
* - GENERIC_SIMULATOR_PROMPT → testeur automatisé, détecte [DONE]
|
|
109
|
+
* - WORKER_INTERNAL_PROMPT → collaborateur, détecte [WORKER_COMPLETE]
|
|
110
|
+
*
|
|
111
|
+
* Le brief est injecté une fois dans les instructions système.
|
|
112
|
+
* La mémoire est gérée par l'historique de conversation (comme le simulateur).
|
|
113
|
+
*
|
|
114
|
+
* @param brief - Le cahier des charges du worker (= instructionEx de SimulatorConfig)
|
|
115
|
+
* @param instructionEx - Instructions additionnelles (optionnel)
|
|
116
|
+
*/
|
|
117
|
+
function WORKER_INTERNAL_PROMPT(brief, instructionEx) {
|
|
118
|
+
return `# IDENTITÉ
|
|
119
|
+
Tu es un **COLLABORATEUR** chargé d'une mission. Tu travailles de manière autonome en formulant des demandes précises à un Agent qui dispose de tous les outils nécessaires (recherche, bases de données, emails, calendrier, documents...).
|
|
120
|
+
|
|
121
|
+
# MISSION
|
|
122
|
+
Tu dois accomplir le cahier des charges décrit dans <worker_brief> en utilisant l'Agent comme exécutant.
|
|
123
|
+
À chaque échange, tu formules UNE question ou instruction précise pour l'Agent.
|
|
124
|
+
Tu analyses sa réponse, accumules les informations utiles, et décides de ta prochaine action.
|
|
125
|
+
|
|
126
|
+
# MÉTHODE
|
|
127
|
+
1. **Analyser** le brief et les réponses précédentes
|
|
128
|
+
2. **Identifier** la prochaine information nécessaire ou action à réaliser
|
|
129
|
+
3. **Formuler** une question/instruction claire et précise pour l'Agent
|
|
130
|
+
4. **Évaluer** la réponse : est-ce suffisant pour atteindre l'objectif ?
|
|
131
|
+
|
|
132
|
+
# RÈGLES
|
|
133
|
+
- Formule UNE seule question/instruction par échange
|
|
134
|
+
- Sois précis et contextuel dans tes demandes
|
|
135
|
+
- N'invente pas d'information — base-toi uniquement sur les réponses de l'Agent
|
|
136
|
+
- Si l'Agent ne peut pas répondre, reformule ou change d'approche
|
|
137
|
+
- Accumule les faits clés de chaque réponse pour les réutiliser
|
|
138
|
+
|
|
139
|
+
# TRIGGERS DE SORTIE
|
|
140
|
+
Quand tu estimes que l'objectif du brief est atteint, ou que tu as accumulé suffisamment d'information pour produire le livrable, termine avec :
|
|
141
|
+
\`[WORKER_COMPLETE]\`
|
|
142
|
+
Suivi immédiatement du JSON de résultat (voir format ci-dessous).
|
|
143
|
+
|
|
144
|
+
Si tu n'as pas terminé, formule simplement ta prochaine question/instruction pour l'Agent.
|
|
145
|
+
|
|
146
|
+
# FORMAT DE SORTIE (uniquement quand terminé)
|
|
147
|
+
\`\`\`
|
|
148
|
+
[WORKER_COMPLETE]
|
|
149
|
+
{"deliverable": "...", "summary": "...", "confidence": 0.95}
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
Où :
|
|
153
|
+
- \`deliverable\` : le résultat complet de la mission (texte, données, rapport...)
|
|
154
|
+
- \`summary\` : résumé court de ce qui a été accompli
|
|
155
|
+
- \`confidence\` : niveau de confiance (0.0 à 1.0) que l'objectif est atteint
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
<worker_brief>
|
|
160
|
+
${brief}
|
|
161
|
+
</worker_brief>
|
|
162
|
+
|
|
163
|
+
**CRITICAL**: Si tu termines, le contenu après [WORKER_COMPLETE] doit être du JSON valide uniquement.
|
|
164
|
+
${instructionEx ? `\n\n${instructionEx}` : ''}`;
|
|
165
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { AgentMessage } from "../stategraph";
|
|
2
2
|
import { AgentConfig, AgenticContext, ExecutionResult } from "../types";
|
|
3
3
|
import { RAGManagerConfig } from "../rag";
|
|
4
|
+
export declare const SIMULATOR_AGENT_NAME = "simple-simulator";
|
|
5
|
+
export declare const SIMULATOR_AGENT_DESCRIPTION = "Simulateur simple pour tests - TOUJOURS retourner JSON valide";
|
|
6
|
+
export declare const WORKER_AGENT_NAME = "worker-agent";
|
|
7
|
+
export declare const WORKER_AGENT_DESCRIPTION = "Worker collaborateur pour missions autonomes";
|
|
4
8
|
export interface SimulatorConfig {
|
|
5
9
|
agents: AgentConfig[];
|
|
6
10
|
start: string;
|
|
7
11
|
verbose: boolean;
|
|
12
|
+
mode?: 'simulator' | 'worker';
|
|
8
13
|
instructionEx?: string;
|
|
9
14
|
mockCacheInitializer?: (sessionId: string) => Promise<void>;
|
|
10
15
|
ragConfig?: RAGManagerConfig;
|
|
@@ -38,6 +43,30 @@ export interface TestCaseInput {
|
|
|
38
43
|
expectedTools?: Record<string, ToolConstraint>;
|
|
39
44
|
onMessage?: (message: AgentMessage) => void;
|
|
40
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Input pour worker.runJob() — parallèle à TestCaseInput
|
|
48
|
+
*/
|
|
49
|
+
export interface WorkerJobInput {
|
|
50
|
+
query: string;
|
|
51
|
+
maxIterations?: number;
|
|
52
|
+
agentContext: AgenticContext;
|
|
53
|
+
onMessage?: (message: AgentMessage) => void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Résultat d'un WorkerJob — parallèle à SimulationResult
|
|
57
|
+
*/
|
|
58
|
+
export interface WorkerResult {
|
|
59
|
+
success: boolean;
|
|
60
|
+
deliverable: string;
|
|
61
|
+
summary: string;
|
|
62
|
+
confidence: number;
|
|
63
|
+
iterations: number;
|
|
64
|
+
maxIterations: number;
|
|
65
|
+
duration: number;
|
|
66
|
+
execution: ExecutionResult;
|
|
67
|
+
messages: AgentMessage[];
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
41
70
|
/**
|
|
42
71
|
* @deprecated Utiliser TestScenario à la place
|
|
43
72
|
*/
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WORKER_AGENT_DESCRIPTION = exports.WORKER_AGENT_NAME = exports.SIMULATOR_AGENT_DESCRIPTION = exports.SIMULATOR_AGENT_NAME = void 0;
|
|
4
|
+
exports.SIMULATOR_AGENT_NAME = 'simple-simulator';
|
|
5
|
+
exports.SIMULATOR_AGENT_DESCRIPTION = 'Simulateur simple pour tests - TOUJOURS retourner JSON valide';
|
|
6
|
+
exports.WORKER_AGENT_NAME = 'worker-agent';
|
|
7
|
+
exports.WORKER_AGENT_DESCRIPTION = 'Worker collaborateur pour missions autonomes';
|
|
@@ -45,7 +45,7 @@ function accumulateUsageTokens(stateGraph, discussion, agentName, model, usage)
|
|
|
45
45
|
function stepsToActions(stateGraph, agentName) {
|
|
46
46
|
return stateGraph.steps(agentName).map(step => ({
|
|
47
47
|
action: step.tool,
|
|
48
|
-
content: step.
|
|
48
|
+
content: step.reason,
|
|
49
49
|
feedback: step.reason
|
|
50
50
|
}));
|
|
51
51
|
}
|
|
@@ -196,7 +196,7 @@ async function readCompletionsStream(params) {
|
|
|
196
196
|
const transferredAgent = agents.find(a => a.name === transferAgentName) || agentConfig;
|
|
197
197
|
const instructions = transferredAgent.instructions;
|
|
198
198
|
const enrichedInstructions = (await params.enrichWithMemory?.("system", transferredAgent, context)) || '';
|
|
199
|
-
// ✅ Set préserve le trail existant via
|
|
199
|
+
// ✅ Set préserve le trail existant via updateSystemContextTrail()
|
|
200
200
|
stateGraph.set(discussionRootAgent, instructions + '\n' + enrichedInstructions);
|
|
201
201
|
}
|
|
202
202
|
//
|
|
@@ -236,12 +236,11 @@ async function readCompletionsStream(params) {
|
|
|
236
236
|
for (let i = 0; i < results.length; i++) {
|
|
237
237
|
const result = results[i];
|
|
238
238
|
const toolCall = parsedCalls[i];
|
|
239
|
+
const reason = result.context ? `${result.context} (${toolCall.args.justification})` : toolCall.args.justification;
|
|
239
240
|
if (result.name) {
|
|
240
241
|
stateGraph.addStep(discussionRootAgent, {
|
|
241
242
|
tool: result.name,
|
|
242
|
-
|
|
243
|
-
reason: toolCall.args.justification || '',
|
|
244
|
-
id: result.id
|
|
243
|
+
reason
|
|
245
244
|
});
|
|
246
245
|
}
|
|
247
246
|
}
|
|
@@ -355,7 +354,7 @@ async function executeAgentSet(agentSet, context, params) {
|
|
|
355
354
|
else if (enrichedInstructions) {
|
|
356
355
|
//
|
|
357
356
|
// Existing discussion: only update system message if enrichedInstructions exists
|
|
358
|
-
// stateGraph.set() preserves context-trail via
|
|
357
|
+
// stateGraph.set() preserves context-trail via updateSystemContextTrail()
|
|
359
358
|
const instructions = currentAgentConfig.instructions + '\n' + enrichedInstructions;
|
|
360
359
|
stateGraph.set(discussionRootAgent, instructions);
|
|
361
360
|
}
|
|
@@ -34,6 +34,9 @@ class RAGManager {
|
|
|
34
34
|
if (!config.baseDir || !(0, fs_1.existsSync)(config.baseDir)) {
|
|
35
35
|
throw new Error(`Le dossier du registre RAG n'existe pas: ${config.baseDir}`);
|
|
36
36
|
}
|
|
37
|
+
if (!config.defaultName || config.defaultName.trim() === '') {
|
|
38
|
+
throw new Error('RAGManagerConfig.defaultName est obligatoire et ne peut pas être vide');
|
|
39
|
+
}
|
|
37
40
|
this.config = {
|
|
38
41
|
tempDir: path_1.default.join(config.baseDir, 'temp'),
|
|
39
42
|
archiveDir: path_1.default.join(config.baseDir, 'archives'),
|
|
@@ -49,7 +52,10 @@ class RAGManager {
|
|
|
49
52
|
// store instance in memory
|
|
50
53
|
DEFAULT_RAG_INSTANCE[this.config.baseDir] = this;
|
|
51
54
|
const registry = this.getRegistry();
|
|
52
|
-
const defaultName = registry.defaultName
|
|
55
|
+
const defaultName = registry.defaultName;
|
|
56
|
+
if (!defaultName || defaultName.trim() === '') {
|
|
57
|
+
throw new Error('registry.json invalide: defaultName manquant');
|
|
58
|
+
}
|
|
53
59
|
DEFAULT_RAG_INSTANCE[defaultName] = this;
|
|
54
60
|
}
|
|
55
61
|
static get(config) {
|
|
@@ -101,7 +107,7 @@ class RAGManager {
|
|
|
101
107
|
registry = {
|
|
102
108
|
version: '1.0',
|
|
103
109
|
registries: {},
|
|
104
|
-
defaultName:
|
|
110
|
+
defaultName: this.config.defaultName
|
|
105
111
|
};
|
|
106
112
|
}
|
|
107
113
|
}
|
|
@@ -109,12 +115,12 @@ class RAGManager {
|
|
|
109
115
|
registry = {
|
|
110
116
|
version: '1.0',
|
|
111
117
|
registries: {},
|
|
112
|
-
defaultName:
|
|
118
|
+
defaultName: this.config.defaultName
|
|
113
119
|
};
|
|
114
120
|
}
|
|
115
|
-
//
|
|
121
|
+
// Fail-fast: defaultName doit toujours être défini explicitement
|
|
116
122
|
if (!registry.defaultName) {
|
|
117
|
-
registry.defaultName
|
|
123
|
+
throw new Error('registry.json invalide: defaultName manquant. Fournir defaultName dans la config RAG.');
|
|
118
124
|
}
|
|
119
125
|
// Scanner les dossiers pour découvrir les RAG existants
|
|
120
126
|
this.scanDirectoriesForRAGs(registry);
|
|
@@ -732,8 +738,15 @@ class RAGManager {
|
|
|
732
738
|
}
|
|
733
739
|
//
|
|
734
740
|
// Charger le metadata pour obtenir la liste des documents
|
|
741
|
+
// Ignorer les dossiers temporaires (temp/) sans metadata
|
|
735
742
|
const metadataFile = path_1.default.join(entry.configPath, types_1.RAG_FILES.METADATA);
|
|
736
743
|
if (!(0, fs_1.existsSync)(metadataFile)) {
|
|
744
|
+
//
|
|
745
|
+
// Si c'est un dossier temporaire (temp/rule-validation-*), retourner vide au lieu d'erreur
|
|
746
|
+
if (entry.configPath.includes('/temp/') || entry.status === 'building') {
|
|
747
|
+
console.warn(`⚠️ Dossier temporaire sans metadata ignoré: ${entry.configPath}`);
|
|
748
|
+
return { documents: {}, totalQueries: 0 };
|
|
749
|
+
}
|
|
737
750
|
throw new Error(`Fichier metadata manquant: ${metadataFile}`);
|
|
738
751
|
}
|
|
739
752
|
let metadata;
|
|
@@ -990,7 +1003,7 @@ class RAGManager {
|
|
|
990
1003
|
// Changer le défaut si nécessaire
|
|
991
1004
|
if (registry.defaultName === name) {
|
|
992
1005
|
const remainingRAGs = Object.keys(registry.registries);
|
|
993
|
-
registry.defaultName = remainingRAGs.length > 0 ? remainingRAGs[0] :
|
|
1006
|
+
registry.defaultName = remainingRAGs.length > 0 ? remainingRAGs[0] : this.config.defaultName;
|
|
994
1007
|
}
|
|
995
1008
|
this.saveRegistry(registry);
|
|
996
1009
|
console.log(`✅ RAG supprimé: ${name}`);
|
|
@@ -1420,7 +1433,10 @@ class RAGManager {
|
|
|
1420
1433
|
*/
|
|
1421
1434
|
getDefault() {
|
|
1422
1435
|
const registry = this.getRegistry();
|
|
1423
|
-
|
|
1436
|
+
if (!registry.defaultName || registry.defaultName.trim() === '') {
|
|
1437
|
+
throw new Error('registry.json invalide: defaultName manquant');
|
|
1438
|
+
}
|
|
1439
|
+
return registry.defaultName;
|
|
1424
1440
|
}
|
|
1425
1441
|
/**
|
|
1426
1442
|
* Obtient les statistiques de tous les RAG
|
package/dist/src/rag/types.d.ts
CHANGED
|
@@ -278,13 +278,15 @@ export interface RAGRegistry {
|
|
|
278
278
|
version: string;
|
|
279
279
|
/** Liste des RAG disponibles */
|
|
280
280
|
registries: Record<string, RAGRegistryEntry>;
|
|
281
|
-
/** RAG par défaut (
|
|
281
|
+
/** RAG par défaut (obligatoire) */
|
|
282
282
|
defaultName?: string;
|
|
283
283
|
}
|
|
284
284
|
/** Configuration du gestionnaire RAG */
|
|
285
285
|
export interface RAGManagerConfig {
|
|
286
286
|
/** Répertoire de base du registre */
|
|
287
287
|
baseDir: string;
|
|
288
|
+
/** Nom du RAG par défaut (obligatoire) */
|
|
289
|
+
defaultName?: string;
|
|
288
290
|
/** Répertoire temporaire pour les constructions */
|
|
289
291
|
tempDir?: string;
|
|
290
292
|
/** Répertoire d'archives */
|
|
@@ -156,14 +156,14 @@ Temporary repository for E2E testing.`;
|
|
|
156
156
|
return (0, index_1.gitCheckConfiguration)(this.git);
|
|
157
157
|
}
|
|
158
158
|
async isPRClosed(branch) {
|
|
159
|
-
return (0, index_1.
|
|
159
|
+
return (0, index_1.gitIsPRClosedRobust)(this.git, branch, this.config);
|
|
160
160
|
}
|
|
161
161
|
async isPRClosedRobust(branch, config) {
|
|
162
162
|
return (0, index_1.gitIsPRClosedRobust)(this.git, branch, config || this.config);
|
|
163
163
|
}
|
|
164
164
|
async closePR(branch, author, message) {
|
|
165
165
|
console.log(`[DEBUG][E2EHelper.closePR] Closing PR: ${branch} by ${author.name}`);
|
|
166
|
-
return (0, index_1.
|
|
166
|
+
return (0, index_1.gitClosePRRobust)(this.git, branch, author, message || `Closed by ${author.name}`, this.config);
|
|
167
167
|
}
|
|
168
168
|
async closePRRobust(branch, closedBy, message, config) {
|
|
169
169
|
return (0, index_1.gitClosePRRobust)(this.git, branch, closedBy, message, config || this.config);
|
|
@@ -174,7 +174,47 @@ Temporary repository for E2E testing.`;
|
|
|
174
174
|
return (0, index_1.gitGetAllPR)(this.git, options);
|
|
175
175
|
}
|
|
176
176
|
async getNextPRNumber(validationPrefix) {
|
|
177
|
-
|
|
177
|
+
const initialPRCounter = 60;
|
|
178
|
+
const registryPath = (0, path_1.join)(this.config.repoPath, '.git', 'with-ids.json');
|
|
179
|
+
const now = new Date().toISOString();
|
|
180
|
+
const allBranches = await this.getAllBranches();
|
|
181
|
+
const numbers = allBranches
|
|
182
|
+
.filter((branch) => branch.startsWith(validationPrefix))
|
|
183
|
+
.map((branch) => parseInt(branch.substring(validationPrefix.length).split('-')[0], 10))
|
|
184
|
+
.filter((num) => !isNaN(num));
|
|
185
|
+
const maxBranchNumber = numbers.length > 0 ? Math.max(...numbers) : initialPRCounter;
|
|
186
|
+
let registry = {
|
|
187
|
+
last: 980,
|
|
188
|
+
lastPR: initialPRCounter,
|
|
189
|
+
used: [],
|
|
190
|
+
updated: now,
|
|
191
|
+
matters: {}
|
|
192
|
+
};
|
|
193
|
+
if ((0, fs_1.existsSync)(registryPath)) {
|
|
194
|
+
try {
|
|
195
|
+
registry = JSON.parse(await fs_1.promises.readFile(registryPath, 'utf8'));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Rebuild minimal registry fallback
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!Number.isInteger(registry.lastPR)) {
|
|
202
|
+
registry.lastPR = initialPRCounter;
|
|
203
|
+
}
|
|
204
|
+
if (!Number.isInteger(registry.last)) {
|
|
205
|
+
registry.last = 980;
|
|
206
|
+
}
|
|
207
|
+
if (!Array.isArray(registry.used)) {
|
|
208
|
+
registry.used = [];
|
|
209
|
+
}
|
|
210
|
+
if (!registry.matters || typeof registry.matters !== 'object') {
|
|
211
|
+
registry.matters = {};
|
|
212
|
+
}
|
|
213
|
+
const nextPR = Math.max(registry.lastPR, maxBranchNumber, initialPRCounter) + 1;
|
|
214
|
+
registry.lastPR = nextPR;
|
|
215
|
+
registry.updated = now;
|
|
216
|
+
await fs_1.promises.writeFile(registryPath, JSON.stringify(registry, null, 2), 'utf8');
|
|
217
|
+
return nextPR;
|
|
178
218
|
}
|
|
179
219
|
async newPR(files, description, author, options) {
|
|
180
220
|
//console.log(`[DEBUG][E2EHelper.newPR] Creating new PR by ${author.name} for files: ${files.join(', ')}`);
|
|
@@ -220,7 +260,11 @@ Temporary repository for E2E testing.`;
|
|
|
220
260
|
async editFile(filePath, content, branch) {
|
|
221
261
|
const user = { name: 'E2E Test', email: 'e2e@test.com' };
|
|
222
262
|
const targetBranch = branch || this.config.draftBranch;
|
|
223
|
-
|
|
263
|
+
const exists = await (0, index_1.gitFileExistsInBranch)(this.git, filePath, targetBranch);
|
|
264
|
+
if (!exists) {
|
|
265
|
+
throw new Error(`File "${filePath}" not found in branch "${targetBranch}"`);
|
|
266
|
+
}
|
|
267
|
+
return (0, index_1.gitCreateOrEditFile)(this.git, filePath, targetBranch, content, user, this.config);
|
|
224
268
|
}
|
|
225
269
|
async validateRepositoryState() {
|
|
226
270
|
const errors = [];
|
|
@@ -608,10 +608,12 @@ class GitHealthManager {
|
|
|
608
608
|
// Parser le matter complet
|
|
609
609
|
const parsed = (0, utils_matter_1.matterParse)(fileData.content);
|
|
610
610
|
const { matter: fullMatter, content } = parsed;
|
|
611
|
-
// ✅ CORRECTION:
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
const matterWithID =
|
|
611
|
+
// ✅ CORRECTION: En cas de doublon détecté, éviter la réutilisation via cache fichier
|
|
612
|
+
// pour forcer une réallocation d'ID. Sinon, garder la logique d'identité stable.
|
|
613
|
+
const isDuplicateIssue = report.duplicateID.some((entry) => entry.startsWith(fileRef));
|
|
614
|
+
const matterWithID = isDuplicateIssue
|
|
615
|
+
? (0, git_1.gitEnsureMatterID)(fullMatter, this.config)
|
|
616
|
+
: (0, git_1.gitEnsureMatterID)(fullMatter, this.config, branchName, file);
|
|
615
617
|
if (matterWithID.id !== fullMatter.id) {
|
|
616
618
|
if (fullMatter.id && fullMatter.id > 999) {
|
|
617
619
|
console.log(` 🔧 ${file}: ID ${fullMatter.id} remplacé par ID existant ${matterWithID.id}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { lock, unlock, gitLoad, isValidInt, gitIsFileMerged, gitLastCommit, gitListFilesInBranch, gitListFilesOutsideRepo, gitFileExistsInBranch, gitGetFilesSummary, gitGetFileContent, gitGetFilePreview, gitGetFileHistory, gitReadFileOutsideRepo, gitGetUnmergedBranchesForFile, gitGetAllBranches, gitGetDiffFiles, gitReadNote, gitWriteNote, gitDeleteNote, } from './repo.tools';
|
|
2
|
-
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
3
|
-
export { gitSyncPR, gitIsPRClosed, gitIsPRClosedRobust, gitGetPRMetadata, gitGetAllPR, gitGetClosedPRs, gitLoadPR, gitPRUpdateComments, gitClosePR, gitClosePRRobust,
|
|
2
|
+
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitAllocateNextPRNumber, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
3
|
+
export { gitSyncPR, gitIsPRClosed, gitIsPRClosedRobust, gitGetPRMetadata, gitGetAllPR, gitGetClosedPRs, gitLoadPR, gitPRUpdateComments, gitClosePR, gitClosePRRobust, gitNewValidationRequest, gitNewPR, } from './repo.pr';
|
|
4
4
|
export * from './git.e2e.helper';
|
|
5
5
|
export * from './git.health';
|
|
6
6
|
export * from './git.helper';
|