agentic-api 2.0.314 → 2.0.585

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +37 -34
  2. package/dist/src/agents/prompts.d.ts +1 -1
  3. package/dist/src/agents/prompts.js +9 -7
  4. package/dist/src/agents/reducer.core.js +2 -2
  5. package/dist/src/agents/simulator.d.ts +33 -4
  6. package/dist/src/agents/simulator.dashboard.d.ts +140 -0
  7. package/dist/src/agents/simulator.dashboard.js +344 -0
  8. package/dist/src/agents/simulator.executor.d.ts +9 -3
  9. package/dist/src/agents/simulator.executor.js +43 -17
  10. package/dist/src/agents/simulator.js +103 -19
  11. package/dist/src/agents/simulator.prompts.d.ts +9 -8
  12. package/dist/src/agents/simulator.prompts.js +68 -62
  13. package/dist/src/agents/simulator.types.d.ts +39 -4
  14. package/dist/src/agents/simulator.utils.d.ts +22 -1
  15. package/dist/src/agents/simulator.utils.js +27 -2
  16. package/dist/src/execute/helpers.d.ts +75 -0
  17. package/dist/src/execute/helpers.js +139 -0
  18. package/dist/src/execute/index.d.ts +11 -0
  19. package/dist/src/execute/index.js +44 -0
  20. package/dist/src/execute/legacy.d.ts +46 -0
  21. package/dist/src/{execute.js → execute/legacy.js} +130 -232
  22. package/dist/src/execute/modelconfig.d.ts +29 -0
  23. package/dist/src/execute/modelconfig.js +72 -0
  24. package/dist/src/execute/responses.d.ts +55 -0
  25. package/dist/src/execute/responses.js +595 -0
  26. package/dist/src/execute/shared.d.ts +83 -0
  27. package/dist/src/execute/shared.js +188 -0
  28. package/dist/src/index.d.ts +5 -1
  29. package/dist/src/index.js +21 -2
  30. package/dist/src/llm/config.d.ts +25 -0
  31. package/dist/src/llm/config.js +38 -0
  32. package/dist/src/llm/index.d.ts +48 -0
  33. package/dist/src/llm/index.js +115 -0
  34. package/dist/src/llm/openai.d.ts +6 -0
  35. package/dist/src/llm/openai.js +154 -0
  36. package/dist/src/llm/pricing.d.ts +26 -0
  37. package/dist/src/llm/pricing.js +129 -0
  38. package/dist/src/llm/xai.d.ts +17 -0
  39. package/dist/src/llm/xai.js +90 -0
  40. package/dist/src/pricing.llm.d.ts +3 -15
  41. package/dist/src/pricing.llm.js +10 -230
  42. package/dist/src/prompts.d.ts +0 -1
  43. package/dist/src/prompts.js +51 -118
  44. package/dist/src/rag/embeddings.d.ts +5 -1
  45. package/dist/src/rag/embeddings.js +23 -7
  46. package/dist/src/rag/parser.js +1 -1
  47. package/dist/src/rag/rag.manager.d.ts +33 -2
  48. package/dist/src/rag/rag.manager.js +159 -61
  49. package/dist/src/rag/types.d.ts +2 -0
  50. package/dist/src/rag/usecase.js +8 -11
  51. package/dist/src/rules/git/git.e2e.helper.js +21 -2
  52. package/dist/src/rules/git/git.health.d.ts +4 -2
  53. package/dist/src/rules/git/git.health.js +113 -16
  54. package/dist/src/rules/git/index.d.ts +1 -1
  55. package/dist/src/rules/git/index.js +3 -2
  56. package/dist/src/rules/git/repo.d.ts +57 -7
  57. package/dist/src/rules/git/repo.js +326 -39
  58. package/dist/src/rules/git/repo.pr.d.ts +8 -0
  59. package/dist/src/rules/git/repo.pr.js +161 -13
  60. package/dist/src/rules/git/repo.tools.d.ts +5 -1
  61. package/dist/src/rules/git/repo.tools.js +54 -7
  62. package/dist/src/rules/types.d.ts +25 -0
  63. package/dist/src/rules/utils.matter.d.ts +0 -20
  64. package/dist/src/rules/utils.matter.js +58 -81
  65. package/dist/src/scrapper.js +3 -2
  66. package/dist/src/stategraph/stategraph.d.ts +26 -1
  67. package/dist/src/stategraph/stategraph.js +43 -2
  68. package/dist/src/stategraph/stategraph.storage.js +4 -0
  69. package/dist/src/stategraph/types.d.ts +5 -0
  70. package/dist/src/types.d.ts +42 -7
  71. package/dist/src/types.js +8 -7
  72. package/dist/src/usecase.js +1 -1
  73. package/dist/src/utils.d.ts +0 -8
  74. package/dist/src/utils.js +26 -29
  75. package/package.json +9 -7
  76. package/dist/src/execute.d.ts +0 -63
