agentic-api 2.0.314 → 2.0.491
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 +9 -7
- package/dist/src/agents/simulator.d.ts +7 -3
- package/dist/src/agents/simulator.executor.d.ts +9 -3
- package/dist/src/agents/simulator.executor.js +43 -17
- package/dist/src/agents/simulator.js +47 -19
- package/dist/src/agents/simulator.prompts.d.ts +9 -8
- package/dist/src/agents/simulator.prompts.js +68 -62
- package/dist/src/agents/simulator.types.d.ts +4 -1
- package/dist/src/agents/simulator.utils.js +0 -2
- package/dist/src/execute/helpers.d.ts +75 -0
- package/dist/src/execute/helpers.js +139 -0
- package/dist/src/execute/index.d.ts +11 -0
- package/dist/src/execute/index.js +44 -0
- package/dist/src/execute/legacy.d.ts +46 -0
- package/dist/src/{execute.js → execute/legacy.js} +130 -232
- package/dist/src/execute/modelconfig.d.ts +19 -0
- package/dist/src/execute/modelconfig.js +56 -0
- package/dist/src/execute/responses.d.ts +55 -0
- package/dist/src/execute/responses.js +594 -0
- package/dist/src/execute/shared.d.ts +83 -0
- package/dist/src/execute/shared.js +188 -0
- package/dist/src/index.js +1 -1
- package/dist/src/pricing.llm.d.ts +1 -1
- package/dist/src/pricing.llm.js +39 -18
- package/dist/src/rag/embeddings.js +8 -2
- package/dist/src/rag/rag.manager.js +27 -15
- package/dist/src/rules/git/git.e2e.helper.js +21 -2
- package/dist/src/rules/git/git.health.d.ts +4 -2
- package/dist/src/rules/git/git.health.js +58 -16
- package/dist/src/rules/git/index.d.ts +1 -1
- package/dist/src/rules/git/index.js +3 -2
- package/dist/src/rules/git/repo.d.ts +46 -3
- package/dist/src/rules/git/repo.js +264 -23
- package/dist/src/rules/git/repo.pr.js +117 -13
- package/dist/src/rules/types.d.ts +11 -0
- package/dist/src/rules/utils.matter.js +16 -7
- package/dist/src/scrapper.js +1 -0
- package/dist/src/stategraph/stategraph.d.ts +26 -1
- package/dist/src/stategraph/stategraph.js +43 -2
- package/dist/src/stategraph/stategraph.storage.js +4 -0
- package/dist/src/stategraph/types.d.ts +5 -0
- package/dist/src/types.d.ts +42 -7
- package/dist/src/types.js +8 -7
- package/dist/src/usecase.js +1 -1
- package/dist/src/utils.js +28 -4
- package/package.json +9 -7
- package/dist/src/execute.d.ts +0 -63
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DummyWritable = void 0;
|
|
4
|
+
exports.sendFeedback = sendFeedback;
|
|
5
|
+
exports.normalizeOptionsForResponses = normalizeOptionsForResponses;
|
|
6
|
+
exports.normalizedFunctionCallFromResponse = normalizedFunctionCallFromResponse;
|
|
7
|
+
exports.normalizeOutputFromResponses = normalizeOutputFromResponses;
|
|
8
|
+
exports.convertMessagesToResponsesInput = convertMessagesToResponsesInput;
|
|
9
|
+
//
|
|
10
|
+
// Writable vide pour les tests
|
|
11
|
+
exports.DummyWritable = {
|
|
12
|
+
write: () => { }
|
|
13
|
+
};
|
|
14
|
+
//
|
|
15
|
+
// Envoi de feedback temps réel vers l'UX
|
|
16
|
+
// Informe l'utilisateur de l'état de l'analyse : agent actif, description, usage, state
|
|
17
|
+
function sendFeedback(params) {
|
|
18
|
+
const { agent, stdout, description, usage, state, verbose } = params;
|
|
19
|
+
const feedback = {
|
|
20
|
+
agent,
|
|
21
|
+
description,
|
|
22
|
+
usage,
|
|
23
|
+
state
|
|
24
|
+
};
|
|
25
|
+
// if(verbose) {
|
|
26
|
+
// console.log('--- DBG sendFeedback:',agent, description || '--', state);
|
|
27
|
+
// }
|
|
28
|
+
//
|
|
29
|
+
// send agent state and description
|
|
30
|
+
stdout.write(`\n<step>${JSON.stringify(feedback)}</step>\n`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalise les options pour l'API Responses
|
|
34
|
+
*
|
|
35
|
+
* Transforme les paramètres du format Chat Completions vers Responses API:
|
|
36
|
+
* - Tools: { type: "function", function: {...} } → { type: "function", name, description, parameters, strict }
|
|
37
|
+
* - Autres transformations futures si nécessaire
|
|
38
|
+
*
|
|
39
|
+
* @param options - Options à normaliser
|
|
40
|
+
* @returns Options normalisées pour Responses API
|
|
41
|
+
*/
|
|
42
|
+
function normalizeOptionsForResponses(options) {
|
|
43
|
+
const normalized = { ...options };
|
|
44
|
+
// Transformer les tools au format Responses API
|
|
45
|
+
if (normalized.tools && normalized.tools.length > 0) {
|
|
46
|
+
normalized.tools = normalized.tools.map((tool) => {
|
|
47
|
+
if (tool.type === 'function' && tool.function) {
|
|
48
|
+
// Format Chat Completions → Responses API
|
|
49
|
+
return {
|
|
50
|
+
type: 'function',
|
|
51
|
+
name: tool.function.name,
|
|
52
|
+
description: tool.function.description,
|
|
53
|
+
parameters: tool.function.parameters,
|
|
54
|
+
strict: tool.function.strict ?? true
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Déjà au bon format ou type différent
|
|
58
|
+
return tool;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return normalized;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Normalise un function call du format Responses vers Chat Completions
|
|
65
|
+
*
|
|
66
|
+
* Responses API: { type: "function_call", name, arguments, call_id }
|
|
67
|
+
* Chat Completions: { id, type: "function", function: { name, arguments } }
|
|
68
|
+
*
|
|
69
|
+
* Si déjà au format Chat Completions (avec .function), retourne tel quel
|
|
70
|
+
*
|
|
71
|
+
* @param functionCall - Function call au format Responses ou Chat Completions
|
|
72
|
+
* @returns Function call au format Chat Completions
|
|
73
|
+
*/
|
|
74
|
+
function normalizedFunctionCallFromResponse(functionCall) {
|
|
75
|
+
// Si déjà au format Chat Completions, retourner tel quel
|
|
76
|
+
if (functionCall.function) {
|
|
77
|
+
return functionCall;
|
|
78
|
+
}
|
|
79
|
+
// Transformer du format Responses vers Chat Completions
|
|
80
|
+
return {
|
|
81
|
+
id: functionCall.call_id,
|
|
82
|
+
type: "function",
|
|
83
|
+
function: {
|
|
84
|
+
name: functionCall.name,
|
|
85
|
+
arguments: functionCall.arguments
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Normalise l'output de l'API Responses vers le format Chat Completions
|
|
91
|
+
*
|
|
92
|
+
* Transforme la structure Responses API vers le format attendu par le reste du code
|
|
93
|
+
*
|
|
94
|
+
* Format Responses API (result.output array):
|
|
95
|
+
* - { type: "reasoning", summary: [] }
|
|
96
|
+
* - { type: "message", content: [{ type: "output_text", text: "..." }] }
|
|
97
|
+
* - { type: "function_call", name, arguments, call_id }
|
|
98
|
+
*
|
|
99
|
+
* Format Chat Completions compatible:
|
|
100
|
+
* - { choices: [{ message: { content, tool_calls, reasoning_text } }], usage: {...}, id: "..." }
|
|
101
|
+
*
|
|
102
|
+
* @param result - Résultat de stream.finalResponse()
|
|
103
|
+
* @returns Résultat au format Chat Completions avec reasoning_text si présent
|
|
104
|
+
*/
|
|
105
|
+
function normalizeOutputFromResponses(result) {
|
|
106
|
+
let text = '';
|
|
107
|
+
let reasoningText = ''; // ✅ Capturer reasoning_text pour modèles reasoning (o-series, gpt-5)
|
|
108
|
+
const functionCalls = [];
|
|
109
|
+
// Parcourir les output items pour extraire text, reasoning et function_calls
|
|
110
|
+
if (result.output && Array.isArray(result.output)) {
|
|
111
|
+
for (const item of result.output) {
|
|
112
|
+
// Function calls sont directement dans output
|
|
113
|
+
if (item.type === 'function_call') {
|
|
114
|
+
functionCalls.push(item);
|
|
115
|
+
}
|
|
116
|
+
// ✅ Capturer reasoning (v5.22.0)
|
|
117
|
+
else if (item.type === 'reasoning' && item.summary) {
|
|
118
|
+
reasoningText = Array.isArray(item.summary) ? item.summary.join('\n') : String(item.summary);
|
|
119
|
+
}
|
|
120
|
+
// Messages contiennent le texte
|
|
121
|
+
else if (item.type === 'message' && item.content) {
|
|
122
|
+
for (const content of item.content) {
|
|
123
|
+
if (content.type === 'output_text' && content.text) {
|
|
124
|
+
text += content.text;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Transformer tous les function_calls vers le format Chat Completions
|
|
131
|
+
const toolCalls = functionCalls.map(normalizedFunctionCallFromResponse);
|
|
132
|
+
// Retourner au format Chat Completions avec reasoning_text optionnel
|
|
133
|
+
return {
|
|
134
|
+
choices: [{
|
|
135
|
+
message: {
|
|
136
|
+
tool_calls: toolCalls,
|
|
137
|
+
content: text,
|
|
138
|
+
reasoning_text: reasoningText || undefined // ✅ Optionnel, seulement si présent
|
|
139
|
+
}
|
|
140
|
+
}],
|
|
141
|
+
usage: {
|
|
142
|
+
prompt_tokens: result.usage?.input_tokens || 0,
|
|
143
|
+
completion_tokens: result.usage?.output_tokens || 0,
|
|
144
|
+
total_tokens: (result.usage?.input_tokens || 0) + (result.usage?.output_tokens || 0)
|
|
145
|
+
},
|
|
146
|
+
id: result.id
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Convertit les messages du stateGraph vers le format Responses API input
|
|
151
|
+
* Gère les messages normaux, tool_calls (Chat Completions), et function_call_output (Responses)
|
|
152
|
+
*
|
|
153
|
+
* Utilisée par:
|
|
154
|
+
* - readCompletionsStream (responses.ts) pour le follow-up après tool calls
|
|
155
|
+
* - executeAgentSet (responses.ts) pour préparer l'input initial
|
|
156
|
+
*
|
|
157
|
+
* @param messages Messages du stateGraph
|
|
158
|
+
* @returns Array d'items au format Responses API input
|
|
159
|
+
*/
|
|
160
|
+
function convertMessagesToResponsesInput(messages) {
|
|
161
|
+
return messages.flatMap((m) => {
|
|
162
|
+
// ✅ Messages function_call_output ont un format différent (pas de role)
|
|
163
|
+
if (m.type === 'function_call_output') {
|
|
164
|
+
return [{ type: m.type, call_id: m.call_id, output: m.output }];
|
|
165
|
+
}
|
|
166
|
+
// ✅ Messages avec tool_calls → convertir vers le format Responses API
|
|
167
|
+
// Documentation: "input_list += response.output" ajoute reasoning + function_calls
|
|
168
|
+
if (m.tool_calls && m.tool_calls.length > 0) {
|
|
169
|
+
const result = [];
|
|
170
|
+
// Ajouter le message assistant si content présent
|
|
171
|
+
if (m.content) {
|
|
172
|
+
result.push({ role: m.role, content: m.content });
|
|
173
|
+
}
|
|
174
|
+
// Convertir chaque tool_call (Chat Completions) en function_call (Responses)
|
|
175
|
+
for (const tc of m.tool_calls) {
|
|
176
|
+
result.push({
|
|
177
|
+
type: "function_call",
|
|
178
|
+
call_id: tc.id,
|
|
179
|
+
name: tc.function.name,
|
|
180
|
+
arguments: tc.function.arguments
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
// Messages normaux avec role
|
|
186
|
+
return [{ role: m.role, content: m.content }];
|
|
187
|
+
});
|
|
188
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -24,7 +24,7 @@ __exportStar(require("./utils"), exports);
|
|
|
24
24
|
__exportStar(require("./types"), exports);
|
|
25
25
|
// StateGraph - nouvelle architecture de gestion des discussions
|
|
26
26
|
__exportStar(require("./stategraph"), exports);
|
|
27
|
-
// Execute
|
|
27
|
+
// Execute (avec feature toggle legacy/responses)
|
|
28
28
|
__exportStar(require("./execute"), exports);
|
|
29
29
|
__exportStar(require("./pricing.llm"), exports);
|
|
30
30
|
// Scrapper
|
|
@@ -8,7 +8,7 @@ type ModelPricing = {
|
|
|
8
8
|
export declare const modelPricing: Record<string, ModelPricing>;
|
|
9
9
|
export declare function calculateCost(model: string, usage?: CompletionUsage): number;
|
|
10
10
|
export declare function accumulateCost(currentUsage: Usage, model: string, usage?: CompletionUsage): number;
|
|
11
|
-
export declare function LLM(openai: any): any;
|
|
11
|
+
export declare function LLM(openai: any, forceThinking?: boolean): any;
|
|
12
12
|
export declare const LLMxai: any;
|
|
13
13
|
/**
|
|
14
14
|
* Get model mapping for OpenAI
|
package/dist/src/pricing.llm.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.modelPricing = {
|
|
|
22
22
|
"gpt-4o-mini-search-preview": { input: 0.0000015, cachedInput: 0.00000075, output: 0.000006 },
|
|
23
23
|
// GPT-5 family
|
|
24
24
|
"gpt-5": { input: 0.00000125, output: 0.00001 },
|
|
25
|
+
"gpt-5.1": { input: 0.00000125, output: 0.00001 },
|
|
25
26
|
"gpt-5-mini": { input: 0.00000025, output: 0.000002 },
|
|
26
27
|
"gpt-5-nano": { input: 0.00000005, output: 0.0000004 },
|
|
27
28
|
"o1": { input: 0.000015, cachedInput: 0.0000075, output: 0.00006 },
|
|
@@ -62,10 +63,14 @@ function accumulateCost(currentUsage, model, usage) {
|
|
|
62
63
|
// depending on the API source, return the correct mapping between ALIAS and destination models and options.
|
|
63
64
|
// - LOW-fast: openai => gpt-5-nano, xAI => grok-4-nano
|
|
64
65
|
// - etc.
|
|
65
|
-
function LLM(openai) {
|
|
66
|
+
function LLM(openai, forceThinking) {
|
|
66
67
|
//
|
|
67
|
-
// Detect provider based on baseURL
|
|
68
|
+
// Detect provider based on baseURL
|
|
68
69
|
const mapping = openai?.baseURL ? LLMmapping[openai?.baseURL] : LLMmapping["default"];
|
|
70
|
+
// FIXME: this is a temporary solution to force reasoning effort to high if thinking is enabled
|
|
71
|
+
if (mapping.reasoning_effort && forceThinking) {
|
|
72
|
+
mapping.reasoning_effort = "high";
|
|
73
|
+
}
|
|
69
74
|
return mapping;
|
|
70
75
|
}
|
|
71
76
|
exports.LLMxai = {
|
|
@@ -79,6 +84,11 @@ exports.LLMxai = {
|
|
|
79
84
|
model: "grok-4-fast-non-reasoning", // Fast non-reasoning model
|
|
80
85
|
stream: true
|
|
81
86
|
},
|
|
87
|
+
"LOW-medium": {
|
|
88
|
+
temperature: 0.2,
|
|
89
|
+
model: "grok-4-fast-non-reasoning", // Fast non-reasoning model
|
|
90
|
+
stream: true
|
|
91
|
+
},
|
|
82
92
|
"MEDIUM-fast": {
|
|
83
93
|
temperature: 0.2,
|
|
84
94
|
model: "grok-4-fast-reasoning", // Fast reasoning model
|
|
@@ -151,20 +161,20 @@ exports.LLMopenai = {
|
|
|
151
161
|
frequency_penalty: 0.0,
|
|
152
162
|
presence_penalty: 0.0,
|
|
153
163
|
model: "gpt-5-nano",
|
|
154
|
-
reasoning_effort: "
|
|
164
|
+
reasoning_effort: "low",
|
|
155
165
|
verbosity: "low",
|
|
156
166
|
stream: true
|
|
157
167
|
},
|
|
158
|
-
"
|
|
168
|
+
"LOW-medium": {
|
|
159
169
|
temperature: 1,
|
|
160
170
|
frequency_penalty: 0.0,
|
|
161
171
|
presence_penalty: 0.0,
|
|
162
|
-
model: "gpt-5-
|
|
163
|
-
reasoning_effort: "
|
|
172
|
+
model: "gpt-5-nano",
|
|
173
|
+
reasoning_effort: "medium",
|
|
164
174
|
verbosity: "low",
|
|
165
175
|
stream: true
|
|
166
176
|
},
|
|
167
|
-
"LOW-
|
|
177
|
+
"LOW-4fast": {
|
|
168
178
|
temperature: .2,
|
|
169
179
|
frequency_penalty: 0.0,
|
|
170
180
|
presence_penalty: 0.0,
|
|
@@ -185,6 +195,15 @@ exports.LLMopenai = {
|
|
|
185
195
|
model: "gpt-4.1",
|
|
186
196
|
stream: true
|
|
187
197
|
},
|
|
198
|
+
"MEDIUM-fast": {
|
|
199
|
+
temperature: 1,
|
|
200
|
+
frequency_penalty: 0.0,
|
|
201
|
+
presence_penalty: 0.0,
|
|
202
|
+
model: "gpt-5-mini",
|
|
203
|
+
reasoning_effort: "minimal",
|
|
204
|
+
verbosity: "low",
|
|
205
|
+
stream: true
|
|
206
|
+
},
|
|
188
207
|
"MEDIUM": {
|
|
189
208
|
temperature: 1,
|
|
190
209
|
frequency_penalty: 0.0,
|
|
@@ -195,14 +214,14 @@ exports.LLMopenai = {
|
|
|
195
214
|
stream: true
|
|
196
215
|
},
|
|
197
216
|
"HIGH-fast": {
|
|
198
|
-
model: "gpt-5",
|
|
199
|
-
reasoning_effort: "
|
|
217
|
+
model: "gpt-5.1",
|
|
218
|
+
reasoning_effort: "none",
|
|
200
219
|
verbosity: "low",
|
|
201
220
|
temperature: 1,
|
|
202
221
|
stream: true
|
|
203
222
|
},
|
|
204
|
-
"HIGH
|
|
205
|
-
model: "gpt-5",
|
|
223
|
+
"HIGH": {
|
|
224
|
+
model: "gpt-5.1",
|
|
206
225
|
reasoning_effort: "low",
|
|
207
226
|
verbosity: "low",
|
|
208
227
|
stream: true
|
|
@@ -214,17 +233,19 @@ exports.LLMopenai = {
|
|
|
214
233
|
stream: true
|
|
215
234
|
},
|
|
216
235
|
"SEARCH": {
|
|
217
|
-
model: "gpt-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
model: "gpt-5-mini",
|
|
237
|
+
reasoning: { effort: "low" },
|
|
238
|
+
tools: [
|
|
239
|
+
{
|
|
240
|
+
type: "web_search_preview",
|
|
241
|
+
user_location: {
|
|
242
|
+
type: "approximate",
|
|
222
243
|
country: "CH",
|
|
223
244
|
city: "Geneva",
|
|
224
245
|
region: "Geneva",
|
|
225
246
|
},
|
|
226
|
-
}
|
|
227
|
-
|
|
247
|
+
}
|
|
248
|
+
],
|
|
228
249
|
},
|
|
229
250
|
};
|
|
230
251
|
const LLMmapping = {
|
|
@@ -190,25 +190,31 @@ class Embeddings {
|
|
|
190
190
|
// Sauvegarder l'ancienne config pour rollback en cas d'erreur
|
|
191
191
|
const oldConfig = this.config;
|
|
192
192
|
const oldVectorsFile = this.vectorsFile;
|
|
193
|
+
console.log(`\n 🔧 Embeddings.update() appelé`);
|
|
194
|
+
console.log(` Ancien baseDir: ${oldConfig.baseDir}`);
|
|
195
|
+
console.log(` Nouveau baseDir: ${newConfig.baseDir}`);
|
|
193
196
|
try {
|
|
194
197
|
// Mettre à jour la configuration
|
|
195
198
|
this.config = newConfig;
|
|
196
199
|
this.vectorsFile = path_1.default.join(newConfig.baseDir, types_1.RAG_FILES.VECTORS);
|
|
197
200
|
this.space = newConfig.dimensions || 1536;
|
|
198
201
|
this.distance = newConfig.distance || 'cosine';
|
|
202
|
+
console.log(` Nouveau vectorsFile: ${this.vectorsFile}`);
|
|
203
|
+
console.log(` Fichier existe: ${(0, fs_1.existsSync)(this.vectorsFile)}`);
|
|
199
204
|
// Recharger l'index si ce n'est pas en mode inmemory
|
|
200
205
|
if (!this.inmemory) {
|
|
201
206
|
// Vérifier que les fichiers existent
|
|
202
207
|
if (!(0, fs_1.existsSync)(this.vectorsFile)) {
|
|
203
208
|
throw new Error(`Fichier d'index manquant: ${this.vectorsFile}`);
|
|
204
209
|
}
|
|
210
|
+
console.log(` 📥 Rechargement de l'index...`);
|
|
205
211
|
// Recharger l'index
|
|
206
212
|
this.loadIndex();
|
|
213
|
+
console.log(` 📥 Rechargement des métadonnées et mapping...`);
|
|
207
214
|
// Recharger les métadonnées et le mapping
|
|
208
215
|
this.loadMetadata();
|
|
209
216
|
this.loadMapping();
|
|
210
|
-
|
|
211
|
-
console.log(`✅ Embedding mis à jour avec nouvelle config: ${newConfig.baseDir}`);
|
|
217
|
+
console.log(` ✅ Embedding mis à jour avec nouvelle config: ${newConfig.baseDir}`);
|
|
212
218
|
}
|
|
213
219
|
}
|
|
214
220
|
catch (error) {
|
|
@@ -231,31 +231,40 @@ class RAGManager {
|
|
|
231
231
|
*/
|
|
232
232
|
notifyUpdate(name, opts) {
|
|
233
233
|
const embeddingData = this.loadedEmbeddings.get(name);
|
|
234
|
+
console.log(`\n🔔 notifyUpdate('${name}') appelé`);
|
|
235
|
+
console.log(` Embedding en cache: ${embeddingData ? 'OUI' : 'NON'}`);
|
|
236
|
+
// Si l'embedding n'est pas encore chargé, rien à faire
|
|
237
|
+
// Il sera chargé avec la nouvelle config au prochain load()
|
|
234
238
|
if (!embeddingData) {
|
|
235
|
-
|
|
236
|
-
|
|
239
|
+
console.log(` ⏭️ Skip: sera chargé avec la nouvelle config au prochain load()`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
// Charger la nouvelle configuration depuis le registre
|
|
243
|
+
const entry = this.getEntry(name);
|
|
244
|
+
if (!entry) {
|
|
245
|
+
console.log(` ⚠️ Entry '${name}' introuvable dans le registre`);
|
|
237
246
|
return;
|
|
238
247
|
}
|
|
248
|
+
console.log(` 📁 entry.configPath: ${entry.configPath}`);
|
|
249
|
+
// L'embedding est chargé, forcer son rechargement via update()
|
|
250
|
+
// update() modifie l'instance EN PLACE, donc tous ceux qui ont une référence voient la nouvelle version
|
|
239
251
|
const { embedding } = embeddingData;
|
|
240
252
|
try {
|
|
241
|
-
// Charger la nouvelle configuration
|
|
242
|
-
const entry = this.getEntry(name);
|
|
243
|
-
if (!entry) {
|
|
244
|
-
throw new Error(`RAG '${name}' n'existe plus dans le registre`);
|
|
245
|
-
}
|
|
246
253
|
const newConfig = this.loadConfig(entry.configPath);
|
|
254
|
+
console.log(` 🔄 Appel de embedding.update() avec baseDir: ${newConfig.baseDir}`);
|
|
247
255
|
// FIXME: Vérifier si l'embedding est busy avant de faire l'update
|
|
248
|
-
//
|
|
256
|
+
// update() va recharger l'index, les métadonnées et le mapping depuis newConfig.baseDir
|
|
257
|
+
// update() modifie l'instance en place, donc tous les utilisateurs voient la nouvelle version
|
|
249
258
|
embedding.update(newConfig);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
console.log(`✅ Embedding '${name}' notifié de la mise à jour (${action})`);
|
|
255
|
-
}
|
|
259
|
+
// Mettre à jour le timestamp du cache
|
|
260
|
+
embeddingData.loadTime = Date.now();
|
|
261
|
+
console.log(` ✅ Embedding rechargé avec succès`);
|
|
262
|
+
console.log(` ✅ embedding.isReady(): ${embedding.isReady()}`);
|
|
256
263
|
}
|
|
257
264
|
catch (error) {
|
|
258
|
-
console.error(
|
|
265
|
+
console.error(` ❌ Erreur lors de la mise à jour de l'embedding '${name}':`, error);
|
|
266
|
+
// En cas d'erreur, update() a déjà fait un rollback de sa propre config
|
|
267
|
+
// L'instance continue de fonctionner avec l'ancienne config
|
|
259
268
|
// Ne pas throw pour éviter de bloquer le flow
|
|
260
269
|
}
|
|
261
270
|
}
|
|
@@ -777,6 +786,9 @@ class RAGManager {
|
|
|
777
786
|
this.cachedRegistry = { ...registry };
|
|
778
787
|
this.saveRegistry(registry);
|
|
779
788
|
console.log(`✅ RAG '${name}' construit avec succès`);
|
|
789
|
+
console.log(` 📁 configPath final: ${entry.configPath}`);
|
|
790
|
+
console.log(` 📦 Embeddings chargés en cache: ${this.loadedEmbeddings.size}`);
|
|
791
|
+
console.log(` 🔍 Embedding '${name}' dans le cache: ${this.loadedEmbeddings.has(name) ? 'OUI' : 'NON'}`);
|
|
780
792
|
// Notifier les embeddings chargés de se mettre à jour
|
|
781
793
|
this.notifyUpdate(name);
|
|
782
794
|
}
|
|
@@ -82,15 +82,34 @@ class GitE2EHelper {
|
|
|
82
82
|
await fs_1.promises.mkdir(uploadDir, { recursive: true });
|
|
83
83
|
// Initialiser le repository Git
|
|
84
84
|
await this.git.init();
|
|
85
|
+
// FIXME: remove this hack that wait for the .git directory to be created
|
|
86
|
+
// ✅ Attendre que le répertoire .git soit complètement créé
|
|
87
|
+
const gitDir = (0, path_1.join)(this.tempRepoPath, '.git');
|
|
88
|
+
// let attempts = 0;
|
|
89
|
+
// while (!existsSync(gitDir) && attempts < 50) {
|
|
90
|
+
// await new Promise(resolve => setTimeout(resolve, 10));
|
|
91
|
+
// attempts++;
|
|
92
|
+
// }
|
|
93
|
+
if (!(0, fs_1.existsSync)(gitDir)) {
|
|
94
|
+
throw new Error('.git directory not created after git init');
|
|
95
|
+
}
|
|
85
96
|
// Configuration Git de base
|
|
86
97
|
await this.git.addConfig('user.name', 'E2E Test');
|
|
87
98
|
await this.git.addConfig('user.email', 'e2e@test.com');
|
|
88
99
|
await this.git.addConfig('init.defaultBranch', this.config.mainBranch);
|
|
89
100
|
// Créer la branche main directement
|
|
90
101
|
await this.git.checkout(['-b', this.config.mainBranch]);
|
|
91
|
-
// Créer un commit initial
|
|
102
|
+
// Créer un commit initial avec un README.md valide (avec front-matter pour les tests withID)
|
|
92
103
|
const readmePath = (0, path_1.join)(this.tempRepoPath, 'README.md');
|
|
93
|
-
|
|
104
|
+
const readmeContent = `---
|
|
105
|
+
id: 1000
|
|
106
|
+
title: E2E Test Repository
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# E2E Test Repository
|
|
110
|
+
|
|
111
|
+
Temporary repository for E2E testing.`;
|
|
112
|
+
await fs_1.promises.writeFile(readmePath, readmeContent);
|
|
94
113
|
await this.git.add('README.md');
|
|
95
114
|
await this.git.commit('Initial commit');
|
|
96
115
|
// Créer la branche draft à partir de main
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RulesGitConfig, GitHealthStatus } from '../types';
|
|
1
|
+
import { RulesGitConfig, GitHealthStatus, GitPrNoteMigrationReport } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Rapport d'analyse des IDs
|
|
4
4
|
*/
|
|
@@ -73,8 +73,10 @@ export declare class GitHealthManager {
|
|
|
73
73
|
* - Notes orphelines sur anciens commits → copie vers HEAD + suppression
|
|
74
74
|
* - Branches sans historique de notes → ignore silencieusement
|
|
75
75
|
* - Erreurs de traitement par branche → continue avec les autres
|
|
76
|
+
*
|
|
77
|
+
* @returns GitPrNoteMigrationReport avec le nombre de notes migrées, déjà OK, et perdues
|
|
76
78
|
*/
|
|
77
|
-
migrateNotes(): Promise<
|
|
79
|
+
migrateNotes(): Promise<GitPrNoteMigrationReport>;
|
|
78
80
|
/**
|
|
79
81
|
* Analyse les IDs des documents Markdown dans une branche
|
|
80
82
|
*
|
|
@@ -31,8 +31,7 @@ class GitHealthManager {
|
|
|
31
31
|
await this.git.checkout(branch);
|
|
32
32
|
}
|
|
33
33
|
catch (checkoutError) {
|
|
34
|
-
// Si checkout normal échoue,
|
|
35
|
-
console.log(`🔧 Checkout forcé vers ${branch}...`, checkoutError);
|
|
34
|
+
// Si checkout normal échoue, continuer (forceRepair gérera l'erreur)
|
|
36
35
|
}
|
|
37
36
|
try {
|
|
38
37
|
// Use the robust health diagnostic function
|
|
@@ -142,12 +141,14 @@ class GitHealthManager {
|
|
|
142
141
|
const draftHealth = await (0, git_1.gitGetBranchHealth)(this.git, draftBranch);
|
|
143
142
|
results.push(draftHealth);
|
|
144
143
|
this.displayHealthSummary(draftHealth);
|
|
145
|
-
// Check all validation branches
|
|
144
|
+
// Check all validation branches with optimized function
|
|
146
145
|
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
147
146
|
const validationBranches = allBranches.filter(b => b.startsWith(validationPrefix));
|
|
148
147
|
if (validationBranches.length > 0) {
|
|
148
|
+
console.log(`🔍 Diagnostic optimisé de ${validationBranches.length} branche(s) de validation...`);
|
|
149
149
|
for (const branch of validationBranches) {
|
|
150
|
-
|
|
150
|
+
// ✅ OPTIMISATION: Utiliser la version légère pour les validations
|
|
151
|
+
const health = await (0, git_1.gitGetValidationBranchHealth)(this.git, branch, this.config);
|
|
151
152
|
results.push(health);
|
|
152
153
|
this.displayHealthSummary(health);
|
|
153
154
|
}
|
|
@@ -294,20 +295,23 @@ class GitHealthManager {
|
|
|
294
295
|
* - Notes orphelines sur anciens commits → copie vers HEAD + suppression
|
|
295
296
|
* - Branches sans historique de notes → ignore silencieusement
|
|
296
297
|
* - Erreurs de traitement par branche → continue avec les autres
|
|
298
|
+
*
|
|
299
|
+
* @returns GitPrNoteMigrationReport avec le nombre de notes migrées, déjà OK, et perdues
|
|
297
300
|
*/
|
|
298
301
|
async migrateNotes() {
|
|
299
302
|
const { mainBranch, validationPrefix, gitNotes } = this.config;
|
|
300
303
|
if (!validationPrefix || !gitNotes.namespace) {
|
|
301
304
|
console.error("Erreur: Le préfixe de validation ou le namespace des notes n'est pas configuré.");
|
|
302
|
-
return;
|
|
305
|
+
return { migrated: 0, alreadyOk: 0, lost: [] };
|
|
303
306
|
}
|
|
304
307
|
const allBranches = await (0, git_1.gitGetAllBranches)(this.git);
|
|
305
308
|
const validationBranches = allBranches.filter(b => b.startsWith(validationPrefix));
|
|
306
309
|
if (validationBranches.length === 0) {
|
|
307
|
-
return;
|
|
310
|
+
return { migrated: 0, alreadyOk: 0, lost: [] };
|
|
308
311
|
}
|
|
309
312
|
let migratedCount = 0;
|
|
310
313
|
let alreadyOkCount = 0;
|
|
314
|
+
const lostNotes = [];
|
|
311
315
|
for (const branch of validationBranches) {
|
|
312
316
|
try {
|
|
313
317
|
const lastCommit = (await this.git.revparse(branch)).trim();
|
|
@@ -325,12 +329,20 @@ class GitHealthManager {
|
|
|
325
329
|
}
|
|
326
330
|
const revListOutput = await this.git.raw('rev-list', `${mergeBase}..${branch}`);
|
|
327
331
|
const branchCommits = revListOutput.split('\n').filter(Boolean);
|
|
332
|
+
// Extract PR number from branch name (e.g., rule-validation-31 -> 31)
|
|
333
|
+
const prNumber = parseInt(branch.split('-').pop() || '0', 10);
|
|
328
334
|
let oldNoteCommit = null;
|
|
329
335
|
for (const commitHash of branchCommits) {
|
|
330
336
|
const note = await (0, git_1.gitReadNote)(this.git, commitHash, gitNotes.namespace, 1);
|
|
331
337
|
if (note) {
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
// CRITICAL FIX: Verify the note belongs to this PR before migrating
|
|
339
|
+
if (note.id === prNumber) {
|
|
340
|
+
oldNoteCommit = commitHash;
|
|
341
|
+
break; // Found the correct note for this PR
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
console.log(` ⚠️ ${branch}: Note trouvée avec ID=${note.id}, attendu ID=${prNumber} - ignorée`);
|
|
345
|
+
}
|
|
334
346
|
}
|
|
335
347
|
}
|
|
336
348
|
// 3. If a note was found on an old commit, migrate it
|
|
@@ -341,6 +353,11 @@ class GitHealthManager {
|
|
|
341
353
|
console.log(`✅ ${branch}: Migration terminée`);
|
|
342
354
|
migratedCount++;
|
|
343
355
|
}
|
|
356
|
+
else {
|
|
357
|
+
// CRITICAL: Note perdue pour cette validation
|
|
358
|
+
console.error(`🚨 ${branch}: NOTE PERDUE - PR ${prNumber} n'a plus de note dans son historique`);
|
|
359
|
+
lostNotes.push({ branch, prNumber });
|
|
360
|
+
}
|
|
344
361
|
}
|
|
345
362
|
catch (error) {
|
|
346
363
|
console.error(`❌ ${branch}: Erreur lors de la migration:`, error);
|
|
@@ -348,9 +365,14 @@ class GitHealthManager {
|
|
|
348
365
|
}
|
|
349
366
|
}
|
|
350
367
|
// Summary only if there was work to do
|
|
351
|
-
if (migratedCount > 0 || alreadyOkCount !== validationBranches.length) {
|
|
352
|
-
console.log(`✅ Migration des notes terminée: ${migratedCount} migrées, ${alreadyOkCount} déjà OK`);
|
|
368
|
+
if (migratedCount > 0 || alreadyOkCount !== validationBranches.length || lostNotes.length > 0) {
|
|
369
|
+
console.log(`✅ Migration des notes terminée: ${migratedCount} migrées, ${alreadyOkCount} déjà OK${lostNotes.length > 0 ? `, ${lostNotes.length} perdues 🚨` : ''}`);
|
|
353
370
|
}
|
|
371
|
+
return {
|
|
372
|
+
migrated: migratedCount,
|
|
373
|
+
alreadyOk: alreadyOkCount,
|
|
374
|
+
lost: lostNotes
|
|
375
|
+
};
|
|
354
376
|
}
|
|
355
377
|
/**
|
|
356
378
|
* Analyse les IDs des documents Markdown dans une branche
|
|
@@ -428,9 +450,9 @@ class GitHealthManager {
|
|
|
428
450
|
else {
|
|
429
451
|
hasValidID = true;
|
|
430
452
|
// Enregistrer l'ID existant dans le registry
|
|
431
|
-
// ✅
|
|
453
|
+
// ✅ gitRegisterExistingID() appelle maintenant setMatterCache() automatiquement si matter est fourni
|
|
432
454
|
try {
|
|
433
|
-
(0, git_1.gitRegisterExistingID)(matter
|
|
455
|
+
(0, git_1.gitRegisterExistingID)(matter, branchName, file, this.config);
|
|
434
456
|
}
|
|
435
457
|
catch (error) {
|
|
436
458
|
if (error.code === 'id_already_used') {
|
|
@@ -548,9 +570,25 @@ class GitHealthManager {
|
|
|
548
570
|
// Parser le matter complet
|
|
549
571
|
const parsed = (0, utils_matter_1.matterParse)(fileData.content);
|
|
550
572
|
const { matter: fullMatter, content } = parsed;
|
|
551
|
-
// Générer un nouvel ID
|
|
552
|
-
fullMatter.id
|
|
553
|
-
|
|
573
|
+
// Générer un nouvel ID si manquant
|
|
574
|
+
if (!fullMatter.id || typeof fullMatter.id !== 'number' || fullMatter.id <= 999) {
|
|
575
|
+
fullMatter.id = (0, git_1.gitGenerateNextID)(this.config);
|
|
576
|
+
console.log(` 🔧 ${file}: Génération d'un nouvel ID: ${fullMatter.id}`);
|
|
577
|
+
}
|
|
578
|
+
// Générer un titre si manquant (basé sur le nom du fichier ou le premier titre de section)
|
|
579
|
+
if (!fullMatter.title || fullMatter.title.trim() === '') {
|
|
580
|
+
// Essayer d'extraire le premier titre de section (# Title)
|
|
581
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
582
|
+
if (titleMatch) {
|
|
583
|
+
fullMatter.title = titleMatch[1].trim();
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
// Sinon, utiliser le nom du fichier sans extension
|
|
587
|
+
const fileName = file.replace(/\.md$/, '').replace(/[-_]/g, ' ');
|
|
588
|
+
fullMatter.title = fileName.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
589
|
+
}
|
|
590
|
+
console.log(` 🔧 ${file}: Génération d'un titre: ${fullMatter.title}`);
|
|
591
|
+
}
|
|
554
592
|
// Re-sérialiser et sauvegarder
|
|
555
593
|
const updatedContent = (0, utils_matter_1.matterSerialize)(content, fullMatter);
|
|
556
594
|
// ✅ IMPORTANT: Désactiver withID car l'ID est déjà généré et enregistré
|
|
@@ -618,6 +656,10 @@ class GitHealthManager {
|
|
|
618
656
|
if (autoFix && (report.missingID.length > 0 || report.missingTitle.length > 0 || report.invalidID.length > 0 || report.duplicateID.length > 0)) {
|
|
619
657
|
finalReport = await this.repairSafeIDs(report, { dryRun });
|
|
620
658
|
}
|
|
659
|
+
// Calculer le nombre de fichiers réparés
|
|
660
|
+
// C'est la différence entre les problèmes avant et après réparation
|
|
661
|
+
const fixed = (report.missingID.length + report.missingTitle.length + report.invalidID.length + report.duplicateID.length) -
|
|
662
|
+
(finalReport.missingID.length + finalReport.missingTitle.length + finalReport.invalidID.length + finalReport.duplicateID.length);
|
|
621
663
|
// Retourner dans l'ancien format pour compatibilité
|
|
622
664
|
return {
|
|
623
665
|
scanned: finalReport.scanned,
|
|
@@ -626,7 +668,7 @@ class GitHealthManager {
|
|
|
626
668
|
missingTitle: finalReport.missingTitle.length,
|
|
627
669
|
invalidID: finalReport.invalidID.length,
|
|
628
670
|
duplicateID: finalReport.duplicateID.length,
|
|
629
|
-
fixed:
|
|
671
|
+
fixed: Math.max(0, fixed), // S'assurer que fixed n'est pas négatif
|
|
630
672
|
errors: finalReport.errors
|
|
631
673
|
};
|
|
632
674
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
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, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
2
|
+
export { gitInit, gitEnsureRepositoryConfiguration, gitEnsureRemoteConfiguration, gitSetupRepository, gitShowConfiguration, gitCheckConfiguration, gitGetBranchHealth, gitGetValidationBranchHealth, gitCreateOrEditFile, gitEditFile, gitRenameFile, gitDeleteFile, gitGenerateNextID, gitReloadIDRegistry, gitRegisterExistingID, gitEnsureMatterID, gitFileStrictMatter, gitIDRegistryExists, gitIDRegistryRename, } from './repo';
|
|
3
3
|
export { gitSyncPR, gitIsPRClosed, gitIsPRClosedRobust, gitGetPRMetadata, gitGetAllPR, gitGetClosedPRs, gitLoadPR, gitPRUpdateComments, gitClosePR, gitClosePRRobust, gitGetNextPRNumber, gitNewValidationRequest, gitNewPR, } from './repo.pr';
|
|
4
4
|
export * from './git.e2e.helper';
|
|
5
5
|
export * from './git.health';
|