@@ -40,6 +40,7 @@ exports.gitGetPRMetadata = gitGetPRMetadata;
40
40
  exports.gitGetAllPR = gitGetAllPR;
41
41
  exports.gitGetClosedPRs = gitGetClosedPRs;
42
42
  exports.gitLoadPR = gitLoadPR;
43
+ exports.gitPRReplaceFile = gitPRReplaceFile;
43
44
  exports.gitPRUpdateComments = gitPRUpdateComments;
44
45
  exports.gitClosePR = gitClosePR;
45
46
  exports.gitClosePRRobust = gitClosePRRobust;
@@ -321,7 +322,7 @@ async function gitLoadPR(git, branch) {
321
322
  if (!metadata) {
322
323
  throw new errors_1.GitOperationError(`PR not found for branch ${branch}`, 'pr_load', { branch });
323
324
  }
324
- const files = metadata.files || await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md');
325
+ const files = metadata.files || (await (0, repo_tools_1.gitGetDiffFiles)(git, branch, metadata?.mergeBase, '.md'));
325
326
  // Récupérer les infos du dernier commit de la branche
326
327
  const log = await git.log({ from: branch, to: branch, maxCount: 1 });
327
328
  const lastCommit = log.latest;
@@ -341,6 +342,49 @@ async function gitLoadPR(git, branch) {
341
342
  throw new errors_1.GitOperationError(`Failed to retrieve PR: ${error}`, 'pr_load');
342
343
  }
343
344
  }
345
+ /**
346
+ * Remplace explicitement un nom de fichier dans les métadonnées d'une PR.
347
+ * Utilisé pour refléter immédiatement les renames sans dépendre de git diff.
348
+ */
349
+ async function gitPRReplaceFile(git, branch, options = {}, config) {
350
+ const { remove, add } = options;
351
+ if (!remove && !add) {
352
+ return;
353
+ }
354
+ const gitConf = (0, repo_tools_1.gitLoad)(config);
355
+ const metadata = await gitGetPRMetadata(git, branch, config);
356
+ if (!metadata) {
357
+ return;
358
+ }
359
+ const currentFiles = Array.isArray(metadata.files) ? metadata.files : [];
360
+ const seen = new Set();
361
+ const nextFiles = [];
362
+ for (const file of currentFiles) {
363
+ if (!file) {
364
+ continue;
365
+ }
366
+ if (remove && file === remove) {
367
+ continue;
368
+ }
369
+ if (seen.has(file)) {
370
+ continue;
371
+ }
372
+ seen.add(file);
373
+ nextFiles.push(file);
374
+ }
375
+ if (add && !seen.has(add)) {
376
+ nextFiles.push(add);
377
+ seen.add(add);
378
+ }
379
+ const changed = currentFiles.length !== nextFiles.length ||
380
+ currentFiles.some((file, idx) => file !== nextFiles[idx]);
381
+ if (!changed) {
382
+ return;
383
+ }
384
+ metadata.files = nextFiles;
385
+ const headCommitHash = (await git.revparse([branch])).trim();
386
+ await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, metadata, gitConf.gitNotes.namespace);
387
+ }
344
388
  async function gitPRUpdateComments(git, branch, details, config) {
345
389
  const gitConf = (0, repo_tools_1.gitLoad)(config);
346
390
  const metadata = await gitGetPRMetadata(git, branch, config);
@@ -407,6 +451,21 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
407
451
  if (!originalMetadata) {
408
452
  throw new errors_1.PRClosureError(`Could not find metadata on branch ${branch}. Cannot close.`, branch, 'metadata_not_found');
409
453
  }
454
+ // 2. Nettoyer tout état de merge en cours avant de commencer
455
+ try {
456
+ await git.raw(['merge', '--abort']).catch(() => {
457
+ // Ignorer si pas de merge en cours
458
+ });
459
+ // Reset de l'index au cas où il serait dans un état instable
460
+ await git.raw(['reset', '--merge']).catch(() => {
461
+ // Ignorer si pas nécessaire
462
+ });
463
+ }
464
+ catch (cleanupError) {
465
+ if (gitConf.verbose) {
466
+ console.warn('Cleanup before merge failed (non-critical):', cleanupError);
467
+ }
468
+ }
410
469
  // 3. Update the metadata note on the PR branch itself to mark it as closed
411
470
  const finalMetadata = {
412
471
  ...originalMetadata,
@@ -420,32 +479,106 @@ async function gitClosePRRobust(git, branch, closedBy, message, config) {
420
479
  await (0, repo_tools_1.gitWriteNote)(git, headCommitHash, finalMetadata, gitConf.gitNotes.namespace);
421
480
  // 4. Now, merge the updated PR branch into the target branch
422
481
  await git.checkout(originalMetadata.mergeBase || gitConf.draftBranch);
423
- const mergeResult = await git
424
- .env({
425
- GIT_AUTHOR_NAME: closedBy.name,
426
- GIT_AUTHOR_EMAIL: closedBy.email,
427
- GIT_COMMITTER_NAME: closedBy.name,
428
- GIT_COMMITTER_EMAIL: closedBy.email,
429
- })
430
- .merge([
431
- '--no-ff', '-X', 'theirs', '-m', `Merge branch '${branch}'`, branch
432
- ]);
482
+ // 5. Stratégie de merge robuste : FORCER la préservation des changements de la branche PR
483
+ // Note: "ours" dans ce contexte signifie "garder la version de la branche qu'on merge" (la PR)
484
+ // car nous sommes sur rule-editor et on merge la PR
485
+ let mergeResult;
486
+ try {
487
+ mergeResult = await git
488
+ .env({
489
+ GIT_AUTHOR_NAME: closedBy.name,
490
+ GIT_AUTHOR_EMAIL: closedBy.email,
491
+ GIT_COMMITTER_NAME: closedBy.name,
492
+ GIT_COMMITTER_EMAIL: closedBy.email,
493
+ })
494
+ .merge([
495
+ '--no-ff',
496
+ '--strategy', 'recursive',
497
+ '--strategy-option', 'theirs',
498
+ '-m', `Merge branch '${branch}'`,
499
+ branch
500
+ ]);
501
+ }
502
+ catch (mergeError) {
503
+ if (gitConf.verbose) {
504
+ console.error('Merge failed with recursive/theirs, trying ours strategy:', mergeError);
505
+ }
506
+ // Nettoyer le merge échoué
507
+ await git.raw(['merge', '--abort']).catch(() => { });
508
+ // Stratégie de fallback ultra-agressive: stratégie "ours" qui prend TOUJOURS la version de la branche source
509
+ // ATTENTION: Ici "ours" = la branche de destination (rule-editor), donc on inverse la logique
510
+ // Solution: utiliser --strategy-option theirs avec merge forcé
511
+ try {
512
+ // Récupérer les fichiers de la PR
513
+ const prFiles = originalMetadata.files || [];
514
+ // Merger avec stratégie ours d'abord (pour créer le commit de merge)
515
+ await git
516
+ .env({
517
+ GIT_AUTHOR_NAME: closedBy.name,
518
+ GIT_AUTHOR_EMAIL: closedBy.email,
519
+ GIT_COMMITTER_NAME: closedBy.name,
520
+ GIT_COMMITTER_EMAIL: closedBy.email,
521
+ })
522
+ .merge([
523
+ '--no-ff',
524
+ '--strategy', 'ours',
525
+ '-m', `Merge branch '${branch}'`,
526
+ branch
527
+ ]);
528
+ // Puis checkout des fichiers depuis la branche PR pour forcer leurs contenus
529
+ for (const file of prFiles) {
530
+ try {
531
+ await git.raw(['checkout', branch, '--', file]);
532
+ }
533
+ catch (fileError) {
534
+ if (gitConf.verbose) {
535
+ console.warn(`Could not checkout ${file} from ${branch}:`, fileError);
536
+ }
537
+ }
538
+ }
539
+ // Amend le commit de merge avec les bons contenus
540
+ await git.commit('--amend', ['--no-edit'], {
541
+ '--author': `${closedBy.name} <${closedBy.email}>`
542
+ });
543
+ mergeResult = { summary: { changes: prFiles.length } };
544
+ }
545
+ catch (fallbackError) {
546
+ throw new errors_1.PRClosureError(`Failed to merge PR ${branch} with all strategies: ${mergeError}\nFallback error: ${fallbackError}`, branch, 'merge_failed');
547
+ }
548
+ }
433
549
  const result = {
434
550
  metadata: finalMetadata,
435
551
  failed: false,
436
- conflicts: mergeResult.conflicts,
552
+ conflicts: mergeResult?.conflicts || [],
437
553
  raw: mergeResult,
438
554
  };
439
555
  return result;
440
556
  }
441
557
  catch (error) {
558
+ // Nettoyer l'état en cas d'erreur
559
+ try {
560
+ await git.raw(['merge', '--abort']).catch(() => { });
561
+ await git.raw(['reset', '--merge']).catch(() => { });
562
+ }
563
+ catch (cleanupError) {
564
+ if (gitConf.verbose) {
565
+ console.error('Failed to cleanup merge state:', cleanupError);
566
+ }
567
+ }
442
568
  if (error instanceof errors_1.PRClosureError)
443
569
  throw error;
444
570
  throw new errors_1.PRClosureError(`Failed to close PR ${branch}: ${error}`, branch, 'unknown');
445
571
  }
446
572
  finally {
447
573
  if (currentBranch) {
448
- await git.checkout(currentBranch);
574
+ try {
575
+ await git.checkout(currentBranch);
576
+ }
577
+ catch (checkoutError) {
578
+ if (gitConf.verbose) {
579
+ console.error(`Failed to checkout back to ${currentBranch}:`, checkoutError);
580
+ }
581
+ }
449
582
  }
450
583
  (0, repo_tools_1.unlock)('checkout');
451
584
  }
@@ -506,6 +639,21 @@ async function gitNewValidationRequest(git, files, description, author, options
506
639
  throw new errors_1.PRCreationError('Author is required', undefined, files);
507
640
  }
508
641
  try {
642
+ // ✅ CLEANUP: Nettoyer tout état de merge corrompu avant de créer la branche
643
+ // Cela évite l'erreur "you need to resolve your current index first"
644
+ try {
645
+ await git.raw(['merge', '--abort']).catch(() => {
646
+ // Ignorer si pas de merge en cours
647
+ });
648
+ await git.raw(['reset', '--merge']).catch(() => {
649
+ // Ignorer si pas nécessaire
650
+ });
651
+ }
652
+ catch (cleanupError) {
653
+ if (gitConfig.verbose) {
654
+ console.warn('Cleanup before branch creation failed (non-critical):', cleanupError);
655
+ }
656
+ }
509
657
  const nextID = await gitGetNextPRNumber(git, validationBranchPrefix);
510
658
  const newBranchName = `${validationBranchPrefix}${nextID}`;
511
659
  await git.checkoutBranch(newBranchName, sourceBranch);
@@ -56,7 +56,11 @@ export declare function gitReadFileOutsideRepo(git: SimpleGit, filePath: string)
56
56
  content: string;
57
57
  date: Date;
58
58
  }>;
59
- export declare function gitGetFileHistory(git: SimpleGit, filename: string, hash?: string): Promise<GitCommitHistory[]>;
59
+ export declare function gitGetFileHistory(git: SimpleGit, filename: string, options?: {
60
+ hash?: string;
61
+ branch?: string;
62
+ maxCommits?: number;
63
+ }): Promise<GitCommitHistory[]>;
60
64
  /**
61
65
  * Récupère le dernier commit d'une branche (opération bas niveau atomique)
62
66
  * @param git Instance SimpleGit
@@ -338,10 +338,23 @@ async function gitReadFileOutsideRepo(git, filePath) {
338
338
  throw error;
339
339
  }
340
340
  }
341
- async function gitGetFileHistory(git, filename, hash) {
341
+ async function gitGetFileHistory(git, filename, options) {
342
+ // - Normalisation des paramètres pour compatibilité
342
343
  try {
343
- const log = await git.log({ file: filename });
344
- const all = log.all.map(commit => ({
344
+ // - Utiliser git.log() pour obtenir l'historique des commits
345
+ // - git.log() retourne déjà les commits du plus récent au plus ancien (ordre standard Git)
346
+ const branch = options?.branch || 'HEAD';
347
+ const logArgs = [];
348
+ if (branch !== 'HEAD') {
349
+ logArgs.push(branch);
350
+ }
351
+ logArgs.push('--', filename);
352
+ const log = await git.log(logArgs);
353
+ if (log.all.length === 0) {
354
+ return [];
355
+ }
356
+ // - Construire l'historique (déjà dans l'ordre du plus récent au plus ancien)
357
+ let all = log.all.map(commit => ({
345
358
  hash: commit.hash,
346
359
  date: new Date(commit.date),
347
360
  message: commit.message,
@@ -350,10 +363,44 @@ async function gitGetFileHistory(git, filename, hash) {
350
363
  email: commit.author_email
351
364
  }
352
365
  }));
353
- // Si un hash est fourni, retourner le contenu du fichier à ce hash
354
- if (hash) {
355
- const fileContent = await gitGetFileContent(git, filename, hash);
356
- const index = all.findIndex(commit => commit.hash === hash);
366
+ // - Si un hash est fourni, s'assurer qu'il est inclus avant l'échantillonnage
367
+ let requestedHashIndex = -1;
368
+ if (options?.hash) {
369
+ requestedHashIndex = all.findIndex(commit => commit.hash === options.hash);
370
+ }
371
+ // - Limitation du nombre de commits avec échantillonnage uniforme
372
+ // - Garantit que le premier (plus récent) et dernier (plus ancien) commit sont inclus
373
+ // - Si un hash spécifique est demandé, il est aussi garanti d'être inclus
374
+ if (options?.maxCommits && all.length > options.maxCommits) {
375
+ const step = Math.ceil((all.length - 2) / (options.maxCommits - 2)) || 1;
376
+ const sampled = [all[0]]; // Premier commit (plus récent)
377
+ const includedIndices = new Set([0]); // Track des indices déjà inclus
378
+ // - Si un hash spécifique est demandé et n'est pas le premier, l'ajouter
379
+ if (requestedHashIndex > 0 && requestedHashIndex < all.length - 1) {
380
+ sampled.push(all[requestedHashIndex]);
381
+ includedIndices.add(requestedHashIndex);
382
+ }
383
+ // - Échantillonnage des commits intermédiaires
384
+ for (let i = 1; i < all.length - 1; i += step) {
385
+ if (!includedIndices.has(i)) {
386
+ sampled.push(all[i]);
387
+ includedIndices.add(i);
388
+ }
389
+ if (sampled.length >= options.maxCommits - 1)
390
+ break;
391
+ }
392
+ // - Dernier commit (plus ancien) si on a encore de la place
393
+ if (sampled.length < options.maxCommits && all.length > 1 && !includedIndices.has(all.length - 1)) {
394
+ sampled.push(all[all.length - 1]);
395
+ }
396
+ // - Trier par date décroissante pour maintenir l'ordre (plus récent en premier)
397
+ sampled.sort((a, b) => b.date.getTime() - a.date.getTime());
398
+ all = sampled;
399
+ }
400
+ // - Si un hash est fourni, retourner le contenu du fichier à ce hash
401
+ if (options?.hash) {
402
+ const fileContent = await gitGetFileContent(git, filename, options.hash);
403
+ const index = all.findIndex(commit => commit.hash === options.hash);
357
404
  if (index !== -1 && fileContent) {
358
405
  all[index] = fileContent;
359
406
  }
@@ -334,6 +334,20 @@ export interface GitCommitHistory {
334
334
  /** Branche où s'est produit ce commit (optionnel) */
335
335
  branch?: string;
336
336
  }
337
+ /**
338
+ * Structure simplifiée d'un commit pour la prévisualisation
339
+ *
340
+ * Utilisée dans loadPreview() pour retourner uniquement les informations essentielles
341
+ * sans le contenu complet du commit.
342
+ */
343
+ export interface CommitPreview {
344
+ /** Hash unique du commit */
345
+ hash: string;
346
+ /** Date et heure du commit */
347
+ date: Date;
348
+ /** Nom de l'auteur du commit */
349
+ author: string;
350
+ }
337
351
  /**
338
352
  * Résumé d'un fichier Git avec son commit associé
339
353
  *
@@ -510,3 +524,14 @@ export interface GitHealthStatus {
510
524
  /** Recommandations de réparation */
511
525
  recommendations: string[];
512
526
  }
527
+ /**
528
+ * Rapport de migration des notes Git
529
+ */
530
+ export interface GitPrNoteMigrationReport {
531
+ migrated: number;
532
+ alreadyOk: number;
533
+ lost: Array<{
534
+ branch: string;
535
+ prNumber: number;
536
+ }>;
537
+ }
@@ -40,23 +40,3 @@ export declare function matterParse(markdown: string): {
40
40
  */
41
41
  export declare function matterSerializeFromRule(rule: Rule): string;
42
42
  export declare function matterSerialize(content: string, matter: FrontMatter | any): string;
43
- /**
44
- * Extrait le front-matter (entre les deux premiers '---') et le contenu Markdown.
45
- * Fonction 100% vanilla : aucun paquet externe requis.
46
- * PROS:
47
- * Code plus propre et maintenable
48
- * - Typage TypeScript strict
49
- * - Plus robuste (ignore lignes indentées)
50
- * - Séparation des responsabilités
51
- * - Meilleure architecture (parsing pur)
52
- * CONS:
53
- * - Perte de fonctionnalité (slugs/tags)
54
- *
55
- * @param markdown Texte Markdown brut
56
- * @returns Objet avec matter, content et data
57
- */
58
- export declare function matterParse_OTHER(markdown: string): {
59
- matter: Record<string, any>;
60
- content: string;
61
- data: Record<string, any>;
62
- };
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.matterParse = matterParse;
4
4
  exports.matterSerializeFromRule = matterSerializeFromRule;
5
5
  exports.matterSerialize = matterSerialize;
6
- exports.matterParse_OTHER = matterParse_OTHER;
7
6
  /**
8
7
  * Parse le front-matter YAML et le contenu Markdown d'un document
9
8
  *
@@ -53,9 +52,17 @@ function matterParse(markdown) {
53
52
  const [rawKey, ...rawValue] = line.split(':');
54
53
  const key = rawKey.trim();
55
54
  const value = rawValue.join(':').trim();
56
- // Ignorer les clés sans valeur
57
- if (!value)
55
+ // Détection de null explicite (YAML: null, ~) - ne pas créer la clé
56
+ if (value === 'null' || value === '~' || value === 'Null' || value === 'NULL') {
57
+ // Si la valeur est null, ne pas créer la clé (comme undefined)
58
58
  continue;
59
+ }
60
+ // Si la valeur est vide (chaîne vide après trim), définir comme chaîne vide
61
+ // L'utilisateur peut définir un string vide pour indiquer qu'il n'a pas de valeur pour ce champ
62
+ if (!value) {
63
+ matter[key] = '';
64
+ continue;
65
+ }
59
66
  // Détection et parsing des arrays YAML
60
67
  if (value.startsWith('[') && value.endsWith(']')) {
61
68
  try {
@@ -122,13 +129,10 @@ function matterSerializeFromRule(rule) {
122
129
  function matterSerialize(content, matter) {
123
130
  // Créer un objet propre pour le front-matter (exclure oldfile)
124
131
  const cleanMatter = { ...matter };
125
- // //
126
- // // FIX: Convertir la Date en string pour éviter l'erreur YAML
127
- // cleanMatter.lastModified = (cleanMatter.lastModified instanceof Date) ? `${new Date().toISOString()}` : `${cleanMatter.lastModified}`;
128
132
  const result = Object.keys(cleanMatter).reduce((acc, key) => {
129
133
  const value = cleanMatter[key];
130
- // Ignorer les valeurs undefined ou null
131
- if (value === undefined || value === null) {
134
+ // Ignorer les valeurs undefined, null ou chaînes vides
135
+ if (value === undefined || value === null || value === '') {
132
136
  return acc;
133
137
  }
134
138
  if (Array.isArray(value)) {
@@ -142,86 +146,59 @@ function matterSerialize(content, matter) {
142
146
  // ✅ FIX: Ne PAS mettre de guillemets autour des booléens
143
147
  return acc + `${key}: ${value}\n`;
144
148
  }
149
+ else if (value instanceof Date) {
150
+ // ✅ FIX: Convertir Date en ISO string avec guillemets
151
+ return acc + `${key}: '${value.toISOString()}'\n`;
152
+ }
145
153
  else {
146
- // Pour les strings, mettre des guillemets
147
- return acc + `${key}: '${value}'\n`;
154
+ const formatted = formatYAMLString(value.toString());
155
+ return acc + `${key}: ${formatted}\n`;
148
156
  }
149
157
  }, '');
150
158
  return `---
151
159
  ${result}---
152
160
  ${content}`;
153
161
  }
154
- /**
155
- * Extrait le front-matter (entre les deux premiers '---') et le contenu Markdown.
156
- * Fonction 100% vanilla : aucun paquet externe requis.
157
- * PROS:
158
- * Code plus propre et maintenable
159
- * - Typage TypeScript strict
160
- * - Plus robuste (ignore lignes indentées)
161
- * - Séparation des responsabilités
162
- * - Meilleure architecture (parsing pur)
163
- * CONS:
164
- * - Perte de fonctionnalité (slugs/tags)
165
- *
166
- * @param markdown Texte Markdown brut
167
- * @returns Objet avec matter, content et data
168
- */
169
- function matterParse_OTHER(markdown) {
170
- // 1. Séparer les lignes
171
- const lines = markdown.split(/\r?\n/);
172
- if (lines[0].trim() !== '---') {
173
- return { matter: {}, content: markdown, data: {} }; // Pas de front-matter
162
+ function formatYAMLString(value) {
163
+ const shouldQuote = needsQuotes(value);
164
+ if (!shouldQuote) {
165
+ return value;
174
166
  }
175
- // 2. Trouver la ligne de fermeture '---'
176
- let end = 1;
177
- while (end < lines.length && lines[end].trim() !== '---')
178
- end++;
179
- // 3. Parser le bloc front-matter (simple clé-valeur)
180
- const matter = {};
181
- for (let i = 1; i < end; i++) {
182
- const line = lines[i];
183
- if (!line.trim())
184
- continue; // ignorer les lignes vides
185
- // Ignorer les lignes indentées (structures complexes non supportées)
186
- if (line.startsWith(' ') || line.startsWith('\t')) {
187
- continue;
188
- }
189
- const trimmedLine = line.trim();
190
- if (trimmedLine.includes(':')) {
191
- const [rawKey, ...rawValue] = trimmedLine.split(':');
192
- const key = rawKey.trim();
193
- const value = rawValue.join(':').trim();
194
- if (value) {
195
- // Détection et parsing des arrays YAML
196
- if (value.startsWith('[') && value.endsWith(']')) {
197
- try {
198
- const parsed = JSON.parse(value);
199
- if (Array.isArray(parsed)) {
200
- matter[key] = parsed;
201
- }
202
- else {
203
- matter[key] = value.replace(/^["']|["']$/g, '');
204
- }
205
- }
206
- catch (e) {
207
- matter[key] = value.replace(/^["']|["']$/g, '');
208
- }
209
- }
210
- // Tentative de typage léger : nombre, booléen, sinon chaîne
211
- else if (/^(true|false)$/i.test(value)) {
212
- matter[key] = value.toLowerCase() === 'true';
213
- }
214
- else if (!isNaN(Number(value)) && value !== '') {
215
- matter[key] = Number(value);
216
- }
217
- else {
218
- matter[key] = value.replace(/^["']|["']$/g, '');
219
- }
220
- }
221
- }
167
+ if (process.env.DEBUG_YAML === '1') {
168
+ console.warn('[matterSerialize] quoting value:', value);
222
169
  }
223
- // 4. Restituer le contenu Markdown sans le front-matter
224
- const content = lines.slice(end + 1).join('\n').trimStart();
225
- const data = matter;
226
- return { data, content, matter };
170
+ const escaped = value.replace(/'/g, "''");
171
+ return `'${escaped}'`;
172
+ }
173
+ function needsQuotes(value) {
174
+ if (value.length === 0) {
175
+ return true;
176
+ }
177
+ const firstChar = value[0];
178
+ const lastChar = value[value.length - 1];
179
+ if (firstChar === ' ' || lastChar === ' ') {
180
+ return true;
181
+ }
182
+ if (/[:#{}\[\],&*?]/.test(value)) {
183
+ return true;
184
+ }
185
+ if (/^-/.test(value)) {
186
+ return true;
187
+ }
188
+ if (/^\d/.test(value)) {
189
+ return true;
190
+ }
191
+ if (value.includes('\n')) {
192
+ return true;
193
+ }
194
+ if (value.includes('- ')) {
195
+ return true;
196
+ }
197
+ if (value.includes('"')) {
198
+ return true;
199
+ }
200
+ if (value.includes("'")) {
201
+ return true;
202
+ }
203
+ return false;
227
204
  }
@@ -13,7 +13,7 @@ const path_1 = __importDefault(require("path"));
13
13
  const fs_1 = __importDefault(require("fs"));
14
14
  const jsdom_1 = require("jsdom");
15
15
  const readability_1 = require("@mozilla/readability");
16
- const pricing_llm_1 = require("./pricing.llm");
16
+ const pricing_1 = require("./llm/pricing");
17
17
  const prompts_1 = require("./prompts");
18
18
  const utils_1 = require("./utils");
19
19
  const execute_1 = require("./execute");
@@ -37,7 +37,7 @@ async function extractCaptcha(base64Image, openai) {
37
37
  messages: [{ role: "user", content }],
38
38
  max_completion_tokens: 50,
39
39
  });
40
- const cost = (0, pricing_llm_1.calculateCost)(model, response.usage);
40
+ const cost = (0, pricing_1.calculateCost)(model, response.usage);
41
41
  // Récupérer la réponse markdown
42
42
  const number = response.choices[0].message.content;
43
43
  return { number, cost };
@@ -190,6 +190,7 @@ async function pdf2markdown(outputDir, pdf, matter, model = "MEDIUM-fast") {
190
190
  // Ca ne marche pas mieux que pdftotext
191
191
  // const { stdout } = await execFileAsync("python3", ["./bin/extract_text_with_links.py", pdf]);
192
192
  // const { text, links } = JSON.parse(stdout);
193
+ // `pdftotext -f 1 -l 2 -layout -eol unix -nodiag "${pdf}" "${outputPath}"`;
193
194
  await execAsync(`pdftotext -nodiag -nopgbrk "${pdf}" "${outputPath}"`);
194
195
  const links = await extractLinksFromPDF(pdf, outputDir);
195
196
  const text = fs_1.default.readFileSync(outputPath, "utf8");
@@ -17,6 +17,14 @@ export declare class AgentStateGraph implements IAgentStateGraph {
17
17
  createOrRestore(agentName: string, description?: string): AgentDiscussion;
18
18
  /**
19
19
  * Ajoute un message à la discussion d'un agent
20
+ * Gère tous types de messages : user, assistant, system, tool, function_call_output, etc.
21
+ *
22
+ * Utilisée par:
23
+ * - readCompletionsStream (responses.ts) : messages assistant avec tool_calls, content
24
+ * - batchProcessToolCalls (helpers.ts) : messages function_call_output
25
+ * - executeAgentSet (responses.ts) : messages user
26
+ * - stategraph.storage.ts : migration ancien format
27
+ *
20
28
  * @param agentName Nom de l'agent
21
29
  * @param message Message sans ID ni timestamp (auto-générés)
22
30
  */
@@ -30,8 +38,14 @@ export declare class AgentStateGraph implements IAgentStateGraph {
30
38
  set(agentName: string, content: string): void;
31
39
  /**
32
40
  * Ajoute une étape au CONTEXT TRAIL et met à jour le message system
41
+ * Injecte automatiquement le trail dans le system message via updateSystemMessage()
42
+ *
43
+ * Utilisée par:
44
+ * - readCompletionsStream (responses.ts) : après chaque tool call exécuté
45
+ * - Tests d'intégration pour validation du context trail
46
+ *
33
47
  * @param agentName Nom de l'agent
34
- * @param step Étape à ajouter au trail
48
+ * @param step Étape à ajouter au trail (tool, context, reason, id optionnel)
35
49
  */
36
50
  addStep(agentName: string, step: StepTrail): void;
37
51
  /**
@@ -60,6 +74,17 @@ export declare class AgentStateGraph implements IAgentStateGraph {
60
74
  /**
61
75
  * Retourne une vue filtrée pour le client
62
76
  * Supprime les messages system et les métadonnées d'outils
77
+ *
78
+ * Filtre appliqué:
79
+ * - Exclut: system, tool, messages avec name (Chat Completions function results)
80
+ * - Exclut: function_call_output (Responses API)
81
+ * - Exclut: messages assistant avec tool_calls mais sans content
82
+ * - Nettoie: <memories> tags dans le content
83
+ *
84
+ * Utilisée par:
85
+ * - ctrl/agent.ts (agentic-server) : ctrlGetHistory, ctrlExecuteAgent
86
+ * - Tests d'intégration pour validation
87
+ *
63
88
  * @param agentName Nom de l'agent
64
89
  * @returns Discussion filtrée pour l'affichage client
65
90
  */