@riotprompt/riotprompt 0.0.8 → 0.0.9

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 (42) hide show
  1. package/.kodrdriv-test-cache.json +6 -0
  2. package/README.md +2 -2
  3. package/dist/builder.js +3 -0
  4. package/dist/builder.js.map +1 -1
  5. package/dist/context-manager.d.ts +135 -0
  6. package/dist/context-manager.js +220 -0
  7. package/dist/context-manager.js.map +1 -0
  8. package/dist/conversation-logger.d.ts +283 -0
  9. package/dist/conversation-logger.js +454 -0
  10. package/dist/conversation-logger.js.map +1 -0
  11. package/dist/conversation.d.ts +271 -0
  12. package/dist/conversation.js +622 -0
  13. package/dist/conversation.js.map +1 -0
  14. package/dist/formatter.js.map +1 -1
  15. package/dist/iteration-strategy.d.ts +231 -0
  16. package/dist/iteration-strategy.js +486 -0
  17. package/dist/iteration-strategy.js.map +1 -0
  18. package/dist/loader.js +3 -0
  19. package/dist/loader.js.map +1 -1
  20. package/dist/message-builder.d.ts +156 -0
  21. package/dist/message-builder.js +254 -0
  22. package/dist/message-builder.js.map +1 -0
  23. package/dist/override.js +3 -0
  24. package/dist/override.js.map +1 -1
  25. package/dist/recipes.d.ts +42 -0
  26. package/dist/recipes.js +189 -4
  27. package/dist/recipes.js.map +1 -1
  28. package/dist/reflection.d.ts +250 -0
  29. package/dist/reflection.js +416 -0
  30. package/dist/reflection.js.map +1 -0
  31. package/dist/riotprompt.cjs +3549 -218
  32. package/dist/riotprompt.cjs.map +1 -1
  33. package/dist/riotprompt.d.ts +18 -2
  34. package/dist/riotprompt.js +9 -1
  35. package/dist/riotprompt.js.map +1 -1
  36. package/dist/token-budget.d.ts +177 -0
  37. package/dist/token-budget.js +404 -0
  38. package/dist/token-budget.js.map +1 -0
  39. package/dist/tools.d.ts +239 -0
  40. package/dist/tools.js +324 -0
  41. package/dist/tools.js.map +1 -0
  42. package/package.json +23 -20
@@ -5,9 +5,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5
5
  const zod = require('zod');
6
6
  const fs = require('fs/promises');
7
7
  const path = require('path');
8
+ const crypto = require('crypto');
9
+ const tiktoken = require('tiktoken');
8
10
  const fs$1 = require('fs');
9
11
  const glob = require('glob');
10
- const crypto = require('crypto');
11
12
 
12
13
  function _interopNamespaceDefault(e) {
13
14
  const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
@@ -1376,235 +1377,3496 @@ const builder = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
1376
1377
  create
1377
1378
  }, Symbol.toStringTag, { value: 'Module' }));
1378
1379
 
1379
- // ===== CONFIGURATION SCHEMAS =====
1380
- const ContentItemSchema = zod.z.union([
1381
- zod.z.string(),
1382
- zod.z.object({
1383
- content: zod.z.string(),
1384
- title: zod.z.string().optional(),
1385
- weight: zod.z.number().optional()
1386
- }),
1387
- zod.z.object({
1388
- path: zod.z.string(),
1389
- title: zod.z.string().optional(),
1390
- weight: zod.z.number().optional()
1391
- }),
1392
- zod.z.object({
1393
- directories: zod.z.array(zod.z.string()),
1394
- title: zod.z.string().optional(),
1395
- weight: zod.z.number().optional()
1396
- })
1397
- ]);
1398
- const RecipeConfigSchema = zod.z.object({
1399
- // Core settings
1400
- basePath: zod.z.string(),
1401
- logger: zod.z.any().optional().default(DEFAULT_LOGGER),
1402
- overridePaths: zod.z.array(zod.z.string()).optional().default([
1403
- "./"
1404
- ]),
1405
- overrides: zod.z.boolean().optional().default(false),
1406
- parameters: ParametersSchema.optional().default({}),
1407
- // Content sections
1408
- persona: ContentItemSchema.optional(),
1409
- instructions: zod.z.array(ContentItemSchema).optional().default([]),
1410
- content: zod.z.array(ContentItemSchema).optional().default([]),
1411
- context: zod.z.array(ContentItemSchema).optional().default([]),
1412
- // Templates and inheritance
1413
- extends: zod.z.string().optional(),
1414
- template: zod.z.string().optional()
1415
- });
1416
- // User-customizable template registry
1417
- let TEMPLATES = {};
1380
+ function _define_property$7(obj, key, value) {
1381
+ if (key in obj) {
1382
+ Object.defineProperty(obj, key, {
1383
+ value: value,
1384
+ enumerable: true,
1385
+ configurable: true,
1386
+ writable: true
1387
+ });
1388
+ } else {
1389
+ obj[key] = value;
1390
+ }
1391
+ return obj;
1392
+ }
1418
1393
  /**
1419
- * Register custom templates with the recipes system
1420
- *
1394
+ * ContextManager tracks and manages dynamically injected context.
1395
+ *
1396
+ * Features:
1397
+ * - Track all injected context with metadata
1398
+ * - Deduplication by ID, hash, or content
1399
+ * - Category-based organization
1400
+ * - Query context state
1401
+ * - Context statistics
1402
+ *
1421
1403
  * @example
1422
1404
  * ```typescript
1423
- * // Register your own templates
1424
- * registerTemplates({
1425
- * myWorkflow: {
1426
- * persona: { path: "personas/my-persona.md" },
1427
- * instructions: [{ path: "instructions/my-instructions.md" }]
1428
- * },
1429
- * anotherTemplate: {
1430
- * persona: { content: "You are a helpful assistant" },
1431
- * instructions: [{ content: "Follow these steps..." }]
1432
- * }
1433
- * });
1405
+ * const manager = new ContextManager();
1406
+ *
1407
+ * // Track injected context
1408
+ * manager.track({
1409
+ * id: 'file:main.ts',
1410
+ * content: fileContent,
1411
+ * title: 'Main File',
1412
+ * category: 'source-code'
1413
+ * }, 5);
1414
+ *
1415
+ * // Check for duplicates
1416
+ * if (manager.hasContext('file:main.ts')) {
1417
+ * console.log('Already provided');
1418
+ * }
1419
+ *
1420
+ * // Query by category
1421
+ * const sourceFiles = manager.getByCategory('source-code');
1434
1422
  * ```
1435
- */ const registerTemplates = (templates)=>{
1436
- TEMPLATES = {
1437
- ...TEMPLATES,
1438
- ...templates
1439
- };
1440
- };
1441
- /**
1442
- * Get currently registered templates
1443
- */ const getTemplates = ()=>({
1444
- ...TEMPLATES
1445
- });
1446
- /**
1447
- * Clear all registered templates
1448
- */ const clearTemplates = ()=>{
1449
- TEMPLATES = {};
1450
- };
1451
- // ===== CORE RECIPE ENGINE =====
1452
- const cook = async (config)=>{
1453
- // Parse and validate configuration with defaults
1454
- const validatedConfig = RecipeConfigSchema.parse({
1455
- overridePaths: [
1456
- "./"
1457
- ],
1458
- overrides: false,
1459
- parameters: {},
1460
- instructions: [],
1461
- content: [],
1462
- context: [],
1463
- ...config
1464
- });
1465
- // Handle template inheritance
1466
- let finalConfig = {
1467
- ...validatedConfig
1468
- };
1469
- if (validatedConfig.template) {
1470
- const template = TEMPLATES[validatedConfig.template];
1471
- if (template) {
1472
- finalConfig = {
1473
- ...validatedConfig,
1474
- persona: validatedConfig.persona || template.persona,
1475
- instructions: [
1476
- ...template.instructions || [],
1477
- ...validatedConfig.instructions || []
1478
- ],
1479
- content: [
1480
- ...template.content || [],
1481
- ...validatedConfig.content || []
1482
- ],
1483
- context: [
1484
- ...template.context || [],
1485
- ...validatedConfig.context || []
1486
- ]
1487
- };
1423
+ */ class ContextManager {
1424
+ /**
1425
+ * Track a context item
1426
+ */ track(item, position) {
1427
+ const id = item.id || this.generateId();
1428
+ const hash = this.hashContent(item.content);
1429
+ const trackedItem = {
1430
+ ...item,
1431
+ id,
1432
+ hash,
1433
+ position,
1434
+ injectedAt: new Date(),
1435
+ timestamp: item.timestamp || new Date(),
1436
+ priority: item.priority || 'medium'
1437
+ };
1438
+ this.items.set(id, trackedItem);
1439
+ this.hashes.add(hash);
1440
+ this.logger.debug('Tracked context item', {
1441
+ id,
1442
+ category: item.category,
1443
+ position
1444
+ });
1445
+ }
1446
+ /**
1447
+ * Check if context with given ID exists
1448
+ */ hasContext(id) {
1449
+ return this.items.has(id);
1450
+ }
1451
+ /**
1452
+ * Check if content with given hash exists
1453
+ */ hasContentHash(content) {
1454
+ const hash = this.hashContent(content);
1455
+ return this.hashes.has(hash);
1456
+ }
1457
+ /**
1458
+ * Check if similar content exists (fuzzy match)
1459
+ */ hasSimilarContent(content) {
1460
+ const normalized = this.normalizeContent(content);
1461
+ for (const item of this.items.values()){
1462
+ const itemNormalized = this.normalizeContent(item.content || '');
1463
+ // Exact match
1464
+ if (normalized === itemNormalized) {
1465
+ return true;
1466
+ }
1467
+ // Substring match (one contains the other)
1468
+ if (normalized.includes(itemNormalized) || itemNormalized.includes(normalized)) {
1469
+ return true;
1470
+ }
1488
1471
  }
1472
+ return false;
1489
1473
  }
1490
- // Setup internal services
1491
- const logger = wrapLogger(finalConfig.logger, 'Recipe');
1492
- const parser$1 = create$4({
1493
- logger
1494
- });
1495
- const override$1 = create$1({
1496
- logger,
1497
- configDirs: finalConfig.overridePaths || [
1498
- "./"
1499
- ],
1500
- overrides: finalConfig.overrides || false
1501
- });
1502
- const loader$1 = create$2({
1503
- logger
1504
- });
1505
- // Create sections
1506
- const personaSection = create$8({
1507
- title: "Persona"
1508
- });
1509
- const instructionSection = create$8({
1510
- title: "Instruction"
1511
- });
1512
- const contentSection = create$8({
1513
- title: "Content"
1514
- });
1515
- const contextSection = create$8({
1516
- title: "Context"
1517
- });
1518
- // Process persona
1519
- if (finalConfig.persona) {
1520
- await processContentItem(finalConfig.persona, personaSection, 'persona', {
1521
- basePath: finalConfig.basePath,
1522
- parser: parser$1,
1523
- override: override$1,
1524
- loader: loader$1,
1525
- parameters: finalConfig.parameters});
1474
+ /**
1475
+ * Get context item by ID
1476
+ */ get(id) {
1477
+ return this.items.get(id);
1526
1478
  }
1527
- // Process instructions
1528
- for (const item of finalConfig.instructions || []){
1529
- await processContentItem(item, instructionSection, 'instruction', {
1530
- basePath: finalConfig.basePath,
1531
- parser: parser$1,
1532
- override: override$1,
1533
- loader: loader$1,
1534
- parameters: finalConfig.parameters});
1479
+ /**
1480
+ * Get all tracked context items
1481
+ */ getAll() {
1482
+ return Array.from(this.items.values());
1535
1483
  }
1536
- // Process content
1537
- for (const item of finalConfig.content || []){
1538
- await processContentItem(item, contentSection, 'content', {
1539
- basePath: finalConfig.basePath,
1540
- parser: parser$1,
1541
- override: override$1,
1542
- loader: loader$1,
1543
- parameters: finalConfig.parameters});
1484
+ /**
1485
+ * Get context items by category
1486
+ */ getByCategory(category) {
1487
+ return this.getAll().filter((item)=>item.category === category);
1544
1488
  }
1545
- // Process context
1546
- for (const item of finalConfig.context || []){
1547
- await processContentItem(item, contextSection, 'context', {
1548
- basePath: finalConfig.basePath,
1549
- parser: parser$1,
1550
- override: override$1,
1551
- loader: loader$1,
1552
- parameters: finalConfig.parameters});
1489
+ /**
1490
+ * Get context items by priority
1491
+ */ getByPriority(priority) {
1492
+ return this.getAll().filter((item)=>item.priority === priority);
1553
1493
  }
1554
- // Build and return prompt
1555
- return create$6({
1556
- persona: personaSection,
1557
- instructions: instructionSection,
1558
- contents: contentSection,
1559
- contexts: contextSection
1560
- });
1561
- };
1562
- const processContentItem = async (item, section, type, ctx)=>{
1563
- const sectionOptions = {
1564
- parameters: ctx.parameters
1565
- };
1566
- if (typeof item === 'string') {
1567
- // Simple string content
1568
- const parsedSection = await ctx.parser.parse(item, sectionOptions);
1569
- section.add(parsedSection);
1570
- } else if ('content' in item) {
1571
- // Inline content with options
1572
- const parsedSection = await ctx.parser.parse(item.content, {
1573
- ...sectionOptions,
1574
- title: item.title,
1575
- weight: item.weight
1576
- });
1577
- section.add(parsedSection);
1578
- } else if ('path' in item) {
1579
- // File path
1580
- const fullPath = path.join(ctx.basePath, item.path);
1581
- const parsedSection = await ctx.parser.parseFile(fullPath, {
1582
- ...sectionOptions,
1583
- title: item.title,
1584
- weight: item.weight
1494
+ /**
1495
+ * Get context items by source
1496
+ */ getBySource(source) {
1497
+ return this.getAll().filter((item)=>item.source === source);
1498
+ }
1499
+ /**
1500
+ * Get all categories
1501
+ */ getCategories() {
1502
+ const categories = new Set();
1503
+ this.items.forEach((item)=>{
1504
+ if (item.category) {
1505
+ categories.add(item.category);
1506
+ }
1585
1507
  });
1586
- const overrideSection = await ctx.override.customize(item.path, parsedSection, sectionOptions);
1587
- section.add(overrideSection);
1588
- } else if ('directories' in item) {
1589
- // Directory loading
1590
- const sections = await ctx.loader.load(item.directories, {
1591
- ...sectionOptions,
1592
- title: item.title,
1593
- weight: item.weight
1508
+ return Array.from(categories).sort();
1509
+ }
1510
+ /**
1511
+ * Get context statistics
1512
+ */ getStats() {
1513
+ const byCategory = new Map();
1514
+ const byPriority = new Map();
1515
+ const bySource = new Map();
1516
+ let oldestTimestamp;
1517
+ let newestTimestamp;
1518
+ this.items.forEach((item)=>{
1519
+ // Category stats
1520
+ if (item.category) {
1521
+ byCategory.set(item.category, (byCategory.get(item.category) || 0) + 1);
1522
+ }
1523
+ // Priority stats
1524
+ const priority = item.priority || 'medium';
1525
+ byPriority.set(priority, (byPriority.get(priority) || 0) + 1);
1526
+ // Source stats
1527
+ if (item.source) {
1528
+ bySource.set(item.source, (bySource.get(item.source) || 0) + 1);
1529
+ }
1530
+ // Timestamp stats
1531
+ if (item.timestamp) {
1532
+ if (!oldestTimestamp || item.timestamp < oldestTimestamp) {
1533
+ oldestTimestamp = item.timestamp;
1534
+ }
1535
+ if (!newestTimestamp || item.timestamp > newestTimestamp) {
1536
+ newestTimestamp = item.timestamp;
1537
+ }
1538
+ }
1594
1539
  });
1595
- section.add(sections);
1540
+ return {
1541
+ totalItems: this.items.size,
1542
+ byCategory,
1543
+ byPriority,
1544
+ bySource,
1545
+ oldestTimestamp,
1546
+ newestTimestamp
1547
+ };
1596
1548
  }
1597
- };
1598
- // ===== FLUENT RECIPE BUILDER =====
1599
- const recipe = (basePath)=>{
1600
- const config = {
1601
- basePath
1602
- };
1603
- const builder = {
1604
- template: (name)=>{
1605
- config.template = name;
1606
- return builder;
1607
- },
1549
+ /**
1550
+ * Remove context item by ID
1551
+ */ remove(id) {
1552
+ const item = this.items.get(id);
1553
+ if (item) {
1554
+ this.items.delete(id);
1555
+ this.hashes.delete(item.hash);
1556
+ this.logger.debug('Removed context item', {
1557
+ id
1558
+ });
1559
+ return true;
1560
+ }
1561
+ return false;
1562
+ }
1563
+ /**
1564
+ * Clear all tracked context
1565
+ */ clear() {
1566
+ this.items.clear();
1567
+ this.hashes.clear();
1568
+ this.logger.debug('Cleared all context');
1569
+ }
1570
+ /**
1571
+ * Generate unique ID for context item
1572
+ */ generateId() {
1573
+ return `ctx-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1574
+ }
1575
+ /**
1576
+ * Hash content for deduplication
1577
+ */ hashContent(content) {
1578
+ return crypto.createHash('sha256').update(content).digest('hex').substring(0, 16);
1579
+ }
1580
+ /**
1581
+ * Normalize content for comparison
1582
+ */ normalizeContent(content) {
1583
+ return content.replace(/\s+/g, ' ').trim().toLowerCase();
1584
+ }
1585
+ constructor(logger){
1586
+ _define_property$7(this, "items", void 0);
1587
+ _define_property$7(this, "hashes", void 0);
1588
+ _define_property$7(this, "logger", void 0);
1589
+ this.items = new Map();
1590
+ this.hashes = new Set();
1591
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ContextManager');
1592
+ }
1593
+ }
1594
+
1595
+ function _define_property$6(obj, key, value) {
1596
+ if (key in obj) {
1597
+ Object.defineProperty(obj, key, {
1598
+ value: value,
1599
+ enumerable: true,
1600
+ configurable: true,
1601
+ writable: true
1602
+ });
1603
+ } else {
1604
+ obj[key] = value;
1605
+ }
1606
+ return obj;
1607
+ }
1608
+ // ===== CONVERSATION LOGGER =====
1609
+ /**
1610
+ * ConversationLogger logs conversations to various formats.
1611
+ *
1612
+ * Features:
1613
+ * - Multiple formats (JSON, Markdown, JSONL)
1614
+ * - Automatic timestamping
1615
+ * - Metadata tracking
1616
+ * - Sensitive data redaction
1617
+ * - Streaming support (JSONL)
1618
+ *
1619
+ * @example
1620
+ * ```typescript
1621
+ * const logger = new ConversationLogger({
1622
+ * enabled: true,
1623
+ * outputPath: 'logs/conversations',
1624
+ * format: 'json',
1625
+ * includeMetadata: true
1626
+ * });
1627
+ *
1628
+ * logger.onConversationStart({ model: 'gpt-4o', startTime: new Date() });
1629
+ * logger.onMessageAdded(message);
1630
+ * const path = await logger.save();
1631
+ * ```
1632
+ */ class ConversationLogger {
1633
+ /**
1634
+ * Start conversation logging
1635
+ */ onConversationStart(metadata) {
1636
+ this.metadata = {
1637
+ ...this.metadata,
1638
+ ...metadata,
1639
+ startTime: this.startTime
1640
+ };
1641
+ this.logger.debug('Conversation logging started', {
1642
+ id: this.conversationId
1643
+ });
1644
+ }
1645
+ /**
1646
+ * Log a message
1647
+ */ onMessageAdded(message, metadata) {
1648
+ let content = message.content;
1649
+ // Redact sensitive data if enabled
1650
+ if (this.config.redactSensitive && content && typeof content === 'string') {
1651
+ content = this.redactContent(content);
1652
+ }
1653
+ const loggedMessage = {
1654
+ index: this.messageIndex++,
1655
+ timestamp: new Date().toISOString(),
1656
+ role: message.role,
1657
+ content,
1658
+ tool_calls: message.tool_calls,
1659
+ tool_call_id: message.tool_call_id,
1660
+ metadata
1661
+ };
1662
+ this.messages.push(loggedMessage);
1663
+ // For JSONL format, append immediately
1664
+ if (this.config.format === 'jsonl') {
1665
+ this.appendToJSONL(loggedMessage).catch(this.config.onError);
1666
+ }
1667
+ }
1668
+ /**
1669
+ * Log a tool call
1670
+ */ onToolCall(callId, toolName, iteration, args, result, duration, success, error) {
1671
+ this.toolCalls.push({
1672
+ callId,
1673
+ toolName,
1674
+ timestamp: new Date().toISOString(),
1675
+ iteration,
1676
+ arguments: args,
1677
+ result,
1678
+ duration,
1679
+ success,
1680
+ error
1681
+ });
1682
+ }
1683
+ /**
1684
+ * End conversation logging
1685
+ */ onConversationEnd(_summary) {
1686
+ this.metadata.endTime = new Date();
1687
+ this.metadata.duration = this.metadata.endTime.getTime() - this.startTime.getTime();
1688
+ this.logger.debug('Conversation logging ended', {
1689
+ messages: this.messages.length,
1690
+ duration: this.metadata.duration
1691
+ });
1692
+ }
1693
+ /**
1694
+ * Save conversation to disk
1695
+ */ async save() {
1696
+ if (!this.config.enabled) {
1697
+ return '';
1698
+ }
1699
+ try {
1700
+ const outputPath = await this.getOutputPath();
1701
+ switch(this.config.format){
1702
+ case 'json':
1703
+ await this.saveAsJSON(outputPath);
1704
+ break;
1705
+ case 'markdown':
1706
+ await this.saveAsMarkdown(outputPath);
1707
+ break;
1708
+ case 'jsonl':
1709
+ break;
1710
+ }
1711
+ this.config.onSaved(outputPath);
1712
+ this.logger.info('Conversation saved', {
1713
+ path: outputPath
1714
+ });
1715
+ return outputPath;
1716
+ } catch (error) {
1717
+ this.config.onError(error);
1718
+ this.logger.error('Failed to save conversation', {
1719
+ error
1720
+ });
1721
+ throw error;
1722
+ }
1723
+ }
1724
+ /**
1725
+ * Get logged conversation object
1726
+ */ getConversation() {
1727
+ return {
1728
+ id: this.conversationId,
1729
+ metadata: this.metadata,
1730
+ messages: this.messages,
1731
+ summary: {
1732
+ totalMessages: this.messages.length,
1733
+ toolCallsExecuted: this.toolCalls.length,
1734
+ iterations: 0,
1735
+ success: true
1736
+ }
1737
+ };
1738
+ }
1739
+ /**
1740
+ * Generate unique conversation ID
1741
+ */ generateId() {
1742
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
1743
+ const random = Math.random().toString(36).substring(2, 8);
1744
+ return `conv-${timestamp}-${random}`;
1745
+ }
1746
+ /**
1747
+ * Get output file path
1748
+ */ async getOutputPath() {
1749
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
1750
+ const filename = this.config.filenameTemplate.replace('{timestamp}', timestamp).replace('{id}', this.conversationId).replace('{template}', this.metadata.template || 'default');
1751
+ const ext = this.config.format === 'markdown' ? '.md' : '.json';
1752
+ const fullPath = path.join(this.config.outputPath, filename + ext);
1753
+ // Ensure directory exists
1754
+ await fs.mkdir(path.dirname(fullPath), {
1755
+ recursive: true
1756
+ });
1757
+ return fullPath;
1758
+ }
1759
+ /**
1760
+ * Save as JSON
1761
+ */ async saveAsJSON(outputPath) {
1762
+ const data = {
1763
+ id: this.conversationId,
1764
+ metadata: this.metadata,
1765
+ messages: this.messages,
1766
+ summary: {
1767
+ totalMessages: this.messages.length,
1768
+ toolCallsExecuted: this.toolCalls.length,
1769
+ iterations: 0,
1770
+ success: true
1771
+ }
1772
+ };
1773
+ await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf-8');
1774
+ }
1775
+ /**
1776
+ * Save as Markdown
1777
+ */ async saveAsMarkdown(outputPath) {
1778
+ let markdown = `# Conversation Log\n\n`;
1779
+ markdown += `**ID**: ${this.conversationId}\n`;
1780
+ markdown += `**Started**: ${this.metadata.startTime.toISOString()}\n`;
1781
+ if (this.metadata.duration) {
1782
+ markdown += `**Duration**: ${(this.metadata.duration / 1000).toFixed(1)}s\n`;
1783
+ }
1784
+ markdown += `**Model**: ${this.metadata.model}\n`;
1785
+ if (this.metadata.template) {
1786
+ markdown += `**Template**: ${this.metadata.template}\n`;
1787
+ }
1788
+ markdown += `\n## Conversation\n\n`;
1789
+ for (const msg of this.messages){
1790
+ const time = new Date(msg.timestamp).toLocaleTimeString();
1791
+ markdown += `### Message ${msg.index + 1} (${time}) - ${msg.role}\n\n`;
1792
+ if (msg.content) {
1793
+ markdown += `\`\`\`\n${msg.content}\n\`\`\`\n\n`;
1794
+ }
1795
+ if (msg.tool_calls) {
1796
+ markdown += `**Tool Calls:**\n`;
1797
+ for (const call of msg.tool_calls){
1798
+ markdown += `- ${call.function.name}: \`${call.function.arguments}\`\n`;
1799
+ }
1800
+ markdown += `\n`;
1801
+ }
1802
+ if (msg.metadata) {
1803
+ markdown += `*Metadata: ${JSON.stringify(msg.metadata)}*\n\n`;
1804
+ }
1805
+ }
1806
+ markdown += `## Summary\n\n`;
1807
+ markdown += `- **Total Messages**: ${this.messages.length}\n`;
1808
+ markdown += `- **Tool Calls**: ${this.toolCalls.length}\n`;
1809
+ await fs.writeFile(outputPath, markdown, 'utf-8');
1810
+ }
1811
+ /**
1812
+ * Append to JSONL file (streaming)
1813
+ */ async appendToJSONL(message) {
1814
+ const outputPath = await this.getOutputPath();
1815
+ const line = JSON.stringify(message) + '\n';
1816
+ await fs.appendFile(outputPath, line, 'utf-8');
1817
+ }
1818
+ /**
1819
+ * Redact sensitive content
1820
+ */ redactContent(content) {
1821
+ let redacted = content;
1822
+ // Apply custom patterns
1823
+ for (const pattern of this.config.redactPatterns){
1824
+ redacted = redacted.replace(pattern, '[REDACTED]');
1825
+ }
1826
+ // Default patterns
1827
+ const defaultPatterns = [
1828
+ /api[_-]?key[\s:="']+[\w-]+/gi,
1829
+ /password[\s:="']+[\w-]+/gi,
1830
+ /Bearer\s+[\w-]+/gi,
1831
+ /sk-[a-zA-Z0-9]{48}/g
1832
+ ];
1833
+ for (const pattern of defaultPatterns){
1834
+ redacted = redacted.replace(pattern, '[REDACTED]');
1835
+ }
1836
+ return redacted;
1837
+ }
1838
+ constructor(config, logger){
1839
+ _define_property$6(this, "config", void 0);
1840
+ _define_property$6(this, "conversationId", void 0);
1841
+ _define_property$6(this, "metadata", void 0);
1842
+ _define_property$6(this, "messages", void 0);
1843
+ _define_property$6(this, "toolCalls", void 0);
1844
+ _define_property$6(this, "startTime", void 0);
1845
+ _define_property$6(this, "logger", void 0);
1846
+ _define_property$6(this, "messageIndex", void 0);
1847
+ this.config = {
1848
+ outputPath: 'logs/conversations',
1849
+ format: 'json',
1850
+ filenameTemplate: 'conversation-{timestamp}',
1851
+ includeMetadata: true,
1852
+ includePrompt: false,
1853
+ redactSensitive: false,
1854
+ redactPatterns: [],
1855
+ onSaved: ()=>{},
1856
+ onError: ()=>{},
1857
+ ...config
1858
+ };
1859
+ this.conversationId = this.generateId();
1860
+ this.messages = [];
1861
+ this.toolCalls = [];
1862
+ this.startTime = new Date();
1863
+ this.messageIndex = 0;
1864
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationLogger');
1865
+ this.metadata = {
1866
+ startTime: this.startTime,
1867
+ model: 'unknown'
1868
+ };
1869
+ }
1870
+ }
1871
+ /**
1872
+ * ConversationReplayer loads and replays logged conversations.
1873
+ *
1874
+ * Features:
1875
+ * - Load from various formats
1876
+ * - Replay conversations
1877
+ * - Compare replays with originals
1878
+ * - Export to different formats
1879
+ *
1880
+ * @example
1881
+ * ```typescript
1882
+ * const replayer = await ConversationReplayer.load('logs/conv.json');
1883
+ *
1884
+ * console.log('Messages:', replayer.messages.length);
1885
+ * console.log('Tool calls:', replayer.getToolCalls().length);
1886
+ *
1887
+ * const timeline = replayer.getTimeline();
1888
+ * console.log('Events:', timeline.length);
1889
+ * ```
1890
+ */ class ConversationReplayer {
1891
+ /**
1892
+ * Load conversation from file
1893
+ */ static async load(filePath, logger) {
1894
+ const wlogger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');
1895
+ wlogger.debug('Loading conversation', {
1896
+ path: filePath
1897
+ });
1898
+ try {
1899
+ const content = await fs.readFile(filePath, 'utf-8');
1900
+ // Determine format by extension
1901
+ if (filePath.endsWith('.json')) {
1902
+ const data = JSON.parse(content);
1903
+ return new ConversationReplayer(data, logger);
1904
+ } else if (filePath.endsWith('.jsonl')) {
1905
+ const lines = content.trim().split('\n');
1906
+ const messages = lines.map((line)=>JSON.parse(line));
1907
+ const conversation = {
1908
+ id: `replayer-${Date.now()}`,
1909
+ metadata: {
1910
+ startTime: new Date(),
1911
+ model: 'unknown'
1912
+ },
1913
+ messages,
1914
+ summary: {
1915
+ totalMessages: messages.length,
1916
+ toolCallsExecuted: 0,
1917
+ iterations: 0,
1918
+ success: true
1919
+ }
1920
+ };
1921
+ return new ConversationReplayer(conversation, logger);
1922
+ } else {
1923
+ throw new Error(`Unsupported format: ${filePath}`);
1924
+ }
1925
+ } catch (error) {
1926
+ wlogger.error('Failed to load conversation', {
1927
+ path: filePath,
1928
+ error
1929
+ });
1930
+ throw error;
1931
+ }
1932
+ }
1933
+ /**
1934
+ * Load latest conversation from directory
1935
+ */ static async loadLatest(directory, logger) {
1936
+ const files = await fs.readdir(directory);
1937
+ const jsonFiles = files.filter((f)=>f.endsWith('.json')).sort().reverse();
1938
+ if (jsonFiles.length === 0) {
1939
+ throw new Error(`No conversation logs found in ${directory}`);
1940
+ }
1941
+ const latestPath = path.join(directory, jsonFiles[0]);
1942
+ return ConversationReplayer.load(latestPath, logger);
1943
+ }
1944
+ /**
1945
+ * Get all messages
1946
+ */ get messages() {
1947
+ return this.conversation.messages;
1948
+ }
1949
+ /**
1950
+ * Get conversation metadata
1951
+ */ getMetadata() {
1952
+ return {
1953
+ ...this.conversation.metadata
1954
+ };
1955
+ }
1956
+ /**
1957
+ * Get tool calls
1958
+ */ getToolCalls() {
1959
+ const toolCalls = [];
1960
+ for (const msg of this.conversation.messages){
1961
+ if (msg.tool_calls) {
1962
+ for (const call of msg.tool_calls){
1963
+ toolCalls.push({
1964
+ callId: call.id,
1965
+ toolName: call.function.name,
1966
+ timestamp: msg.timestamp,
1967
+ iteration: 0,
1968
+ arguments: JSON.parse(call.function.arguments),
1969
+ result: null,
1970
+ duration: 0,
1971
+ success: true
1972
+ });
1973
+ }
1974
+ }
1975
+ }
1976
+ return toolCalls;
1977
+ }
1978
+ /**
1979
+ * Get message at index
1980
+ */ getMessageAt(index) {
1981
+ return this.conversation.messages[index];
1982
+ }
1983
+ /**
1984
+ * Get timeline of events
1985
+ */ getTimeline() {
1986
+ const events = [];
1987
+ for (const msg of this.conversation.messages){
1988
+ events.push({
1989
+ timestamp: msg.timestamp,
1990
+ iteration: 0,
1991
+ type: 'message',
1992
+ description: `${msg.role} message`
1993
+ });
1994
+ }
1995
+ return events;
1996
+ }
1997
+ /**
1998
+ * Export to format
1999
+ */ async exportToFormat(format, outputPath) {
2000
+ this.logger.debug('Exporting to format', {
2001
+ format,
2002
+ path: outputPath
2003
+ });
2004
+ switch(format){
2005
+ case 'json':
2006
+ await fs.writeFile(outputPath, JSON.stringify(this.conversation, null, 2), 'utf-8');
2007
+ break;
2008
+ case 'markdown':
2009
+ await this.exportMarkdown(outputPath);
2010
+ break;
2011
+ case 'jsonl':
2012
+ {
2013
+ const lines = this.messages.map((m)=>JSON.stringify(m)).join('\n');
2014
+ await fs.writeFile(outputPath, lines, 'utf-8');
2015
+ break;
2016
+ }
2017
+ }
2018
+ return outputPath;
2019
+ }
2020
+ /**
2021
+ * Export as markdown
2022
+ */ async exportMarkdown(outputPath) {
2023
+ let markdown = `# Conversation Log\n\n`;
2024
+ markdown += `**ID**: ${this.conversation.id}\n`;
2025
+ const startTime = typeof this.conversation.metadata.startTime === 'string' ? this.conversation.metadata.startTime : this.conversation.metadata.startTime.toISOString();
2026
+ markdown += `**Started**: ${startTime}\n\n`;
2027
+ for (const msg of this.conversation.messages){
2028
+ markdown += `## ${msg.role.toUpperCase()} (${msg.index})\n\n`;
2029
+ if (msg.content) {
2030
+ markdown += `${msg.content}\n\n`;
2031
+ }
2032
+ }
2033
+ await fs.writeFile(outputPath, markdown, 'utf-8');
2034
+ }
2035
+ constructor(conversation, logger){
2036
+ _define_property$6(this, "conversation", void 0);
2037
+ _define_property$6(this, "logger", void 0);
2038
+ this.conversation = conversation;
2039
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');
2040
+ }
2041
+ }
2042
+
2043
+ function _define_property$5(obj, key, value) {
2044
+ if (key in obj) {
2045
+ Object.defineProperty(obj, key, {
2046
+ value: value,
2047
+ enumerable: true,
2048
+ configurable: true,
2049
+ writable: true
2050
+ });
2051
+ } else {
2052
+ obj[key] = value;
2053
+ }
2054
+ return obj;
2055
+ }
2056
+ /**
2057
+ * MessageBuilder provides semantic, type-safe message construction.
2058
+ *
2059
+ * Features:
2060
+ * - Semantic message types (system, user, assistant, tool)
2061
+ * - Model-specific role handling (system vs developer)
2062
+ * - Structured content composition
2063
+ * - Metadata attachment
2064
+ * - Format-aware building
2065
+ *
2066
+ * @example
2067
+ * ```typescript
2068
+ * const message = MessageBuilder.system()
2069
+ * .withContent('You are a helpful assistant')
2070
+ * .withInstructions(instructionSection)
2071
+ * .buildForModel('gpt-4o');
2072
+ *
2073
+ * const toolMessage = MessageBuilder.tool('call_123')
2074
+ * .withResult(result)
2075
+ * .withMetadata({ duration: 45 })
2076
+ * .build();
2077
+ * ```
2078
+ */ class MessageBuilder {
2079
+ /**
2080
+ * Create system message builder
2081
+ */ static system(logger) {
2082
+ return new MessageBuilder('system', logger);
2083
+ }
2084
+ /**
2085
+ * Create user message builder
2086
+ */ static user(logger) {
2087
+ return new MessageBuilder('user', logger);
2088
+ }
2089
+ /**
2090
+ * Create assistant message builder
2091
+ */ static assistant(logger) {
2092
+ return new MessageBuilder('assistant', logger);
2093
+ }
2094
+ /**
2095
+ * Create tool message builder
2096
+ */ static tool(callId, logger) {
2097
+ const builder = new MessageBuilder('tool', logger);
2098
+ builder.toolCallId = callId;
2099
+ return builder;
2100
+ }
2101
+ /**
2102
+ * Create developer message builder (for o1 models)
2103
+ */ static developer(logger) {
2104
+ return new MessageBuilder('developer', logger);
2105
+ }
2106
+ /**
2107
+ * Add content to message
2108
+ */ withContent(content) {
2109
+ if (typeof content === 'string') {
2110
+ this.contentParts.push(content);
2111
+ } else {
2112
+ // Format section
2113
+ const formatter$1 = this.formatter || create$5();
2114
+ this.contentParts.push(formatter$1.format(content));
2115
+ }
2116
+ return this;
2117
+ }
2118
+ /**
2119
+ * Add persona section (typically for system messages)
2120
+ */ withPersona(persona) {
2121
+ const formatter$1 = this.formatter || create$5();
2122
+ this.contentParts.push(formatter$1.format(persona));
2123
+ return this;
2124
+ }
2125
+ /**
2126
+ * Add instructions section
2127
+ */ withInstructions(instructions) {
2128
+ if (Array.isArray(instructions)) {
2129
+ this.contentParts.push(instructions.join('\n'));
2130
+ } else {
2131
+ const formatter$1 = this.formatter || create$5();
2132
+ this.contentParts.push(formatter$1.format(instructions));
2133
+ }
2134
+ return this;
2135
+ }
2136
+ /**
2137
+ * Add context section
2138
+ */ withContext(context) {
2139
+ if (Array.isArray(context)) {
2140
+ const contextStr = context.map((c)=>c.title ? `## ${c.title}\n\n${c.content}` : c.content).join('\n\n');
2141
+ this.contentParts.push(contextStr);
2142
+ } else {
2143
+ const formatter$1 = this.formatter || create$5();
2144
+ this.contentParts.push(formatter$1.format(context));
2145
+ }
2146
+ return this;
2147
+ }
2148
+ /**
2149
+ * Set tool call ID (for tool messages)
2150
+ */ withCallId(id) {
2151
+ this.toolCallId = id;
2152
+ return this;
2153
+ }
2154
+ /**
2155
+ * Set tool result (for tool messages)
2156
+ */ withResult(result) {
2157
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
2158
+ this.contentParts.push(resultStr);
2159
+ return this;
2160
+ }
2161
+ /**
2162
+ * Add tool calls (for assistant messages)
2163
+ */ withToolCalls(calls) {
2164
+ this.toolCalls = calls;
2165
+ return this;
2166
+ }
2167
+ /**
2168
+ * Add metadata
2169
+ */ withMetadata(metadata) {
2170
+ this.metadata = {
2171
+ ...this.metadata,
2172
+ ...metadata
2173
+ };
2174
+ return this;
2175
+ }
2176
+ /**
2177
+ * Add timestamp to metadata
2178
+ */ withTimestamp() {
2179
+ this.metadata.timestamp = new Date();
2180
+ return this;
2181
+ }
2182
+ /**
2183
+ * Set priority in metadata
2184
+ */ withPriority(priority) {
2185
+ this.metadata.priority = priority;
2186
+ return this;
2187
+ }
2188
+ /**
2189
+ * Set formatter for section rendering
2190
+ */ withFormatter(formatter) {
2191
+ this.formatter = formatter;
2192
+ return this;
2193
+ }
2194
+ /**
2195
+ * Build message with semantic role
2196
+ */ build() {
2197
+ const content = this.contentParts.join('\n\n');
2198
+ const message = {
2199
+ role: this.semanticRole,
2200
+ content: content || null
2201
+ };
2202
+ // Add tool-specific fields
2203
+ if (this.semanticRole === 'tool' && this.toolCallId) {
2204
+ message.tool_call_id = this.toolCallId;
2205
+ }
2206
+ if (this.toolCalls) {
2207
+ message.tool_calls = this.toolCalls;
2208
+ }
2209
+ return message;
2210
+ }
2211
+ /**
2212
+ * Build message with model-specific role
2213
+ */ buildForModel(model) {
2214
+ const message = this.build();
2215
+ // Handle model-specific role requirements
2216
+ if (this.semanticRole === 'system') {
2217
+ // O1 models use 'developer' instead of 'system'
2218
+ if (model.startsWith('o1') || model.startsWith('o3') || model === 'o1-pro') {
2219
+ message.role = 'developer';
2220
+ }
2221
+ }
2222
+ return message;
2223
+ }
2224
+ constructor(role, logger){
2225
+ _define_property$5(this, "semanticRole", void 0);
2226
+ _define_property$5(this, "contentParts", void 0);
2227
+ _define_property$5(this, "metadata", void 0);
2228
+ _define_property$5(this, "formatter", void 0);
2229
+ _define_property$5(this, "toolCallId", void 0);
2230
+ _define_property$5(this, "toolCalls", void 0);
2231
+ _define_property$5(this, "logger", void 0);
2232
+ this.semanticRole = role;
2233
+ this.contentParts = [];
2234
+ this.metadata = {};
2235
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'MessageBuilder');
2236
+ }
2237
+ }
2238
+ /**
2239
+ * Message template functions for common patterns
2240
+ */ const MessageTemplates = {
2241
+ /**
2242
+ * System message for agentic tasks
2243
+ */ agenticSystem: (persona, instructions)=>{
2244
+ const builder = MessageBuilder.system();
2245
+ if (persona) {
2246
+ builder.withContent(persona);
2247
+ }
2248
+ if (instructions) {
2249
+ builder.withInstructions(instructions);
2250
+ }
2251
+ return builder;
2252
+ },
2253
+ /**
2254
+ * User query with optional context
2255
+ */ userQuery: (query, context)=>{
2256
+ const builder = MessageBuilder.user().withContent(query);
2257
+ if (context) {
2258
+ builder.withContext(context);
2259
+ }
2260
+ return builder;
2261
+ },
2262
+ /**
2263
+ * Tool result with metadata
2264
+ */ toolResult: (callId, result, metadata)=>{
2265
+ const builder = MessageBuilder.tool(callId).withResult(result).withTimestamp();
2266
+ if (metadata) {
2267
+ builder.withMetadata(metadata);
2268
+ }
2269
+ return builder;
2270
+ },
2271
+ /**
2272
+ * Tool success result
2273
+ */ toolSuccess: (callId, result, duration)=>{
2274
+ return MessageBuilder.tool(callId).withResult(result).withMetadata({
2275
+ success: true,
2276
+ duration
2277
+ }).withTimestamp();
2278
+ },
2279
+ /**
2280
+ * Tool failure result
2281
+ */ toolFailure: (callId, error)=>{
2282
+ return MessageBuilder.tool(callId).withResult({
2283
+ error: error.message,
2284
+ stack: error.stack
2285
+ }).withMetadata({
2286
+ success: false,
2287
+ errorName: error.name
2288
+ }).withTimestamp();
2289
+ }
2290
+ };
2291
+
2292
+ function _define_property$4(obj, key, value) {
2293
+ if (key in obj) {
2294
+ Object.defineProperty(obj, key, {
2295
+ value: value,
2296
+ enumerable: true,
2297
+ configurable: true,
2298
+ writable: true
2299
+ });
2300
+ } else {
2301
+ obj[key] = value;
2302
+ }
2303
+ return obj;
2304
+ }
2305
+ // ===== TOKEN COUNTER =====
2306
+ /**
2307
+ * TokenCounter counts tokens using tiktoken for accurate model-specific counting.
2308
+ *
2309
+ * Features:
2310
+ * - Model-specific token counting
2311
+ * - Message overhead calculation
2312
+ * - Tool call token estimation
2313
+ * - Response token estimation
2314
+ *
2315
+ * @example
2316
+ * ```typescript
2317
+ * const counter = new TokenCounter('gpt-4o');
2318
+ *
2319
+ * const tokens = counter.count('Hello, world!');
2320
+ * console.log(`Text uses ${tokens} tokens`);
2321
+ *
2322
+ * const messageTokens = counter.countMessage({
2323
+ * role: 'user',
2324
+ * content: 'What is the weather?'
2325
+ * });
2326
+ * ```
2327
+ */ class TokenCounter {
2328
+ /**
2329
+ * Count tokens in text
2330
+ */ count(text) {
2331
+ if (!text) return 0;
2332
+ return this.encoder.encode(text).length;
2333
+ }
2334
+ /**
2335
+ * Count tokens in a single message
2336
+ */ countMessage(message) {
2337
+ let tokens = 4; // Base overhead per message
2338
+ // Content tokens
2339
+ if (message.content) {
2340
+ tokens += this.count(message.content);
2341
+ }
2342
+ // Role tokens
2343
+ tokens += 1;
2344
+ // Tool call tokens
2345
+ if (message.tool_calls) {
2346
+ for (const toolCall of message.tool_calls){
2347
+ tokens += this.count(JSON.stringify(toolCall));
2348
+ tokens += 3; // Tool call overhead
2349
+ }
2350
+ }
2351
+ // Tool result tokens
2352
+ if (message.tool_call_id) {
2353
+ tokens += this.count(message.tool_call_id);
2354
+ tokens += 2; // Tool result overhead
2355
+ }
2356
+ return tokens;
2357
+ }
2358
+ /**
2359
+ * Count tokens in entire conversation
2360
+ */ countConversation(messages) {
2361
+ let total = 3; // Conversation start overhead
2362
+ for (const message of messages){
2363
+ total += this.countMessage(message);
2364
+ }
2365
+ return total;
2366
+ }
2367
+ /**
2368
+ * Count with additional overhead estimation
2369
+ */ countWithOverhead(messages, includeToolOverhead = false) {
2370
+ let total = this.countConversation(messages);
2371
+ // Add tool definition overhead if tools are present
2372
+ if (includeToolOverhead) {
2373
+ const hasTools = messages.some((m)=>m.tool_calls && m.tool_calls.length > 0);
2374
+ if (hasTools) {
2375
+ total += 100; // Estimated tool definition overhead
2376
+ }
2377
+ }
2378
+ return total;
2379
+ }
2380
+ /**
2381
+ * Estimate tokens needed for response
2382
+ */ estimateResponseTokens(messages) {
2383
+ // Heuristic: average response is about 20% of input
2384
+ const inputTokens = this.countConversation(messages);
2385
+ return Math.max(500, Math.floor(inputTokens * 0.2));
2386
+ }
2387
+ /**
2388
+ * Map RiotPrompt model to Tiktoken model
2389
+ */ mapToTiktokenModel(model) {
2390
+ switch(model){
2391
+ case 'gpt-4o':
2392
+ case 'gpt-4o-mini':
2393
+ return 'gpt-4o';
2394
+ case 'o1-preview':
2395
+ case 'o1-mini':
2396
+ case 'o1':
2397
+ case 'o3-mini':
2398
+ case 'o1-pro':
2399
+ // O1 models use gpt-4o tokenization
2400
+ return 'gpt-4o';
2401
+ default:
2402
+ return 'gpt-4o';
2403
+ }
2404
+ }
2405
+ /**
2406
+ * Free encoder resources
2407
+ */ dispose() {
2408
+ this.encoder.free();
2409
+ }
2410
+ constructor(model, logger){
2411
+ _define_property$4(this, "encoder", void 0);
2412
+ _define_property$4(this, "model", void 0);
2413
+ _define_property$4(this, "logger", void 0);
2414
+ this.model = model;
2415
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'TokenCounter');
2416
+ // Map RiotPrompt models to Tiktoken models
2417
+ const tiktokenModel = this.mapToTiktokenModel(model);
2418
+ this.encoder = tiktoken.encoding_for_model(tiktokenModel);
2419
+ this.logger.debug('Created TokenCounter', {
2420
+ model
2421
+ });
2422
+ }
2423
+ }
2424
+ // ===== TOKEN BUDGET MANAGER =====
2425
+ /**
2426
+ * TokenBudgetManager manages token budgets and compression strategies.
2427
+ *
2428
+ * Features:
2429
+ * - Monitor token usage
2430
+ * - Automatic compression when budget exceeded
2431
+ * - Multiple compression strategies
2432
+ * - Priority-based message retention
2433
+ * - Usage statistics and callbacks
2434
+ *
2435
+ * @example
2436
+ * ```typescript
2437
+ * const manager = new TokenBudgetManager({
2438
+ * max: 8000,
2439
+ * reserveForResponse: 1000,
2440
+ * strategy: 'priority-based',
2441
+ * onBudgetExceeded: 'compress'
2442
+ * }, 'gpt-4o');
2443
+ *
2444
+ * // Check if message can be added
2445
+ * if (manager.canAddMessage(message)) {
2446
+ * messages.push(message);
2447
+ * } else {
2448
+ * // Compress conversation
2449
+ * messages = manager.compress(messages);
2450
+ * messages.push(message);
2451
+ * }
2452
+ * ```
2453
+ */ class TokenBudgetManager {
2454
+ /**
2455
+ * Get current token usage
2456
+ */ getCurrentUsage(messages) {
2457
+ const used = this.counter.countConversation(messages);
2458
+ const max = this.config.max;
2459
+ const remaining = Math.max(0, max - used - this.config.reserveForResponse);
2460
+ const percentage = used / max * 100;
2461
+ return {
2462
+ used,
2463
+ max,
2464
+ remaining,
2465
+ percentage
2466
+ };
2467
+ }
2468
+ /**
2469
+ * Get remaining tokens available
2470
+ */ getRemainingTokens(messages) {
2471
+ return this.getCurrentUsage(messages).remaining;
2472
+ }
2473
+ /**
2474
+ * Check if near token limit
2475
+ */ isNearLimit(messages, threshold) {
2476
+ const usage = this.getCurrentUsage(messages);
2477
+ const checkThreshold = threshold !== null && threshold !== void 0 ? threshold : this.config.warningThreshold;
2478
+ const isNear = usage.percentage >= checkThreshold * 100;
2479
+ if (isNear) {
2480
+ var _this_config_onWarning, _this_config;
2481
+ (_this_config_onWarning = (_this_config = this.config).onWarning) === null || _this_config_onWarning === void 0 ? void 0 : _this_config_onWarning.call(_this_config, usage);
2482
+ }
2483
+ return isNear;
2484
+ }
2485
+ /**
2486
+ * Check if a message can be added without exceeding budget
2487
+ */ canAddMessage(message, currentMessages) {
2488
+ const currentTokens = this.counter.countConversation(currentMessages);
2489
+ const messageTokens = this.counter.countMessage(message);
2490
+ const total = currentTokens + messageTokens + this.config.reserveForResponse;
2491
+ return total <= this.config.max;
2492
+ }
2493
+ /**
2494
+ * Compress messages according to strategy
2495
+ */ compress(messages) {
2496
+ var _this_config_onCompression, _this_config;
2497
+ const before = messages.length;
2498
+ const tokensBefore = this.counter.countConversation(messages);
2499
+ const targetTokens = this.config.max - this.config.reserveForResponse;
2500
+ this.logger.debug('Compressing messages', {
2501
+ before,
2502
+ tokensBefore,
2503
+ targetTokens,
2504
+ strategy: this.config.strategy
2505
+ });
2506
+ // No compression needed
2507
+ if (tokensBefore <= targetTokens) {
2508
+ return messages;
2509
+ }
2510
+ let compressed;
2511
+ switch(this.config.strategy){
2512
+ case 'priority-based':
2513
+ compressed = this.compressByPriority(messages, targetTokens);
2514
+ break;
2515
+ case 'fifo':
2516
+ compressed = this.compressFIFO(messages, targetTokens);
2517
+ break;
2518
+ case 'adaptive':
2519
+ compressed = this.compressAdaptive(messages, targetTokens);
2520
+ break;
2521
+ case 'summarize':
2522
+ // For now, fall back to FIFO (summarization would require LLM call)
2523
+ compressed = this.compressFIFO(messages, targetTokens);
2524
+ break;
2525
+ default:
2526
+ compressed = this.compressFIFO(messages, targetTokens);
2527
+ }
2528
+ const tokensAfter = this.counter.countConversation(compressed);
2529
+ const stats = {
2530
+ messagesBefore: before,
2531
+ messagesAfter: compressed.length,
2532
+ tokensBefore,
2533
+ tokensAfter,
2534
+ tokensSaved: tokensBefore - tokensAfter,
2535
+ strategy: this.config.strategy
2536
+ };
2537
+ (_this_config_onCompression = (_this_config = this.config).onCompression) === null || _this_config_onCompression === void 0 ? void 0 : _this_config_onCompression.call(_this_config, stats);
2538
+ this.logger.info('Compressed conversation', stats);
2539
+ return compressed;
2540
+ }
2541
+ /**
2542
+ * Compress by priority (keep high-priority messages)
2543
+ */ compressByPriority(messages, targetTokens) {
2544
+ // Calculate priority for each message
2545
+ const withPriority = messages.map((msg, idx)=>({
2546
+ message: msg,
2547
+ priority: this.calculatePriority(msg, idx, messages.length),
2548
+ tokens: this.counter.countMessage(msg),
2549
+ index: idx
2550
+ }));
2551
+ // Sort by priority (descending)
2552
+ withPriority.sort((a, b)=>b.priority - a.priority);
2553
+ // Keep highest priority messages that fit in budget
2554
+ const kept = [];
2555
+ let totalTokens = 0;
2556
+ for (const item of withPriority){
2557
+ if (totalTokens + item.tokens <= targetTokens) {
2558
+ kept.push(item);
2559
+ totalTokens += item.tokens;
2560
+ }
2561
+ }
2562
+ // Sort back to original order
2563
+ kept.sort((a, b)=>a.index - b.index);
2564
+ return kept.map((item)=>item.message);
2565
+ }
2566
+ /**
2567
+ * Compress using FIFO (remove oldest first)
2568
+ */ compressFIFO(messages, targetTokens) {
2569
+ var _this_config_preserveRecent;
2570
+ const preserved = [];
2571
+ let totalTokens = 0;
2572
+ // Always preserve system messages if configured
2573
+ const systemMessages = messages.filter((m)=>m.role === 'system');
2574
+ if (this.config.preserveSystem) {
2575
+ for (const msg of systemMessages){
2576
+ preserved.push(msg);
2577
+ totalTokens += this.counter.countMessage(msg);
2578
+ }
2579
+ }
2580
+ // Preserve recent messages
2581
+ const recentCount = (_this_config_preserveRecent = this.config.preserveRecent) !== null && _this_config_preserveRecent !== void 0 ? _this_config_preserveRecent : 3;
2582
+ const recentMessages = messages.slice(-recentCount).filter((m)=>m.role !== 'system');
2583
+ for (const msg of recentMessages){
2584
+ if (!preserved.includes(msg)) {
2585
+ const tokens = this.counter.countMessage(msg);
2586
+ if (totalTokens + tokens <= targetTokens) {
2587
+ preserved.push(msg);
2588
+ totalTokens += tokens;
2589
+ }
2590
+ }
2591
+ }
2592
+ // Add older messages if space available
2593
+ const otherMessages = messages.filter((m)=>!preserved.includes(m) && m.role !== 'system');
2594
+ for(let i = otherMessages.length - 1; i >= 0; i--){
2595
+ const msg = otherMessages[i];
2596
+ const tokens = this.counter.countMessage(msg);
2597
+ if (totalTokens + tokens <= targetTokens) {
2598
+ preserved.unshift(msg);
2599
+ totalTokens += tokens;
2600
+ } else {
2601
+ break;
2602
+ }
2603
+ }
2604
+ // Sort to maintain conversation order
2605
+ return messages.filter((m)=>preserved.includes(m));
2606
+ }
2607
+ /**
2608
+ * Adaptive compression based on conversation phase
2609
+ */ compressAdaptive(messages, targetTokens) {
2610
+ const messageCount = messages.length;
2611
+ // Early phase: minimal compression (keep most messages)
2612
+ if (messageCount <= 5) {
2613
+ return this.compressFIFO(messages, targetTokens);
2614
+ }
2615
+ // Mid phase: moderate compression
2616
+ if (messageCount <= 15) {
2617
+ // Use FIFO but preserve more recent messages
2618
+ const modifiedConfig = {
2619
+ ...this.config,
2620
+ preserveRecent: 5
2621
+ };
2622
+ const tempManager = new TokenBudgetManager(modifiedConfig, 'gpt-4o', this.logger);
2623
+ return tempManager.compressFIFO(messages, targetTokens);
2624
+ }
2625
+ // Late phase: aggressive compression (priority-based)
2626
+ return this.compressByPriority(messages, targetTokens);
2627
+ }
2628
+ /**
2629
+ * Calculate message priority for compression
2630
+ */ calculatePriority(message, index, total) {
2631
+ let priority = 1.0;
2632
+ // System messages: highest priority
2633
+ if (message.role === 'system') {
2634
+ priority = 10.0;
2635
+ }
2636
+ // Recent messages: higher priority
2637
+ const recencyBonus = index / total;
2638
+ priority += recencyBonus * 2;
2639
+ // Tool results: moderate priority
2640
+ if (message.role === 'tool') {
2641
+ priority += 0.5;
2642
+ }
2643
+ // Messages with tool calls: keep for context
2644
+ if (message.tool_calls && message.tool_calls.length > 0) {
2645
+ priority += 0.8;
2646
+ }
2647
+ return priority;
2648
+ }
2649
+ /**
2650
+ * Truncate to exact number of messages
2651
+ */ truncate(messages, maxMessages) {
2652
+ if (messages.length <= maxMessages) {
2653
+ return messages;
2654
+ }
2655
+ // Keep system messages + recent messages
2656
+ const systemMessages = messages.filter((m)=>m.role === 'system');
2657
+ const otherMessages = messages.filter((m)=>m.role !== 'system');
2658
+ const recentOther = otherMessages.slice(-(maxMessages - systemMessages.length));
2659
+ return [
2660
+ ...systemMessages,
2661
+ ...recentOther
2662
+ ];
2663
+ }
2664
+ /**
2665
+ * Dispose resources
2666
+ */ dispose() {
2667
+ this.counter.dispose();
2668
+ }
2669
+ constructor(config, model, logger){
2670
+ _define_property$4(this, "config", void 0);
2671
+ _define_property$4(this, "counter", void 0);
2672
+ _define_property$4(this, "logger", void 0);
2673
+ this.config = {
2674
+ warningThreshold: 0.8,
2675
+ preserveRecent: 3,
2676
+ preserveSystem: true,
2677
+ preserveHighPriority: true,
2678
+ onWarning: ()=>{},
2679
+ onCompression: ()=>{},
2680
+ ...config
2681
+ };
2682
+ this.counter = new TokenCounter(model, logger);
2683
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'TokenBudgetManager');
2684
+ this.logger.debug('Created TokenBudgetManager', {
2685
+ max: this.config.max,
2686
+ strategy: this.config.strategy
2687
+ });
2688
+ }
2689
+ }
2690
+
2691
+ function _define_property$3(obj, key, value) {
2692
+ if (key in obj) {
2693
+ Object.defineProperty(obj, key, {
2694
+ value: value,
2695
+ enumerable: true,
2696
+ configurable: true,
2697
+ writable: true
2698
+ });
2699
+ } else {
2700
+ obj[key] = value;
2701
+ }
2702
+ return obj;
2703
+ }
2704
+ // ===== SCHEMAS =====
2705
+ const ConversationBuilderConfigSchema = zod.z.object({
2706
+ model: zod.z.string(),
2707
+ formatter: zod.z.any().optional(),
2708
+ trackContext: zod.z.boolean().optional().default(true),
2709
+ deduplicateContext: zod.z.boolean().optional().default(true)
2710
+ });
2711
+ // ===== CONVERSATION BUILDER =====
2712
+ /**
2713
+ * ConversationBuilder manages multi-turn conversations with full lifecycle support.
2714
+ *
2715
+ * Features:
2716
+ * - Initialize from RiotPrompt prompts
2717
+ * - Add messages of any type (system, user, assistant, tool)
2718
+ * - Handle tool calls and results
2719
+ * - Inject dynamic context
2720
+ * - Clone for parallel exploration
2721
+ * - Serialize/deserialize for persistence
2722
+ *
2723
+ * @example
2724
+ * ```typescript
2725
+ * // Create from prompt
2726
+ * const conversation = ConversationBuilder.create()
2727
+ * .fromPrompt(prompt, 'gpt-4o')
2728
+ * .build();
2729
+ *
2730
+ * // Add messages
2731
+ * conversation.addUserMessage('Analyze this code');
2732
+ *
2733
+ * // Handle tool calls
2734
+ * conversation.addAssistantWithToolCalls(null, toolCalls);
2735
+ * conversation.addToolResult(toolCallId, result);
2736
+ *
2737
+ * // Export
2738
+ * const messages = conversation.toMessages();
2739
+ * ```
2740
+ */ class ConversationBuilder {
2741
+ /**
2742
+ * Create a new ConversationBuilder instance
2743
+ */ static create(config, logger) {
2744
+ const defaultConfig = {
2745
+ model: 'gpt-4o',
2746
+ trackContext: true,
2747
+ deduplicateContext: true,
2748
+ ...config
2749
+ };
2750
+ return new ConversationBuilder(defaultConfig, logger);
2751
+ }
2752
+ /**
2753
+ * Initialize conversation from a RiotPrompt prompt
2754
+ */ fromPrompt(prompt, model) {
2755
+ const targetModel = model || this.config.model;
2756
+ this.logger.debug('Initializing from prompt', {
2757
+ model: targetModel
2758
+ });
2759
+ // Use formatter (provided or create new one)
2760
+ const formatter$1 = this.config.formatter || create$5();
2761
+ const request = formatter$1.formatPrompt(targetModel, prompt);
2762
+ // Add all messages from formatted request
2763
+ request.messages.forEach((msg)=>{
2764
+ this.state.messages.push(msg);
2765
+ });
2766
+ this.updateMetadata();
2767
+ this.logger.debug('Initialized from prompt', {
2768
+ messageCount: this.state.messages.length
2769
+ });
2770
+ return this;
2771
+ }
2772
+ /**
2773
+ * Add a system message
2774
+ */ addSystemMessage(content) {
2775
+ this.logger.debug('Adding system message');
2776
+ let messageContent;
2777
+ if (typeof content === 'string') {
2778
+ messageContent = content;
2779
+ } else {
2780
+ // Format section using formatter
2781
+ const formatter$1 = this.config.formatter || create$5();
2782
+ messageContent = formatter$1.format(content);
2783
+ }
2784
+ this.state.messages.push({
2785
+ role: 'system',
2786
+ content: messageContent
2787
+ });
2788
+ this.updateMetadata();
2789
+ return this;
2790
+ }
2791
+ /**
2792
+ * Add a user message (with automatic budget management)
2793
+ */ addUserMessage(content) {
2794
+ this.logger.debug('Adding user message');
2795
+ let messageContent;
2796
+ if (typeof content === 'string') {
2797
+ messageContent = content;
2798
+ } else {
2799
+ // Format section using formatter
2800
+ const formatter$1 = this.config.formatter || create$5();
2801
+ messageContent = formatter$1.format(content);
2802
+ }
2803
+ const message = {
2804
+ role: 'user',
2805
+ content: messageContent
2806
+ };
2807
+ // Check budget if enabled
2808
+ if (this.budgetManager) {
2809
+ if (!this.budgetManager.canAddMessage(message, this.state.messages)) {
2810
+ this.logger.warn('Budget exceeded, compressing conversation');
2811
+ this.state.messages = this.budgetManager.compress(this.state.messages);
2812
+ }
2813
+ }
2814
+ this.state.messages.push(message);
2815
+ this.updateMetadata();
2816
+ return this;
2817
+ }
2818
+ /**
2819
+ * Add an assistant message
2820
+ */ addAssistantMessage(content) {
2821
+ this.logger.debug('Adding assistant message');
2822
+ this.state.messages.push({
2823
+ role: 'assistant',
2824
+ content: content || ''
2825
+ });
2826
+ this.updateMetadata();
2827
+ return this;
2828
+ }
2829
+ /**
2830
+ * Add an assistant message with tool calls
2831
+ */ addAssistantWithToolCalls(content, toolCalls) {
2832
+ this.logger.debug('Adding assistant message with tool calls', {
2833
+ toolCount: toolCalls.length
2834
+ });
2835
+ this.state.messages.push({
2836
+ role: 'assistant',
2837
+ content: content,
2838
+ tool_calls: toolCalls
2839
+ });
2840
+ this.state.metadata.toolCallCount += toolCalls.length;
2841
+ this.updateMetadata();
2842
+ return this;
2843
+ }
2844
+ /**
2845
+ * Add a tool result message
2846
+ */ addToolResult(toolCallId, content, toolName) {
2847
+ this.logger.debug('Adding tool result', {
2848
+ toolCallId,
2849
+ toolName
2850
+ });
2851
+ const message = {
2852
+ role: 'tool',
2853
+ tool_call_id: toolCallId,
2854
+ content: content
2855
+ };
2856
+ if (toolName) {
2857
+ message.name = toolName;
2858
+ }
2859
+ this.state.messages.push(message);
2860
+ this.updateMetadata();
2861
+ return this;
2862
+ }
2863
+ /**
2864
+ * Alias for addToolResult (more intuitive naming)
2865
+ */ addToolMessage(toolCallId, content, toolName) {
2866
+ return this.addToolResult(toolCallId, content, toolName);
2867
+ }
2868
+ /**
2869
+ * Inject context into the conversation with advanced options
2870
+ *
2871
+ * @param context - Array of content items to inject
2872
+ * @param options - Injection options (position, format, deduplication, etc.)
2873
+ */ injectContext(context, options) {
2874
+ var _this_config_deduplicateContext;
2875
+ const opts = {
2876
+ position: 'end',
2877
+ format: 'structured',
2878
+ deduplicate: (_this_config_deduplicateContext = this.config.deduplicateContext) !== null && _this_config_deduplicateContext !== void 0 ? _this_config_deduplicateContext : true,
2879
+ deduplicateBy: 'id',
2880
+ priority: 'medium',
2881
+ weight: 1.0,
2882
+ category: undefined,
2883
+ source: undefined,
2884
+ ...options
2885
+ };
2886
+ this.logger.debug('Injecting context', {
2887
+ itemCount: context.length,
2888
+ options: opts
2889
+ });
2890
+ // Filter out duplicates if enabled
2891
+ const itemsToAdd = [];
2892
+ for (const item of context){
2893
+ const enrichedItem = {
2894
+ ...item,
2895
+ priority: item.priority || opts.priority,
2896
+ weight: item.weight || opts.weight,
2897
+ category: item.category || opts.category,
2898
+ source: item.source || opts.source,
2899
+ timestamp: item.timestamp || new Date()
2900
+ };
2901
+ // Check deduplication
2902
+ if (opts.deduplicate) {
2903
+ let skip = false;
2904
+ switch(opts.deduplicateBy){
2905
+ case 'id':
2906
+ if (enrichedItem.id && this.state.contextManager.hasContext(enrichedItem.id)) {
2907
+ this.logger.debug('Skipping duplicate context by ID', {
2908
+ id: enrichedItem.id
2909
+ });
2910
+ skip = true;
2911
+ }
2912
+ break;
2913
+ case 'hash':
2914
+ if (this.state.contextManager.hasContentHash(enrichedItem.content)) {
2915
+ this.logger.debug('Skipping duplicate context by hash');
2916
+ skip = true;
2917
+ }
2918
+ break;
2919
+ case 'content':
2920
+ if (this.state.contextManager.hasSimilarContent(enrichedItem.content)) {
2921
+ this.logger.debug('Skipping duplicate context by content');
2922
+ skip = true;
2923
+ }
2924
+ break;
2925
+ }
2926
+ if (skip) {
2927
+ continue;
2928
+ }
2929
+ }
2930
+ itemsToAdd.push(enrichedItem);
2931
+ }
2932
+ // Only proceed if we have items to add
2933
+ if (itemsToAdd.length === 0) {
2934
+ return this;
2935
+ }
2936
+ // Calculate position
2937
+ const position = this.calculatePosition(opts.position);
2938
+ // Format and inject
2939
+ for (const item of itemsToAdd){
2940
+ const formatted = this.formatContextItem(item, opts.format);
2941
+ const contextMessage = {
2942
+ role: 'user',
2943
+ content: formatted
2944
+ };
2945
+ this.state.messages.splice(position, 0, contextMessage);
2946
+ // Track in context manager
2947
+ this.state.contextManager.track(item, position);
2948
+ }
2949
+ this.updateMetadata();
2950
+ return this;
2951
+ }
2952
+ /**
2953
+ * Inject system-level context
2954
+ */ injectSystemContext(context) {
2955
+ this.logger.debug('Injecting system context');
2956
+ let messageContent;
2957
+ if (typeof context === 'string') {
2958
+ messageContent = context;
2959
+ } else {
2960
+ const formatter$1 = this.config.formatter || create$5();
2961
+ messageContent = formatter$1.format(context);
2962
+ }
2963
+ this.state.messages.push({
2964
+ role: 'system',
2965
+ content: messageContent
2966
+ });
2967
+ this.updateMetadata();
2968
+ return this;
2969
+ }
2970
+ /**
2971
+ * Get the number of messages in the conversation
2972
+ */ getMessageCount() {
2973
+ return this.state.messages.length;
2974
+ }
2975
+ /**
2976
+ * Get the last message in the conversation
2977
+ */ getLastMessage() {
2978
+ return this.state.messages[this.state.messages.length - 1];
2979
+ }
2980
+ /**
2981
+ * Get all messages
2982
+ */ getMessages() {
2983
+ return [
2984
+ ...this.state.messages
2985
+ ];
2986
+ }
2987
+ /**
2988
+ * Check if conversation has any tool calls
2989
+ */ hasToolCalls() {
2990
+ return this.state.metadata.toolCallCount > 0;
2991
+ }
2992
+ /**
2993
+ * Get conversation metadata
2994
+ */ getMetadata() {
2995
+ return {
2996
+ ...this.state.metadata
2997
+ };
2998
+ }
2999
+ /**
3000
+ * Export messages in OpenAI format
3001
+ */ toMessages() {
3002
+ return this.state.messages.map((msg)=>({
3003
+ ...msg
3004
+ }));
3005
+ }
3006
+ /**
3007
+ * Serialize conversation to JSON
3008
+ */ toJSON() {
3009
+ const serialized = {
3010
+ messages: this.state.messages,
3011
+ metadata: {
3012
+ ...this.state.metadata,
3013
+ created: this.state.metadata.created.toISOString(),
3014
+ lastModified: this.state.metadata.lastModified.toISOString()
3015
+ },
3016
+ contextProvided: Array.from(this.state.contextProvided)
3017
+ };
3018
+ return JSON.stringify(serialized, null, 2);
3019
+ }
3020
+ /**
3021
+ * Restore conversation from JSON
3022
+ */ static fromJSON(json, config, logger) {
3023
+ const parsed = JSON.parse(json);
3024
+ const builder = ConversationBuilder.create({
3025
+ model: parsed.metadata.model,
3026
+ ...config
3027
+ }, logger);
3028
+ // Restore state
3029
+ builder.state.messages = parsed.messages;
3030
+ builder.state.metadata = {
3031
+ ...parsed.metadata,
3032
+ created: new Date(parsed.metadata.created),
3033
+ lastModified: new Date(parsed.metadata.lastModified)
3034
+ };
3035
+ builder.state.contextProvided = new Set(parsed.contextProvided);
3036
+ return builder;
3037
+ }
3038
+ /**
3039
+ * Clone the conversation for parallel exploration
3040
+ */ clone() {
3041
+ this.logger.debug('Cloning conversation');
3042
+ const cloned = ConversationBuilder.create({
3043
+ ...this.config
3044
+ }, this.logger);
3045
+ // Deep copy state (note: contextManager is already created in constructor)
3046
+ cloned.state.messages = this.state.messages.map((msg)=>({
3047
+ ...msg
3048
+ }));
3049
+ cloned.state.metadata = {
3050
+ ...this.state.metadata
3051
+ };
3052
+ cloned.state.contextProvided = new Set(this.state.contextProvided);
3053
+ // Copy context manager state
3054
+ const allContext = this.state.contextManager.getAll();
3055
+ allContext.forEach((item)=>{
3056
+ cloned.state.contextManager.track(item, item.position);
3057
+ });
3058
+ return cloned;
3059
+ }
3060
+ /**
3061
+ * Truncate conversation to last N messages
3062
+ */ truncate(maxMessages) {
3063
+ this.logger.debug('Truncating conversation', {
3064
+ maxMessages,
3065
+ current: this.state.messages.length
3066
+ });
3067
+ if (this.state.messages.length > maxMessages) {
3068
+ this.state.messages = this.state.messages.slice(-maxMessages);
3069
+ this.updateMetadata();
3070
+ }
3071
+ return this;
3072
+ }
3073
+ /**
3074
+ * Remove all messages of a specific type
3075
+ */ removeMessagesOfType(role) {
3076
+ this.logger.debug('Removing messages of type', {
3077
+ role
3078
+ });
3079
+ this.state.messages = this.state.messages.filter((msg)=>msg.role !== role);
3080
+ this.updateMetadata();
3081
+ return this;
3082
+ }
3083
+ /**
3084
+ * Get the context manager
3085
+ */ getContextManager() {
3086
+ return this.state.contextManager;
3087
+ }
3088
+ /**
3089
+ * Get conversation state (for conditional injection)
3090
+ */ getState() {
3091
+ return {
3092
+ messages: [
3093
+ ...this.state.messages
3094
+ ],
3095
+ metadata: {
3096
+ ...this.state.metadata
3097
+ },
3098
+ contextProvided: new Set(this.state.contextProvided),
3099
+ contextManager: this.state.contextManager
3100
+ };
3101
+ }
3102
+ // ===== SEMANTIC MESSAGE METHODS (Feature 5) =====
3103
+ /**
3104
+ * Add a system message using semantic builder
3105
+ */ asSystem(content) {
3106
+ const message = MessageBuilder.system(this.logger).withContent(content).withFormatter(this.config.formatter || create$5()).buildForModel(this.config.model);
3107
+ this.state.messages.push(message);
3108
+ this.updateMetadata();
3109
+ return this;
3110
+ }
3111
+ /**
3112
+ * Add a user message using semantic builder
3113
+ */ asUser(content) {
3114
+ const message = MessageBuilder.user(this.logger).withContent(content).withFormatter(this.config.formatter || create$5()).buildForModel(this.config.model);
3115
+ // Check budget if enabled
3116
+ if (this.budgetManager) {
3117
+ if (!this.budgetManager.canAddMessage(message, this.state.messages)) {
3118
+ this.logger.warn('Budget exceeded, compressing conversation');
3119
+ this.state.messages = this.budgetManager.compress(this.state.messages);
3120
+ }
3121
+ }
3122
+ this.state.messages.push(message);
3123
+ this.updateMetadata();
3124
+ return this;
3125
+ }
3126
+ /**
3127
+ * Add an assistant message using semantic builder
3128
+ */ asAssistant(content, toolCalls) {
3129
+ const builder = MessageBuilder.assistant(this.logger).withFormatter(this.config.formatter || create$5());
3130
+ if (content) {
3131
+ builder.withContent(content);
3132
+ }
3133
+ if (toolCalls) {
3134
+ builder.withToolCalls(toolCalls);
3135
+ }
3136
+ const message = builder.buildForModel(this.config.model);
3137
+ if (toolCalls) {
3138
+ this.state.metadata.toolCallCount += toolCalls.length;
3139
+ }
3140
+ this.state.messages.push(message);
3141
+ this.updateMetadata();
3142
+ return this;
3143
+ }
3144
+ /**
3145
+ * Add a tool result message using semantic builder
3146
+ */ asTool(callId, result, metadata) {
3147
+ const builder = MessageBuilder.tool(callId, this.logger).withResult(result);
3148
+ if (metadata) {
3149
+ builder.withMetadata(metadata);
3150
+ }
3151
+ const message = builder.buildForModel(this.config.model);
3152
+ this.state.messages.push(message);
3153
+ this.updateMetadata();
3154
+ return this;
3155
+ }
3156
+ /**
3157
+ * Configure token budget
3158
+ */ withTokenBudget(config) {
3159
+ this.logger.debug('Configuring token budget', {
3160
+ max: config.max
3161
+ });
3162
+ this.budgetManager = new TokenBudgetManager(config, this.config.model, this.logger);
3163
+ return this;
3164
+ }
3165
+ /**
3166
+ * Configure conversation logging
3167
+ */ withLogging(config) {
3168
+ this.logger.debug('Configuring conversation logging');
3169
+ this.conversationLogger = new ConversationLogger(config, this.logger);
3170
+ this.conversationLogger.onConversationStart({
3171
+ model: this.config.model,
3172
+ startTime: new Date()
3173
+ });
3174
+ return this;
3175
+ }
3176
+ /**
3177
+ * Save conversation log
3178
+ */ async saveLog() {
3179
+ if (!this.conversationLogger) {
3180
+ throw new Error('Logging not enabled. Call withLogging() first.');
3181
+ }
3182
+ this.conversationLogger.onConversationEnd({
3183
+ totalMessages: this.state.messages.length,
3184
+ toolCallsExecuted: this.state.metadata.toolCallCount,
3185
+ iterations: 0,
3186
+ success: true
3187
+ });
3188
+ return await this.conversationLogger.save();
3189
+ }
3190
+ /**
3191
+ * Get current token usage
3192
+ */ getTokenUsage() {
3193
+ if (!this.budgetManager) {
3194
+ return {
3195
+ used: 0,
3196
+ max: Infinity,
3197
+ remaining: Infinity,
3198
+ percentage: 0
3199
+ };
3200
+ }
3201
+ return this.budgetManager.getCurrentUsage(this.state.messages);
3202
+ }
3203
+ /**
3204
+ * Manually compress conversation
3205
+ */ compress(_strategy) {
3206
+ if (this.budgetManager) {
3207
+ this.state.messages = this.budgetManager.compress(this.state.messages);
3208
+ }
3209
+ return this;
3210
+ }
3211
+ /**
3212
+ * Build and return the builder (for fluent API compatibility)
3213
+ */ build() {
3214
+ return this;
3215
+ }
3216
+ /**
3217
+ * Calculate position for context injection
3218
+ */ calculatePosition(position) {
3219
+ if (typeof position === 'number') {
3220
+ return Math.max(0, Math.min(position, this.state.messages.length));
3221
+ }
3222
+ switch(position){
3223
+ case 'end':
3224
+ return this.state.messages.length;
3225
+ case 'before-last':
3226
+ return Math.max(0, this.state.messages.length - 1);
3227
+ case 'after-system':
3228
+ {
3229
+ // Find last system message (reverse search for compatibility)
3230
+ let lastSystemIdx = -1;
3231
+ for(let i = this.state.messages.length - 1; i >= 0; i--){
3232
+ if (this.state.messages[i].role === 'system') {
3233
+ lastSystemIdx = i;
3234
+ break;
3235
+ }
3236
+ }
3237
+ return lastSystemIdx >= 0 ? lastSystemIdx + 1 : 0;
3238
+ }
3239
+ default:
3240
+ return this.state.messages.length;
3241
+ }
3242
+ }
3243
+ /**
3244
+ * Format context item based on format option
3245
+ */ formatContextItem(item, format) {
3246
+ switch(format){
3247
+ case 'structured':
3248
+ {
3249
+ let result = `## ${item.title || 'Context'}\n\n${item.content}`;
3250
+ // Add metadata if available
3251
+ const metadata = [];
3252
+ if (item.source) {
3253
+ metadata.push(`Source: ${item.source}`);
3254
+ }
3255
+ if (item.timestamp) {
3256
+ metadata.push(`Timestamp: ${item.timestamp.toISOString()}`);
3257
+ }
3258
+ if (metadata.length > 0) {
3259
+ result += `\n\n_${metadata.join(' | ')}_`;
3260
+ }
3261
+ return result;
3262
+ }
3263
+ case 'inline':
3264
+ return `Note: ${item.title ? `${item.title}: ` : ''}${item.content}`;
3265
+ case 'reference':
3266
+ return `[Context Reference: ${item.id || 'unknown'}]\nSee attached context${item.title ? ` for ${item.title}` : ''}`;
3267
+ default:
3268
+ return item.content;
3269
+ }
3270
+ }
3271
+ /**
3272
+ * Update metadata after state changes
3273
+ */ updateMetadata() {
3274
+ this.state.metadata.messageCount = this.state.messages.length;
3275
+ this.state.metadata.lastModified = new Date();
3276
+ }
3277
+ constructor(config, logger){
3278
+ _define_property$3(this, "state", void 0);
3279
+ _define_property$3(this, "config", void 0);
3280
+ _define_property$3(this, "logger", void 0);
3281
+ _define_property$3(this, "budgetManager", void 0);
3282
+ _define_property$3(this, "conversationLogger", void 0);
3283
+ this.config = ConversationBuilderConfigSchema.parse(config);
3284
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationBuilder');
3285
+ this.state = {
3286
+ messages: [],
3287
+ metadata: {
3288
+ model: this.config.model,
3289
+ created: new Date(),
3290
+ lastModified: new Date(),
3291
+ messageCount: 0,
3292
+ toolCallCount: 0
3293
+ },
3294
+ contextProvided: new Set(),
3295
+ contextManager: new ContextManager(logger)
3296
+ };
3297
+ this.logger.debug('Created ConversationBuilder', {
3298
+ model: this.config.model
3299
+ });
3300
+ }
3301
+ }
3302
+
3303
+ function _define_property$2(obj, key, value) {
3304
+ if (key in obj) {
3305
+ Object.defineProperty(obj, key, {
3306
+ value: value,
3307
+ enumerable: true,
3308
+ configurable: true,
3309
+ writable: true
3310
+ });
3311
+ } else {
3312
+ obj[key] = value;
3313
+ }
3314
+ return obj;
3315
+ }
3316
+ // ===== VALIDATION SCHEMAS =====
3317
+ // Simplified parameter schema - just validate structure, not deep nesting
3318
+ const ToolSchema = zod.z.object({
3319
+ name: zod.z.string().min(1),
3320
+ description: zod.z.string().min(1),
3321
+ parameters: zod.z.object({
3322
+ type: zod.z.literal('object'),
3323
+ properties: zod.z.record(zod.z.string(), zod.z.any()).default({}),
3324
+ required: zod.z.array(zod.z.string()).optional()
3325
+ }).passthrough(),
3326
+ execute: zod.z.custom((val)=>typeof val === 'function', {
3327
+ message: 'execute must be a function'
3328
+ }),
3329
+ category: zod.z.string().optional(),
3330
+ cost: zod.z.enum([
3331
+ 'cheap',
3332
+ 'moderate',
3333
+ 'expensive'
3334
+ ]).optional(),
3335
+ examples: zod.z.array(zod.z.object({
3336
+ scenario: zod.z.string(),
3337
+ params: zod.z.any(),
3338
+ expectedResult: zod.z.string()
3339
+ })).optional()
3340
+ }).passthrough(); // Allow additional fields at tool level too
3341
+ // ===== TOOL REGISTRY =====
3342
+ /**
3343
+ * ToolRegistry manages tool definitions and execution.
3344
+ *
3345
+ * Features:
3346
+ * - Register and manage tools
3347
+ * - Execute tools with context
3348
+ * - Track usage statistics
3349
+ * - Export to different formats (OpenAI, Anthropic)
3350
+ * - Filter by category
3351
+ *
3352
+ * @example
3353
+ * ```typescript
3354
+ * const registry = ToolRegistry.create({
3355
+ * workingDirectory: process.cwd(),
3356
+ * logger: myLogger
3357
+ * });
3358
+ *
3359
+ * registry.register({
3360
+ * name: 'read_file',
3361
+ * description: 'Read a file',
3362
+ * parameters: {
3363
+ * type: 'object',
3364
+ * properties: {
3365
+ * path: { type: 'string', description: 'File path' }
3366
+ * },
3367
+ * required: ['path']
3368
+ * },
3369
+ * execute: async ({ path }) => {
3370
+ * return await fs.readFile(path, 'utf-8');
3371
+ * }
3372
+ * });
3373
+ *
3374
+ * const result = await registry.execute('read_file', { path: 'test.txt' });
3375
+ * ```
3376
+ */ class ToolRegistry {
3377
+ /**
3378
+ * Create a new ToolRegistry instance
3379
+ */ static create(context, logger) {
3380
+ return new ToolRegistry(context, logger);
3381
+ }
3382
+ /**
3383
+ * Register a single tool
3384
+ */ register(tool) {
3385
+ // Validate tool
3386
+ try {
3387
+ ToolSchema.parse(tool);
3388
+ } catch (error) {
3389
+ throw new Error(`Invalid tool definition for "${tool.name}": ${error}`);
3390
+ }
3391
+ if (this.tools.has(tool.name)) {
3392
+ this.logger.warn(`Tool "${tool.name}" already registered, overwriting`);
3393
+ }
3394
+ this.tools.set(tool.name, tool);
3395
+ this.usageStats.set(tool.name, {
3396
+ calls: 0,
3397
+ failures: 0,
3398
+ totalDuration: 0
3399
+ });
3400
+ this.logger.debug('Registered tool', {
3401
+ name: tool.name,
3402
+ category: tool.category
3403
+ });
3404
+ }
3405
+ /**
3406
+ * Register multiple tools at once
3407
+ */ registerAll(tools) {
3408
+ this.logger.debug('Registering multiple tools', {
3409
+ count: tools.length
3410
+ });
3411
+ tools.forEach((tool)=>this.register(tool));
3412
+ }
3413
+ /**
3414
+ * Get a tool by name
3415
+ */ get(name) {
3416
+ return this.tools.get(name);
3417
+ }
3418
+ /**
3419
+ * Get all registered tools
3420
+ */ getAll() {
3421
+ return Array.from(this.tools.values());
3422
+ }
3423
+ /**
3424
+ * Get tools by category
3425
+ */ getByCategory(category) {
3426
+ return this.getAll().filter((tool)=>tool.category === category);
3427
+ }
3428
+ /**
3429
+ * Check if a tool is registered
3430
+ */ has(name) {
3431
+ return this.tools.has(name);
3432
+ }
3433
+ /**
3434
+ * Get number of registered tools
3435
+ */ count() {
3436
+ return this.tools.size;
3437
+ }
3438
+ /**
3439
+ * Execute a tool by name
3440
+ */ async execute(name, params) {
3441
+ const tool = this.tools.get(name);
3442
+ if (!tool) {
3443
+ throw new Error(`Tool "${name}" not found`);
3444
+ }
3445
+ this.logger.debug('Executing tool', {
3446
+ name,
3447
+ params
3448
+ });
3449
+ const startTime = Date.now();
3450
+ const stats = this.usageStats.get(name);
3451
+ stats.calls++;
3452
+ try {
3453
+ const result = await tool.execute(params, this.context);
3454
+ const duration = Date.now() - startTime;
3455
+ stats.totalDuration += duration;
3456
+ this.logger.debug('Tool execution succeeded', {
3457
+ name,
3458
+ duration
3459
+ });
3460
+ return result;
3461
+ } catch (error) {
3462
+ stats.failures++;
3463
+ this.logger.error('Tool execution failed', {
3464
+ name,
3465
+ error
3466
+ });
3467
+ throw error;
3468
+ }
3469
+ }
3470
+ /**
3471
+ * Execute multiple tools in sequence
3472
+ */ async executeBatch(calls) {
3473
+ this.logger.debug('Executing batch', {
3474
+ count: calls.length
3475
+ });
3476
+ const results = [];
3477
+ for (const call of calls){
3478
+ try {
3479
+ const result = await this.execute(call.name, call.params);
3480
+ results.push(result);
3481
+ } catch (error) {
3482
+ results.push({
3483
+ error: String(error)
3484
+ });
3485
+ }
3486
+ }
3487
+ return results;
3488
+ }
3489
+ /**
3490
+ * Export tools in OpenAI format
3491
+ */ toOpenAIFormat() {
3492
+ return this.getAll().map((tool)=>({
3493
+ type: 'function',
3494
+ function: {
3495
+ name: tool.name,
3496
+ description: tool.description,
3497
+ parameters: {
3498
+ type: 'object',
3499
+ properties: tool.parameters.properties,
3500
+ required: tool.parameters.required
3501
+ }
3502
+ }
3503
+ }));
3504
+ }
3505
+ /**
3506
+ * Export tools in Anthropic format
3507
+ */ toAnthropicFormat() {
3508
+ return this.getAll().map((tool)=>({
3509
+ name: tool.name,
3510
+ description: tool.description,
3511
+ input_schema: {
3512
+ type: 'object',
3513
+ properties: tool.parameters.properties,
3514
+ required: tool.parameters.required
3515
+ }
3516
+ }));
3517
+ }
3518
+ /**
3519
+ * Get tool definitions (without execute function)
3520
+ */ getDefinitions() {
3521
+ return this.getAll().map((tool)=>({
3522
+ name: tool.name,
3523
+ description: tool.description,
3524
+ parameters: tool.parameters,
3525
+ category: tool.category,
3526
+ cost: tool.cost,
3527
+ examples: tool.examples
3528
+ }));
3529
+ }
3530
+ /**
3531
+ * Get usage statistics for all tools
3532
+ */ getUsageStats() {
3533
+ const stats = new Map();
3534
+ this.usageStats.forEach((rawStats, name)=>{
3535
+ stats.set(name, {
3536
+ calls: rawStats.calls,
3537
+ failures: rawStats.failures,
3538
+ successRate: rawStats.calls > 0 ? (rawStats.calls - rawStats.failures) / rawStats.calls : 0,
3539
+ averageDuration: rawStats.calls > 0 ? rawStats.totalDuration / rawStats.calls : undefined
3540
+ });
3541
+ });
3542
+ return stats;
3543
+ }
3544
+ /**
3545
+ * Get most frequently used tools
3546
+ */ getMostUsed(limit = 5) {
3547
+ const sorted = Array.from(this.usageStats.entries()).sort((a, b)=>b[1].calls - a[1].calls).slice(0, limit).map(([name])=>this.tools.get(name)).filter((tool)=>tool !== undefined);
3548
+ return sorted;
3549
+ }
3550
+ /**
3551
+ * Get list of all categories
3552
+ */ getCategories() {
3553
+ const categories = new Set();
3554
+ this.getAll().forEach((tool)=>{
3555
+ if (tool.category) {
3556
+ categories.add(tool.category);
3557
+ }
3558
+ });
3559
+ return Array.from(categories).sort();
3560
+ }
3561
+ /**
3562
+ * Update execution context
3563
+ */ updateContext(context) {
3564
+ this.context = {
3565
+ ...this.context,
3566
+ ...context
3567
+ };
3568
+ this.logger.debug('Updated context', {
3569
+ keys: Object.keys(context)
3570
+ });
3571
+ }
3572
+ /**
3573
+ * Get current context
3574
+ */ getContext() {
3575
+ return {
3576
+ ...this.context
3577
+ };
3578
+ }
3579
+ /**
3580
+ * Clear all tools
3581
+ */ clear() {
3582
+ this.logger.debug('Clearing all tools');
3583
+ this.tools.clear();
3584
+ this.usageStats.clear();
3585
+ }
3586
+ /**
3587
+ * Unregister a specific tool
3588
+ */ unregister(name) {
3589
+ if (this.tools.has(name)) {
3590
+ this.tools.delete(name);
3591
+ this.usageStats.delete(name);
3592
+ this.logger.debug('Unregistered tool', {
3593
+ name
3594
+ });
3595
+ return true;
3596
+ }
3597
+ return false;
3598
+ }
3599
+ /**
3600
+ * Reset usage statistics
3601
+ */ resetStats() {
3602
+ this.logger.debug('Resetting usage statistics');
3603
+ this.usageStats.forEach((stats)=>{
3604
+ stats.calls = 0;
3605
+ stats.failures = 0;
3606
+ stats.totalDuration = 0;
3607
+ });
3608
+ }
3609
+ constructor(context = {}, logger){
3610
+ _define_property$2(this, "tools", void 0);
3611
+ _define_property$2(this, "context", void 0);
3612
+ _define_property$2(this, "logger", void 0);
3613
+ _define_property$2(this, "usageStats", void 0);
3614
+ this.tools = new Map();
3615
+ this.context = context;
3616
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ToolRegistry');
3617
+ this.usageStats = new Map();
3618
+ this.logger.debug('Created ToolRegistry');
3619
+ }
3620
+ }
3621
+
3622
+ function _define_property$1(obj, key, value) {
3623
+ if (key in obj) {
3624
+ Object.defineProperty(obj, key, {
3625
+ value: value,
3626
+ enumerable: true,
3627
+ configurable: true,
3628
+ writable: true
3629
+ });
3630
+ } else {
3631
+ obj[key] = value;
3632
+ }
3633
+ return obj;
3634
+ }
3635
+ // ===== METRICS COLLECTOR =====
3636
+ /**
3637
+ * MetricsCollector gathers execution metrics during agentic execution.
3638
+ *
3639
+ * @example
3640
+ * ```typescript
3641
+ * const collector = new MetricsCollector();
3642
+ *
3643
+ * collector.recordToolCall('read_file', iteration, duration, true);
3644
+ * collector.recordToolCall('search_code', iteration, duration, false, error);
3645
+ *
3646
+ * const metrics = collector.getMetrics(messages);
3647
+ * ```
3648
+ */ class MetricsCollector {
3649
+ /**
3650
+ * Record a tool execution
3651
+ */ recordToolCall(name, iteration, duration, success, error, inputSize, outputSize) {
3652
+ this.toolMetrics.push({
3653
+ name,
3654
+ iteration,
3655
+ timestamp: new Date().toISOString(),
3656
+ duration,
3657
+ success,
3658
+ error,
3659
+ inputSize,
3660
+ outputSize
3661
+ });
3662
+ }
3663
+ /**
3664
+ * Increment iteration count
3665
+ */ incrementIteration() {
3666
+ this.iterationCount++;
3667
+ }
3668
+ /**
3669
+ * Get complete metrics
3670
+ */ getMetrics(messages, model) {
3671
+ const endTime = new Date();
3672
+ const totalDuration = endTime.getTime() - this.startTime.getTime();
3673
+ // Calculate tool statistics
3674
+ const toolStats = this.calculateToolStats();
3675
+ // Count unique tools
3676
+ const uniqueTools = new Set(this.toolMetrics.map((m)=>m.name));
3677
+ // Calculate investigation depth
3678
+ const totalTools = this.toolMetrics.length;
3679
+ const investigationDepth = totalTools < 3 ? 'shallow' : totalTools < 8 ? 'moderate' : 'deep';
3680
+ // Calculate iteration efficiency
3681
+ const iterationEfficiency = this.iterationCount > 0 ? totalTools / this.iterationCount : 0;
3682
+ // Calculate token usage if model provided
3683
+ let tokenUsage;
3684
+ if (model) {
3685
+ try {
3686
+ const counter = new TokenCounter(model);
3687
+ const total = counter.countConversation(messages);
3688
+ counter.dispose();
3689
+ tokenUsage = {
3690
+ total,
3691
+ systemPrompt: 0,
3692
+ userContent: 0,
3693
+ toolResults: 0,
3694
+ conversation: total
3695
+ };
3696
+ } catch (error) {
3697
+ this.logger.warn('Could not calculate token usage', {
3698
+ error
3699
+ });
3700
+ }
3701
+ }
3702
+ return {
3703
+ startTime: this.startTime,
3704
+ endTime,
3705
+ totalDuration,
3706
+ iterations: this.iterationCount,
3707
+ toolCallsExecuted: this.toolMetrics.length,
3708
+ toolMetrics: this.toolMetrics,
3709
+ toolStats,
3710
+ messageCount: messages.length,
3711
+ tokenUsage,
3712
+ investigationDepth,
3713
+ toolDiversity: uniqueTools.size,
3714
+ iterationEfficiency
3715
+ };
3716
+ }
3717
+ /**
3718
+ * Calculate aggregated tool statistics
3719
+ */ calculateToolStats() {
3720
+ const stats = new Map();
3721
+ // Group by tool name
3722
+ const byTool = new Map();
3723
+ for (const metric of this.toolMetrics){
3724
+ if (!byTool.has(metric.name)) {
3725
+ byTool.set(metric.name, []);
3726
+ }
3727
+ byTool.get(metric.name).push(metric);
3728
+ }
3729
+ // Calculate stats for each tool
3730
+ for (const [name, metrics] of byTool){
3731
+ const total = metrics.length;
3732
+ const success = metrics.filter((m)=>m.success).length;
3733
+ const failures = total - success;
3734
+ const totalDuration = metrics.reduce((sum, m)=>sum + m.duration, 0);
3735
+ const avgDuration = totalDuration / total;
3736
+ const successRate = total > 0 ? success / total : 0;
3737
+ stats.set(name, {
3738
+ name,
3739
+ total,
3740
+ success,
3741
+ failures,
3742
+ totalDuration,
3743
+ avgDuration,
3744
+ successRate
3745
+ });
3746
+ }
3747
+ return stats;
3748
+ }
3749
+ constructor(logger){
3750
+ _define_property$1(this, "startTime", void 0);
3751
+ _define_property$1(this, "toolMetrics", void 0);
3752
+ _define_property$1(this, "iterationCount", void 0);
3753
+ _define_property$1(this, "logger", void 0);
3754
+ this.startTime = new Date();
3755
+ this.toolMetrics = [];
3756
+ this.iterationCount = 0;
3757
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'MetricsCollector');
3758
+ }
3759
+ }
3760
+ // ===== REFLECTION REPORT GENERATOR =====
3761
+ /**
3762
+ * ReflectionReportGenerator generates analysis reports from execution metrics.
3763
+ *
3764
+ * @example
3765
+ * ```typescript
3766
+ * const generator = new ReflectionReportGenerator();
3767
+ * const report = generator.generate(metrics, result);
3768
+ *
3769
+ * console.log('Success rate:', report.toolEffectiveness.overallSuccessRate);
3770
+ * console.log('Recommendations:', report.recommendations.length);
3771
+ * ```
3772
+ */ class ReflectionReportGenerator {
3773
+ /**
3774
+ * Generate reflection report
3775
+ */ generate(metrics, result) {
3776
+ var _result_finalMessage;
3777
+ this.logger.debug('Generating reflection report');
3778
+ const report = {
3779
+ id: `reflection-${Date.now()}`,
3780
+ generated: new Date(),
3781
+ summary: this.generateSummary(metrics),
3782
+ toolEffectiveness: this.analyzeToolEffectiveness(metrics),
3783
+ performanceInsights: this.analyzePerformance(metrics),
3784
+ timeline: this.buildTimeline(metrics),
3785
+ tokenUsage: metrics.tokenUsage,
3786
+ qualityAssessment: this.assessQuality(metrics),
3787
+ recommendations: this.generateRecommendations(metrics, result),
3788
+ conversationHistory: result.conversation.getMessages(),
3789
+ output: ((_result_finalMessage = result.finalMessage) === null || _result_finalMessage === void 0 ? void 0 : _result_finalMessage.content) || undefined
3790
+ };
3791
+ this.logger.info('Generated reflection report', {
3792
+ recommendations: report.recommendations.length,
3793
+ toolsAnalyzed: metrics.toolStats.size
3794
+ });
3795
+ return report;
3796
+ }
3797
+ /**
3798
+ * Generate execution summary
3799
+ */ generateSummary(metrics) {
3800
+ const successfulTools = metrics.toolMetrics.filter((m)=>m.success).length;
3801
+ const successRate = metrics.toolMetrics.length > 0 ? successfulTools / metrics.toolMetrics.length : 0;
3802
+ return {
3803
+ startTime: metrics.startTime,
3804
+ endTime: metrics.endTime || new Date(),
3805
+ totalDuration: metrics.totalDuration,
3806
+ iterations: metrics.iterations,
3807
+ toolCallsExecuted: metrics.toolCallsExecuted,
3808
+ uniqueToolsUsed: metrics.toolDiversity,
3809
+ successRate
3810
+ };
3811
+ }
3812
+ /**
3813
+ * Analyze tool effectiveness
3814
+ */ analyzeToolEffectiveness(metrics) {
3815
+ const successfulTools = metrics.toolMetrics.filter((m)=>m.success).length;
3816
+ const overallSuccessRate = metrics.toolMetrics.length > 0 ? successfulTools / metrics.toolMetrics.length : 1;
3817
+ // Find failed tools
3818
+ const failedTools = Array.from(metrics.toolStats.values()).filter((stats)=>stats.failures > 0).map((stats)=>({
3819
+ name: stats.name,
3820
+ failures: stats.failures,
3821
+ rate: stats.successRate
3822
+ })).sort((a, b)=>b.failures - a.failures);
3823
+ // Find slow tools (>1s average)
3824
+ const slowTools = Array.from(metrics.toolStats.values()).filter((stats)=>stats.avgDuration > 1000).map((stats)=>({
3825
+ name: stats.name,
3826
+ avgDuration: stats.avgDuration
3827
+ })).sort((a, b)=>b.avgDuration - a.avgDuration);
3828
+ // Most used tools
3829
+ const mostUsedTools = Array.from(metrics.toolStats.values()).map((stats)=>({
3830
+ name: stats.name,
3831
+ count: stats.total
3832
+ })).sort((a, b)=>b.count - a.count).slice(0, 5);
3833
+ return {
3834
+ overallSuccessRate,
3835
+ toolStats: metrics.toolStats,
3836
+ failedTools,
3837
+ slowTools,
3838
+ mostUsedTools
3839
+ };
3840
+ }
3841
+ /**
3842
+ * Analyze performance
3843
+ */ analyzePerformance(metrics) {
3844
+ const avgIterationDuration = metrics.iterations > 0 ? metrics.totalDuration / metrics.iterations : 0;
3845
+ // Find slowest and fastest tools
3846
+ const toolsBySpeed = Array.from(metrics.toolStats.values()).sort((a, b)=>b.avgDuration - a.avgDuration);
3847
+ const slowestTool = toolsBySpeed[0] ? {
3848
+ name: toolsBySpeed[0].name,
3849
+ duration: toolsBySpeed[0].avgDuration
3850
+ } : undefined;
3851
+ const fastestTool = toolsBySpeed[toolsBySpeed.length - 1] ? {
3852
+ name: toolsBySpeed[toolsBySpeed.length - 1].name,
3853
+ duration: toolsBySpeed[toolsBySpeed.length - 1].avgDuration
3854
+ } : undefined;
3855
+ // Identify bottlenecks
3856
+ const bottlenecks = [];
3857
+ if (slowestTool && slowestTool.duration > 1000) {
3858
+ bottlenecks.push(`${slowestTool.name} averaging ${slowestTool.duration}ms`);
3859
+ }
3860
+ if (avgIterationDuration > 10000) {
3861
+ bottlenecks.push(`Slow iterations averaging ${avgIterationDuration.toFixed(0)}ms`);
3862
+ }
3863
+ return {
3864
+ totalDuration: metrics.totalDuration,
3865
+ avgIterationDuration,
3866
+ slowestTool,
3867
+ fastestTool,
3868
+ bottlenecks
3869
+ };
3870
+ }
3871
+ /**
3872
+ * Build execution timeline
3873
+ */ buildTimeline(metrics) {
3874
+ const events = [];
3875
+ for (const metric of metrics.toolMetrics){
3876
+ events.push({
3877
+ timestamp: metric.timestamp,
3878
+ iteration: metric.iteration,
3879
+ type: 'tool-call',
3880
+ description: `${metric.name}(${metric.success ? 'success' : 'failure'})`,
3881
+ duration: metric.duration,
3882
+ success: metric.success
3883
+ });
3884
+ }
3885
+ return events.sort((a, b)=>new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
3886
+ }
3887
+ /**
3888
+ * Assess investigation quality
3889
+ */ assessQuality(metrics) {
3890
+ const toolDiversity = metrics.toolDiversity;
3891
+ const iterationEfficiency = metrics.iterationEfficiency;
3892
+ // Calculate coverage (tools / iterations - aim for 1-2)
3893
+ const coverage = metrics.iterations > 0 ? Math.min(1, metrics.toolCallsExecuted / (metrics.iterations * 2)) : 0;
3894
+ // Overall quality score (0-1)
3895
+ const depthScore = metrics.investigationDepth === 'deep' ? 1 : metrics.investigationDepth === 'moderate' ? 0.7 : 0.3;
3896
+ const diversityScore = Math.min(1, toolDiversity / 5); // 5+ tools = max score
3897
+ const efficiencyScore = Math.min(1, iterationEfficiency / 2); // 2 tools/iteration = max
3898
+ const overall = (depthScore + diversityScore + efficiencyScore) / 3;
3899
+ return {
3900
+ investigationDepth: metrics.investigationDepth,
3901
+ toolDiversity,
3902
+ iterationEfficiency,
3903
+ coverage,
3904
+ overall
3905
+ };
3906
+ }
3907
+ /**
3908
+ * Generate recommendations
3909
+ */ generateRecommendations(metrics, _result) {
3910
+ const recommendations = [];
3911
+ // Check for tool failures
3912
+ const failedTools = Array.from(metrics.toolStats.values()).filter((stats)=>stats.failures > 0);
3913
+ if (failedTools.length > 0) {
3914
+ recommendations.push({
3915
+ type: 'tool-failure',
3916
+ severity: 'high',
3917
+ message: `${failedTools.length} tool(s) had failures. Review tool implementations.`,
3918
+ suggestion: 'Check error logs and validate tool parameters',
3919
+ relatedTools: failedTools.map((t)=>t.name)
3920
+ });
3921
+ }
3922
+ // Check for shallow investigation
3923
+ if (metrics.investigationDepth === 'shallow' && metrics.toolCallsExecuted < 2) {
3924
+ recommendations.push({
3925
+ type: 'investigation-depth',
3926
+ severity: 'medium',
3927
+ message: 'Investigation was shallow. Consider adjusting strategy to encourage more tool usage.',
3928
+ suggestion: 'Use investigateThenRespond strategy with requireMinimumTools'
3929
+ });
3930
+ }
3931
+ // Check for slow tools
3932
+ const slowTools = Array.from(metrics.toolStats.values()).filter((stats)=>stats.avgDuration > 1000);
3933
+ if (slowTools.length > 0) {
3934
+ recommendations.push({
3935
+ type: 'performance',
3936
+ severity: 'medium',
3937
+ message: `${slowTools.length} tool(s) taking >1s. Consider optimization.`,
3938
+ suggestion: 'Add caching, reduce scope, or optimize implementations',
3939
+ relatedTools: slowTools.map((t)=>t.name)
3940
+ });
3941
+ }
3942
+ // Check token usage
3943
+ if (metrics.tokenUsage) {
3944
+ if (metrics.tokenUsage.percentage && metrics.tokenUsage.percentage > 80) {
3945
+ recommendations.push({
3946
+ type: 'token-budget',
3947
+ severity: 'high',
3948
+ message: `Token usage at ${metrics.tokenUsage.percentage.toFixed(1)}%. Increase budget or enable compression.`,
3949
+ suggestion: 'Increase max tokens or use priority-based compression'
3950
+ });
3951
+ }
3952
+ }
3953
+ return recommendations;
3954
+ }
3955
+ /**
3956
+ * Format report as markdown
3957
+ */ formatMarkdown(report) {
3958
+ let markdown = `# Agentic Execution - Self-Reflection Report\n\n`;
3959
+ markdown += `**Generated:** ${report.generated.toISOString()}\n`;
3960
+ markdown += `**Duration:** ${(report.summary.totalDuration / 1000).toFixed(1)}s\n\n`;
3961
+ markdown += `## Execution Summary\n\n`;
3962
+ markdown += `- **Iterations**: ${report.summary.iterations}\n`;
3963
+ markdown += `- **Tool Calls**: ${report.summary.toolCallsExecuted}\n`;
3964
+ markdown += `- **Unique Tools**: ${report.summary.uniqueToolsUsed}\n`;
3965
+ markdown += `- **Investigation Depth**: ${report.qualityAssessment.investigationDepth}\n`;
3966
+ markdown += `- **Success Rate**: ${(report.summary.successRate * 100).toFixed(1)}%\n\n`;
3967
+ markdown += `## Tool Effectiveness Analysis\n\n`;
3968
+ markdown += `| Tool | Calls | Success | Failures | Success Rate | Avg Duration |\n`;
3969
+ markdown += `|------|-------|---------|----------|--------------|---------------|\n`;
3970
+ for (const [name, stats] of report.toolEffectiveness.toolStats){
3971
+ markdown += `| ${name} | ${stats.total} | ${stats.success} | ${stats.failures} | `;
3972
+ markdown += `${(stats.successRate * 100).toFixed(1)}% | ${stats.avgDuration.toFixed(0)}ms |\n`;
3973
+ }
3974
+ if (report.toolEffectiveness.failedTools.length > 0) {
3975
+ markdown += `\n### Tools with Failures\n\n`;
3976
+ for (const tool of report.toolEffectiveness.failedTools){
3977
+ markdown += `- **${tool.name}**: ${tool.failures} failures (${(tool.rate * 100).toFixed(1)}% success)\n`;
3978
+ }
3979
+ }
3980
+ if (report.toolEffectiveness.slowTools.length > 0) {
3981
+ markdown += `\n### Slow Tools (>1s average)\n\n`;
3982
+ for (const tool of report.toolEffectiveness.slowTools){
3983
+ markdown += `- **${tool.name}**: ${(tool.avgDuration / 1000).toFixed(2)}s average\n`;
3984
+ }
3985
+ }
3986
+ markdown += `\n## Quality Assessment\n\n`;
3987
+ markdown += `- **Overall Score**: ${(report.qualityAssessment.overall * 100).toFixed(0)}%\n`;
3988
+ markdown += `- **Investigation Depth**: ${report.qualityAssessment.investigationDepth}\n`;
3989
+ markdown += `- **Tool Diversity**: ${report.qualityAssessment.toolDiversity} unique tools\n`;
3990
+ markdown += `- **Efficiency**: ${report.qualityAssessment.iterationEfficiency.toFixed(2)} tools per iteration\n\n`;
3991
+ if (report.recommendations.length > 0) {
3992
+ markdown += `## Recommendations\n\n`;
3993
+ const byPriority = {
3994
+ high: report.recommendations.filter((r)=>r.severity === 'high'),
3995
+ medium: report.recommendations.filter((r)=>r.severity === 'medium'),
3996
+ low: report.recommendations.filter((r)=>r.severity === 'low')
3997
+ };
3998
+ if (byPriority.high.length > 0) {
3999
+ markdown += `### 🔴 High Priority\n\n`;
4000
+ byPriority.high.forEach((rec, i)=>{
4001
+ markdown += `${i + 1}. **${rec.message}**\n`;
4002
+ if (rec.suggestion) {
4003
+ markdown += ` - Suggestion: ${rec.suggestion}\n`;
4004
+ }
4005
+ markdown += `\n`;
4006
+ });
4007
+ }
4008
+ if (byPriority.medium.length > 0) {
4009
+ markdown += `### 🟡 Medium Priority\n\n`;
4010
+ byPriority.medium.forEach((rec, i)=>{
4011
+ markdown += `${i + 1}. **${rec.message}**\n`;
4012
+ if (rec.suggestion) {
4013
+ markdown += ` - Suggestion: ${rec.suggestion}\n`;
4014
+ }
4015
+ markdown += `\n`;
4016
+ });
4017
+ }
4018
+ }
4019
+ if (report.output) {
4020
+ markdown += `## Final Output\n\n`;
4021
+ markdown += `\`\`\`\n${report.output}\n\`\`\`\n\n`;
4022
+ }
4023
+ markdown += `---\n\n`;
4024
+ markdown += `*Report generated by RiotPrompt Agentic Reflection System*\n`;
4025
+ return markdown;
4026
+ }
4027
+ constructor(logger){
4028
+ _define_property$1(this, "logger", void 0);
4029
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ReflectionReportGenerator');
4030
+ }
4031
+ }
4032
+
4033
+ function _define_property(obj, key, value) {
4034
+ if (key in obj) {
4035
+ Object.defineProperty(obj, key, {
4036
+ value: value,
4037
+ enumerable: true,
4038
+ configurable: true,
4039
+ writable: true
4040
+ });
4041
+ } else {
4042
+ obj[key] = value;
4043
+ }
4044
+ return obj;
4045
+ }
4046
+ // ===== STRATEGY EXECUTOR =====
4047
+ /**
4048
+ * StrategyExecutor executes iteration strategies.
4049
+ *
4050
+ * Features:
4051
+ * - Execute multi-phase strategies
4052
+ * - Manage tool calls and results
4053
+ * - Track state and metrics
4054
+ * - Handle timeouts and errors
4055
+ * - Provide lifecycle hooks
4056
+ *
4057
+ * @example
4058
+ * ```typescript
4059
+ * const executor = new StrategyExecutor(llmClient);
4060
+ *
4061
+ * const result = await executor.execute(
4062
+ * conversation,
4063
+ * toolRegistry,
4064
+ * strategy
4065
+ * );
4066
+ *
4067
+ * console.log('Completed in', result.totalIterations, 'iterations');
4068
+ * console.log('Used', result.toolCallsExecuted, 'tools');
4069
+ * ```
4070
+ */ class StrategyExecutor {
4071
+ /**
4072
+ * Enable reflection generation
4073
+ */ withReflection(config) {
4074
+ this.reflectionConfig = config;
4075
+ return this;
4076
+ }
4077
+ /**
4078
+ * Execute a strategy
4079
+ */ async execute(conversation, tools, strategy) {
4080
+ var _this_reflectionConfig;
4081
+ const startTime = Date.now();
4082
+ // Initialize metrics collector if reflection enabled
4083
+ if ((_this_reflectionConfig = this.reflectionConfig) === null || _this_reflectionConfig === void 0 ? void 0 : _this_reflectionConfig.enabled) {
4084
+ this.metricsCollector = new MetricsCollector(this.logger);
4085
+ }
4086
+ const state = {
4087
+ phase: 0,
4088
+ iteration: 0,
4089
+ toolCallsExecuted: 0,
4090
+ startTime,
4091
+ insights: [],
4092
+ findings: [],
4093
+ errors: []
4094
+ };
4095
+ this.logger.info('Starting strategy execution', {
4096
+ strategy: strategy.name
4097
+ });
4098
+ const context = {
4099
+ conversation,
4100
+ tools,
4101
+ llm: this.llm,
4102
+ state
4103
+ };
4104
+ try {
4105
+ var _strategy_onStart, _this_reflectionConfig1, _strategy_onComplete;
4106
+ // Initialize
4107
+ await ((_strategy_onStart = strategy.onStart) === null || _strategy_onStart === void 0 ? void 0 : _strategy_onStart.call(strategy, context));
4108
+ // Execute phases or single loop
4109
+ const phases = strategy.phases || [
4110
+ {
4111
+ name: 'default',
4112
+ maxIterations: strategy.maxIterations,
4113
+ toolUsage: 'encouraged'
4114
+ }
4115
+ ];
4116
+ const phaseResults = [];
4117
+ for (const phase of phases){
4118
+ var _phase_skipIf, _strategy_onPhaseComplete;
4119
+ // Check if should skip phase
4120
+ if ((_phase_skipIf = phase.skipIf) === null || _phase_skipIf === void 0 ? void 0 : _phase_skipIf.call(phase, state)) {
4121
+ this.logger.debug('Skipping phase', {
4122
+ phase: phase.name
4123
+ });
4124
+ continue;
4125
+ }
4126
+ state.phase = phase.name;
4127
+ state.iteration = 0;
4128
+ this.logger.debug('Starting phase', {
4129
+ phase: phase.name
4130
+ });
4131
+ const phaseResult = await this.executePhase(conversation, tools, phase, state, strategy);
4132
+ phaseResults.push(phaseResult);
4133
+ // Track iteration for metrics
4134
+ if (this.metricsCollector) {
4135
+ this.metricsCollector.incrementIteration();
4136
+ }
4137
+ await ((_strategy_onPhaseComplete = strategy.onPhaseComplete) === null || _strategy_onPhaseComplete === void 0 ? void 0 : _strategy_onPhaseComplete.call(strategy, phaseResult, state));
4138
+ // Check if should continue
4139
+ if (strategy.shouldContinue && !strategy.shouldContinue(state)) {
4140
+ this.logger.debug('Strategy decided to stop');
4141
+ break;
4142
+ }
4143
+ }
4144
+ const duration = Date.now() - startTime;
4145
+ const result = {
4146
+ finalMessage: conversation.getLastMessage(),
4147
+ phases: phaseResults,
4148
+ totalIterations: state.iteration,
4149
+ toolCallsExecuted: state.toolCallsExecuted,
4150
+ duration,
4151
+ success: true,
4152
+ conversation
4153
+ };
4154
+ // Generate reflection if enabled
4155
+ if (this.metricsCollector && ((_this_reflectionConfig1 = this.reflectionConfig) === null || _this_reflectionConfig1 === void 0 ? void 0 : _this_reflectionConfig1.enabled)) {
4156
+ const metrics = this.metricsCollector.getMetrics(conversation.getMessages(), conversation.getMetadata().model);
4157
+ const generator = new ReflectionReportGenerator(this.logger);
4158
+ result.reflection = generator.generate(metrics, result);
4159
+ // Save reflection if output path specified
4160
+ if (this.reflectionConfig.outputPath && result.reflection) {
4161
+ await this.saveReflection(result.reflection, this.reflectionConfig);
4162
+ }
4163
+ }
4164
+ await ((_strategy_onComplete = strategy.onComplete) === null || _strategy_onComplete === void 0 ? void 0 : _strategy_onComplete.call(strategy, result));
4165
+ this.logger.info('Strategy execution complete', {
4166
+ iterations: result.totalIterations,
4167
+ toolCalls: result.toolCallsExecuted,
4168
+ duration
4169
+ });
4170
+ return result;
4171
+ } catch (error) {
4172
+ this.logger.error('Strategy execution failed', {
4173
+ error
4174
+ });
4175
+ return {
4176
+ finalMessage: conversation.getLastMessage(),
4177
+ phases: [],
4178
+ totalIterations: state.iteration,
4179
+ toolCallsExecuted: state.toolCallsExecuted,
4180
+ duration: Date.now() - startTime,
4181
+ success: false,
4182
+ conversation
4183
+ };
4184
+ }
4185
+ }
4186
+ /**
4187
+ * Save reflection report
4188
+ */ async saveReflection(reflection, config) {
4189
+ if (!config.outputPath) {
4190
+ return;
4191
+ }
4192
+ try {
4193
+ const fs = await import('fs/promises');
4194
+ const path = await import('path');
4195
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
4196
+ const filename = `reflection-${timestamp}.${config.format === 'json' ? 'json' : 'md'}`;
4197
+ const fullPath = path.join(config.outputPath, filename);
4198
+ // Ensure directory exists
4199
+ await fs.mkdir(config.outputPath, {
4200
+ recursive: true
4201
+ });
4202
+ // Save based on format
4203
+ if (config.format === 'json') {
4204
+ await fs.writeFile(fullPath, JSON.stringify(reflection, null, 2), 'utf-8');
4205
+ } else {
4206
+ const generator = new ReflectionReportGenerator(this.logger);
4207
+ const markdown = generator.formatMarkdown(reflection);
4208
+ await fs.writeFile(fullPath, markdown, 'utf-8');
4209
+ }
4210
+ this.logger.info('Reflection saved', {
4211
+ path: fullPath
4212
+ });
4213
+ } catch (error) {
4214
+ this.logger.error('Failed to save reflection', {
4215
+ error
4216
+ });
4217
+ }
4218
+ }
4219
+ /**
4220
+ * Execute a single phase
4221
+ */ async executePhase(conversation, tools, phase, state, strategy) {
4222
+ const phaseStartTools = state.toolCallsExecuted;
4223
+ // Add phase instructions if provided
4224
+ if (phase.instructions) {
4225
+ conversation.asUser(phase.instructions);
4226
+ }
4227
+ // Iteration loop for this phase
4228
+ for(let i = 0; i < phase.maxIterations; i++){
4229
+ var _strategy_onIteration;
4230
+ state.iteration++;
4231
+ this.logger.debug('Iteration', {
4232
+ phase: phase.name,
4233
+ iteration: i + 1
4234
+ });
4235
+ // Check iteration hook
4236
+ const action = await ((_strategy_onIteration = strategy.onIteration) === null || _strategy_onIteration === void 0 ? void 0 : _strategy_onIteration.call(strategy, i, state));
4237
+ if (action === 'stop') {
4238
+ break;
4239
+ }
4240
+ if (action === 'next-phase') {
4241
+ break;
4242
+ }
4243
+ // Get LLM response
4244
+ const toolsToProvide = phase.toolUsage !== 'forbidden' ? tools.toOpenAIFormat() : undefined;
4245
+ const response = await this.llm.complete(conversation.toMessages(), toolsToProvide);
4246
+ // Handle tool calls
4247
+ if (response.tool_calls && response.tool_calls.length > 0) {
4248
+ if (phase.toolUsage === 'forbidden') {
4249
+ this.logger.warn('Tool calls requested but forbidden in this phase');
4250
+ conversation.asAssistant(response.content);
4251
+ continue;
4252
+ }
4253
+ conversation.asAssistant(response.content, response.tool_calls);
4254
+ // Execute tools
4255
+ for (const toolCall of response.tool_calls){
4256
+ var _strategy_onToolCall;
4257
+ // Check if tool is allowed in this phase
4258
+ if (phase.allowedTools && !phase.allowedTools.includes(toolCall.function.name)) {
4259
+ this.logger.debug('Tool not allowed in phase', {
4260
+ tool: toolCall.function.name
4261
+ });
4262
+ continue;
4263
+ }
4264
+ // Check tool call hook
4265
+ const toolAction = await ((_strategy_onToolCall = strategy.onToolCall) === null || _strategy_onToolCall === void 0 ? void 0 : _strategy_onToolCall.call(strategy, toolCall, state));
4266
+ if (toolAction === 'skip') {
4267
+ continue;
4268
+ }
4269
+ // Execute tool
4270
+ const toolStart = Date.now();
4271
+ try {
4272
+ var _strategy_onToolResult;
4273
+ const result = await tools.execute(toolCall.function.name, JSON.parse(toolCall.function.arguments));
4274
+ const toolDuration = Date.now() - toolStart;
4275
+ const toolResult = {
4276
+ callId: toolCall.id,
4277
+ toolName: toolCall.function.name,
4278
+ result,
4279
+ duration: toolDuration
4280
+ };
4281
+ conversation.asTool(toolCall.id, result, {
4282
+ duration: toolDuration,
4283
+ success: true
4284
+ });
4285
+ state.toolCallsExecuted++;
4286
+ // Record metrics
4287
+ if (this.metricsCollector) {
4288
+ this.metricsCollector.recordToolCall(toolCall.function.name, state.iteration, toolDuration, true);
4289
+ }
4290
+ await ((_strategy_onToolResult = strategy.onToolResult) === null || _strategy_onToolResult === void 0 ? void 0 : _strategy_onToolResult.call(strategy, toolResult, state));
4291
+ } catch (error) {
4292
+ var _strategy_onToolResult1;
4293
+ this.logger.error('Tool execution failed', {
4294
+ tool: toolCall.function.name,
4295
+ error
4296
+ });
4297
+ const toolDuration = Date.now() - toolStart;
4298
+ const toolResult = {
4299
+ callId: toolCall.id,
4300
+ toolName: toolCall.function.name,
4301
+ result: null,
4302
+ error: error,
4303
+ duration: toolDuration
4304
+ };
4305
+ conversation.asTool(toolCall.id, {
4306
+ error: error.message
4307
+ }, {
4308
+ success: false,
4309
+ errorName: error.name
4310
+ });
4311
+ state.errors.push(error);
4312
+ // Record metrics
4313
+ if (this.metricsCollector) {
4314
+ this.metricsCollector.recordToolCall(toolCall.function.name, state.iteration, toolDuration, false, error.message);
4315
+ }
4316
+ await ((_strategy_onToolResult1 = strategy.onToolResult) === null || _strategy_onToolResult1 === void 0 ? void 0 : _strategy_onToolResult1.call(strategy, toolResult, state));
4317
+ }
4318
+ }
4319
+ } else {
4320
+ // No tool calls - add response and potentially end phase
4321
+ conversation.asAssistant(response.content);
4322
+ // Check if this phase requires tool calls
4323
+ if (phase.toolUsage === 'required' && state.toolCallsExecuted === phaseStartTools) {
4324
+ this.logger.warn('No tools used but required in phase');
4325
+ // Continue to try again
4326
+ } else if (phase.earlyExit !== false) {
4327
+ break;
4328
+ }
4329
+ }
4330
+ // Check phase completion conditions
4331
+ const toolCallsInPhase = state.toolCallsExecuted - phaseStartTools;
4332
+ if (phase.minToolCalls && toolCallsInPhase < phase.minToolCalls) {
4333
+ continue; // Need more tool calls
4334
+ }
4335
+ if (phase.maxToolCalls && toolCallsInPhase >= phase.maxToolCalls) {
4336
+ break; // Hit max tool calls for phase
4337
+ }
4338
+ if (phase.continueIf && !phase.continueIf(state)) {
4339
+ break; // Condition not met
4340
+ }
4341
+ }
4342
+ return {
4343
+ name: phase.name,
4344
+ iterations: state.iteration,
4345
+ toolCalls: state.toolCallsExecuted - phaseStartTools,
4346
+ success: true,
4347
+ insights: state.insights
4348
+ };
4349
+ }
4350
+ constructor(llm, logger){
4351
+ _define_property(this, "llm", void 0);
4352
+ _define_property(this, "logger", void 0);
4353
+ _define_property(this, "metricsCollector", void 0);
4354
+ _define_property(this, "reflectionConfig", void 0);
4355
+ this.llm = llm;
4356
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'StrategyExecutor');
4357
+ }
4358
+ }
4359
+ // ===== PRE-BUILT STRATEGIES =====
4360
+ /**
4361
+ * Factory for creating iteration strategies
4362
+ */ class IterationStrategyFactory {
4363
+ /**
4364
+ * Investigate then respond strategy
4365
+ * Phase 1: Use tools to gather information
4366
+ * Phase 2: Synthesize into final answer
4367
+ */ static investigateThenRespond(config = {}) {
4368
+ const { maxInvestigationSteps = 5, requireMinimumTools = 1, finalSynthesis = true } = config;
4369
+ return {
4370
+ name: 'investigate-then-respond',
4371
+ description: 'Investigate using tools, then synthesize findings',
4372
+ maxIterations: maxInvestigationSteps + (finalSynthesis ? 1 : 0),
4373
+ phases: [
4374
+ {
4375
+ name: 'investigate',
4376
+ maxIterations: maxInvestigationSteps,
4377
+ toolUsage: 'encouraged',
4378
+ minToolCalls: requireMinimumTools,
4379
+ earlyExit: false
4380
+ },
4381
+ ...finalSynthesis ? [
4382
+ {
4383
+ name: 'respond',
4384
+ maxIterations: 1,
4385
+ toolUsage: 'forbidden',
4386
+ instructions: 'Based on your investigation, provide a comprehensive answer.',
4387
+ requireFinalAnswer: true
4388
+ }
4389
+ ] : []
4390
+ ]
4391
+ };
4392
+ }
4393
+ /**
4394
+ * Multi-pass refinement strategy
4395
+ * Generate, critique, refine repeatedly
4396
+ */ static multiPassRefinement(config = {}) {
4397
+ const { passes = 3, critiqueBetweenPasses = true } = config;
4398
+ const phases = [];
4399
+ for(let i = 0; i < passes; i++){
4400
+ phases.push({
4401
+ name: `pass-${i + 1}`,
4402
+ maxIterations: 1,
4403
+ toolUsage: 'optional',
4404
+ instructions: i === 0 ? 'Generate your best response' : 'Refine your previous response based on the critique'
4405
+ });
4406
+ if (critiqueBetweenPasses && i < passes - 1) {
4407
+ phases.push({
4408
+ name: `critique-${i + 1}`,
4409
+ maxIterations: 1,
4410
+ toolUsage: 'forbidden',
4411
+ instructions: 'Critique the previous response. What can be improved?'
4412
+ });
4413
+ }
4414
+ }
4415
+ return {
4416
+ name: 'multi-pass-refinement',
4417
+ description: 'Iteratively refine response through multiple passes',
4418
+ maxIterations: passes * 2,
4419
+ phases
4420
+ };
4421
+ }
4422
+ /**
4423
+ * Breadth-first investigation
4424
+ * Explore broadly before going deep
4425
+ */ static breadthFirst(config = {}) {
4426
+ const { levelsDeep = 3, toolsPerLevel = 4 } = config;
4427
+ const phases = [];
4428
+ for(let level = 0; level < levelsDeep; level++){
4429
+ phases.push({
4430
+ name: `level-${level + 1}`,
4431
+ maxIterations: toolsPerLevel,
4432
+ toolUsage: 'encouraged',
4433
+ minToolCalls: 1,
4434
+ maxToolCalls: toolsPerLevel,
4435
+ instructions: level === 0 ? 'Get a broad overview' : `Dive deeper into areas discovered in level ${level}`
4436
+ });
4437
+ }
4438
+ return {
4439
+ name: 'breadth-first',
4440
+ description: 'Explore broadly at each level before going deeper',
4441
+ maxIterations: levelsDeep * toolsPerLevel,
4442
+ phases
4443
+ };
4444
+ }
4445
+ /**
4446
+ * Depth-first investigation
4447
+ * Deep dive immediately
4448
+ */ static depthFirst(config = {}) {
4449
+ const { maxDepth = 5, backtrackOnFailure = true } = config;
4450
+ return {
4451
+ name: 'depth-first',
4452
+ description: 'Deep dive investigation path',
4453
+ maxIterations: maxDepth,
4454
+ phases: [
4455
+ {
4456
+ name: 'deep-dive',
4457
+ maxIterations: maxDepth,
4458
+ toolUsage: 'encouraged',
4459
+ adaptiveDepth: true
4460
+ }
4461
+ ],
4462
+ shouldContinue: (state)=>{
4463
+ // Continue if making progress
4464
+ if (backtrackOnFailure && state.errors.length > 2) {
4465
+ return false;
4466
+ }
4467
+ return true;
4468
+ }
4469
+ };
4470
+ }
4471
+ /**
4472
+ * Adaptive strategy
4473
+ * Changes behavior based on progress
4474
+ */ static adaptive(_config = {}) {
4475
+ return {
4476
+ name: 'adaptive',
4477
+ description: 'Adapts strategy based on progress',
4478
+ maxIterations: 20,
4479
+ onIteration: async (iteration, state)=>{
4480
+ // Change behavior based on iteration count
4481
+ if (iteration < 5) {
4482
+ // Early: broad exploration
4483
+ return 'continue';
4484
+ } else if (iteration < 15) {
4485
+ // Mid: focused investigation
4486
+ return 'continue';
4487
+ } else {
4488
+ // Late: wrap up
4489
+ return state.toolCallsExecuted > 0 ? 'continue' : 'stop';
4490
+ }
4491
+ }
4492
+ };
4493
+ }
4494
+ /**
4495
+ * Simple iteration (basic tool-use loop)
4496
+ */ static simple(config = {}) {
4497
+ const { maxIterations = 10, allowTools = true } = config;
4498
+ return {
4499
+ name: 'simple',
4500
+ description: 'Simple iteration loop',
4501
+ maxIterations,
4502
+ phases: [
4503
+ {
4504
+ name: 'main',
4505
+ maxIterations,
4506
+ toolUsage: allowTools ? 'encouraged' : 'forbidden',
4507
+ earlyExit: true
4508
+ }
4509
+ ]
4510
+ };
4511
+ }
4512
+ }
4513
+
4514
+ // ===== CONFIGURATION SCHEMAS =====
4515
+ const ContentItemSchema = zod.z.union([
4516
+ zod.z.string(),
4517
+ zod.z.object({
4518
+ content: zod.z.string(),
4519
+ title: zod.z.string().optional(),
4520
+ weight: zod.z.number().optional()
4521
+ }),
4522
+ zod.z.object({
4523
+ path: zod.z.string(),
4524
+ title: zod.z.string().optional(),
4525
+ weight: zod.z.number().optional()
4526
+ }),
4527
+ zod.z.object({
4528
+ directories: zod.z.array(zod.z.string()),
4529
+ title: zod.z.string().optional(),
4530
+ weight: zod.z.number().optional()
4531
+ })
4532
+ ]);
4533
+ const RecipeConfigSchema = zod.z.object({
4534
+ // Core settings
4535
+ basePath: zod.z.string(),
4536
+ logger: zod.z.any().optional().default(DEFAULT_LOGGER),
4537
+ overridePaths: zod.z.array(zod.z.string()).optional().default([
4538
+ "./"
4539
+ ]),
4540
+ overrides: zod.z.boolean().optional().default(false),
4541
+ parameters: ParametersSchema.optional().default({}),
4542
+ // Content sections
4543
+ persona: ContentItemSchema.optional(),
4544
+ instructions: zod.z.array(ContentItemSchema).optional().default([]),
4545
+ content: zod.z.array(ContentItemSchema).optional().default([]),
4546
+ context: zod.z.array(ContentItemSchema).optional().default([]),
4547
+ // Templates and inheritance
4548
+ extends: zod.z.string().optional(),
4549
+ template: zod.z.string().optional(),
4550
+ // Tool integration
4551
+ tools: zod.z.any().optional(),
4552
+ toolGuidance: zod.z.union([
4553
+ zod.z.enum([
4554
+ 'auto',
4555
+ 'minimal',
4556
+ 'detailed'
4557
+ ]),
4558
+ zod.z.object({
4559
+ strategy: zod.z.enum([
4560
+ 'adaptive',
4561
+ 'prescriptive',
4562
+ 'minimal'
4563
+ ]),
4564
+ includeExamples: zod.z.boolean().optional(),
4565
+ explainWhenToUse: zod.z.boolean().optional(),
4566
+ includeCategories: zod.z.boolean().optional(),
4567
+ customInstructions: zod.z.string().optional()
4568
+ })
4569
+ ]).optional(),
4570
+ toolCategories: zod.z.array(zod.z.string()).optional()
4571
+ });
4572
+ // User-customizable template registry
4573
+ let TEMPLATES = {};
4574
+ /**
4575
+ * Register custom templates with the recipes system
4576
+ *
4577
+ * @example
4578
+ * ```typescript
4579
+ * // Register your own templates
4580
+ * registerTemplates({
4581
+ * myWorkflow: {
4582
+ * persona: { path: "personas/my-persona.md" },
4583
+ * instructions: [{ path: "instructions/my-instructions.md" }]
4584
+ * },
4585
+ * anotherTemplate: {
4586
+ * persona: { content: "You are a helpful assistant" },
4587
+ * instructions: [{ content: "Follow these steps..." }]
4588
+ * }
4589
+ * });
4590
+ * ```
4591
+ */ const registerTemplates = (templates)=>{
4592
+ TEMPLATES = {
4593
+ ...TEMPLATES,
4594
+ ...templates
4595
+ };
4596
+ };
4597
+ /**
4598
+ * Get currently registered templates
4599
+ */ const getTemplates = ()=>({
4600
+ ...TEMPLATES
4601
+ });
4602
+ /**
4603
+ * Clear all registered templates
4604
+ */ const clearTemplates = ()=>{
4605
+ TEMPLATES = {};
4606
+ };
4607
+ // ===== TOOL GUIDANCE GENERATION =====
4608
+ /**
4609
+ * Generate tool guidance instructions based on strategy
4610
+ */ const generateToolGuidance = (tools, guidance)=>{
4611
+ if (tools.length === 0) {
4612
+ return '';
4613
+ }
4614
+ // Normalize guidance config
4615
+ let config;
4616
+ if (typeof guidance === 'string') {
4617
+ switch(guidance){
4618
+ case 'auto':
4619
+ case 'detailed':
4620
+ config = {
4621
+ strategy: 'adaptive',
4622
+ includeExamples: true,
4623
+ explainWhenToUse: true
4624
+ };
4625
+ break;
4626
+ case 'minimal':
4627
+ config = {
4628
+ strategy: 'minimal',
4629
+ includeExamples: false,
4630
+ explainWhenToUse: false
4631
+ };
4632
+ break;
4633
+ default:
4634
+ config = {
4635
+ strategy: 'adaptive'
4636
+ };
4637
+ }
4638
+ } else {
4639
+ config = guidance;
4640
+ }
4641
+ let output = '## Available Tools\n\n';
4642
+ if (config.customInstructions) {
4643
+ output += config.customInstructions + '\n\n';
4644
+ }
4645
+ // Group by category if enabled
4646
+ if (config.includeCategories) {
4647
+ const categorized = new Map();
4648
+ tools.forEach((tool)=>{
4649
+ const category = tool.category || 'General';
4650
+ if (!categorized.has(category)) {
4651
+ categorized.set(category, []);
4652
+ }
4653
+ categorized.get(category).push(tool);
4654
+ });
4655
+ categorized.forEach((categoryTools, category)=>{
4656
+ output += `### ${category}\n\n`;
4657
+ categoryTools.forEach((tool)=>{
4658
+ output += formatToolGuidance(tool, config);
4659
+ });
4660
+ });
4661
+ } else {
4662
+ tools.forEach((tool)=>{
4663
+ output += formatToolGuidance(tool, config);
4664
+ });
4665
+ }
4666
+ return output;
4667
+ };
4668
+ const formatToolGuidance = (tool, config)=>{
4669
+ let output = `**${tool.name}**`;
4670
+ if (tool.cost) {
4671
+ output += ` _(${tool.cost})_`;
4672
+ }
4673
+ output += `\n${tool.description}\n\n`;
4674
+ if (config.strategy !== 'minimal') {
4675
+ // Parameters
4676
+ const required = tool.parameters.required || [];
4677
+ const paramList = Object.entries(tool.parameters.properties).map(([name, param])=>{
4678
+ const isRequired = required.includes(name);
4679
+ return `- \`${name}\`${isRequired ? ' (required)' : ''}: ${param.description}`;
4680
+ }).join('\n');
4681
+ if (paramList) {
4682
+ output += 'Parameters:\n' + paramList + '\n\n';
4683
+ }
4684
+ // When to use (adaptive and prescriptive)
4685
+ if (config.explainWhenToUse && (config.strategy === 'adaptive' || config.strategy === 'prescriptive')) {
4686
+ output += `**When to use:** ${tool.description}\n\n`;
4687
+ }
4688
+ // Examples
4689
+ if (config.includeExamples && tool.examples && tool.examples.length > 0) {
4690
+ output += '**Examples:**\n';
4691
+ tool.examples.forEach((example)=>{
4692
+ output += `- ${example.scenario}: \`${tool.name}(${JSON.stringify(example.params)})\`\n`;
4693
+ });
4694
+ output += '\n';
4695
+ }
4696
+ }
4697
+ output += '---\n\n';
4698
+ return output;
4699
+ };
4700
+ // ===== CORE RECIPE ENGINE =====
4701
+ const cook = async (config)=>{
4702
+ // Parse and validate configuration with defaults
4703
+ const validatedConfig = RecipeConfigSchema.parse({
4704
+ overridePaths: [
4705
+ "./"
4706
+ ],
4707
+ overrides: false,
4708
+ parameters: {},
4709
+ instructions: [],
4710
+ content: [],
4711
+ context: [],
4712
+ ...config
4713
+ });
4714
+ // Handle template inheritance
4715
+ let finalConfig = {
4716
+ ...validatedConfig
4717
+ };
4718
+ if (validatedConfig.template) {
4719
+ const template = TEMPLATES[validatedConfig.template];
4720
+ if (template) {
4721
+ finalConfig = {
4722
+ ...validatedConfig,
4723
+ persona: validatedConfig.persona || template.persona,
4724
+ instructions: [
4725
+ ...template.instructions || [],
4726
+ ...validatedConfig.instructions || []
4727
+ ],
4728
+ content: [
4729
+ ...template.content || [],
4730
+ ...validatedConfig.content || []
4731
+ ],
4732
+ context: [
4733
+ ...template.context || [],
4734
+ ...validatedConfig.context || []
4735
+ ]
4736
+ };
4737
+ }
4738
+ }
4739
+ // Setup internal services
4740
+ const logger = wrapLogger(finalConfig.logger, 'Recipe');
4741
+ const parser$1 = create$4({
4742
+ logger
4743
+ });
4744
+ const override$1 = create$1({
4745
+ logger,
4746
+ configDirs: finalConfig.overridePaths || [
4747
+ "./"
4748
+ ],
4749
+ overrides: finalConfig.overrides || false
4750
+ });
4751
+ const loader$1 = create$2({
4752
+ logger
4753
+ });
4754
+ // Create sections
4755
+ const personaSection = create$8({
4756
+ title: "Persona"
4757
+ });
4758
+ const instructionSection = create$8({
4759
+ title: "Instruction"
4760
+ });
4761
+ const contentSection = create$8({
4762
+ title: "Content"
4763
+ });
4764
+ const contextSection = create$8({
4765
+ title: "Context"
4766
+ });
4767
+ // Process persona
4768
+ if (finalConfig.persona) {
4769
+ await processContentItem(finalConfig.persona, personaSection, 'persona', {
4770
+ basePath: finalConfig.basePath,
4771
+ parser: parser$1,
4772
+ override: override$1,
4773
+ loader: loader$1,
4774
+ parameters: finalConfig.parameters});
4775
+ }
4776
+ // Process instructions
4777
+ for (const item of finalConfig.instructions || []){
4778
+ await processContentItem(item, instructionSection, 'instruction', {
4779
+ basePath: finalConfig.basePath,
4780
+ parser: parser$1,
4781
+ override: override$1,
4782
+ loader: loader$1,
4783
+ parameters: finalConfig.parameters});
4784
+ }
4785
+ // Generate tool guidance if tools are provided
4786
+ if (finalConfig.tools) {
4787
+ const tools = Array.isArray(finalConfig.tools) ? finalConfig.tools : finalConfig.tools.getAll();
4788
+ // Filter by categories if specified
4789
+ const filteredTools = finalConfig.toolCategories ? tools.filter((tool)=>finalConfig.toolCategories.includes(tool.category || '')) : tools;
4790
+ if (filteredTools.length > 0 && finalConfig.toolGuidance) {
4791
+ const guidance = generateToolGuidance(filteredTools, finalConfig.toolGuidance);
4792
+ const toolSection = await parser$1.parse(guidance, {
4793
+ parameters: finalConfig.parameters
4794
+ });
4795
+ instructionSection.add(toolSection);
4796
+ }
4797
+ }
4798
+ // Process content
4799
+ for (const item of finalConfig.content || []){
4800
+ await processContentItem(item, contentSection, 'content', {
4801
+ basePath: finalConfig.basePath,
4802
+ parser: parser$1,
4803
+ override: override$1,
4804
+ loader: loader$1,
4805
+ parameters: finalConfig.parameters});
4806
+ }
4807
+ // Process context
4808
+ for (const item of finalConfig.context || []){
4809
+ await processContentItem(item, contextSection, 'context', {
4810
+ basePath: finalConfig.basePath,
4811
+ parser: parser$1,
4812
+ override: override$1,
4813
+ loader: loader$1,
4814
+ parameters: finalConfig.parameters});
4815
+ }
4816
+ // Build and return prompt
4817
+ return create$6({
4818
+ persona: personaSection,
4819
+ instructions: instructionSection,
4820
+ contents: contentSection,
4821
+ contexts: contextSection
4822
+ });
4823
+ };
4824
+ const processContentItem = async (item, section, type, ctx)=>{
4825
+ const sectionOptions = {
4826
+ parameters: ctx.parameters
4827
+ };
4828
+ if (typeof item === 'string') {
4829
+ // Simple string content
4830
+ const parsedSection = await ctx.parser.parse(item, sectionOptions);
4831
+ section.add(parsedSection);
4832
+ } else if ('content' in item) {
4833
+ // Inline content with options
4834
+ const parsedSection = await ctx.parser.parse(item.content, {
4835
+ ...sectionOptions,
4836
+ title: item.title,
4837
+ weight: item.weight
4838
+ });
4839
+ section.add(parsedSection);
4840
+ } else if ('path' in item) {
4841
+ // File path
4842
+ const fullPath = path.join(ctx.basePath, item.path);
4843
+ const parsedSection = await ctx.parser.parseFile(fullPath, {
4844
+ ...sectionOptions,
4845
+ title: item.title,
4846
+ weight: item.weight
4847
+ });
4848
+ const overrideSection = await ctx.override.customize(item.path, parsedSection, sectionOptions);
4849
+ section.add(overrideSection);
4850
+ } else if ('directories' in item) {
4851
+ // Directory loading
4852
+ const sections = await ctx.loader.load(item.directories, {
4853
+ ...sectionOptions,
4854
+ title: item.title,
4855
+ weight: item.weight
4856
+ });
4857
+ section.add(sections);
4858
+ }
4859
+ };
4860
+ // ===== FLUENT RECIPE BUILDER =====
4861
+ const recipe = (basePath)=>{
4862
+ const config = {
4863
+ basePath
4864
+ };
4865
+ const builder = {
4866
+ template: (name)=>{
4867
+ config.template = name;
4868
+ return builder;
4869
+ },
1608
4870
  with: (partialConfig)=>{
1609
4871
  Object.assign(config, partialConfig);
1610
4872
  return builder;
@@ -1649,7 +4911,61 @@ const recipe = (basePath)=>{
1649
4911
  config.overridePaths = paths;
1650
4912
  return builder;
1651
4913
  },
1652
- cook: ()=>cook(config)
4914
+ tools: (tools)=>{
4915
+ config.tools = tools;
4916
+ return builder;
4917
+ },
4918
+ toolRegistry: (registry)=>{
4919
+ config.tools = registry;
4920
+ return builder;
4921
+ },
4922
+ toolGuidance: (guidance)=>{
4923
+ config.toolGuidance = guidance;
4924
+ return builder;
4925
+ },
4926
+ toolCategories: (categories)=>{
4927
+ config.toolCategories = categories;
4928
+ return builder;
4929
+ },
4930
+ cook: ()=>cook(config),
4931
+ buildConversation: async (model, tokenBudget)=>{
4932
+ const prompt = await cook(config);
4933
+ const conversation = ConversationBuilder.create({
4934
+ model
4935
+ }, config.logger);
4936
+ conversation.fromPrompt(prompt, model);
4937
+ // Apply token budget if provided
4938
+ if (tokenBudget) {
4939
+ conversation.withTokenBudget(tokenBudget);
4940
+ }
4941
+ return conversation;
4942
+ },
4943
+ getToolRegistry: ()=>{
4944
+ if (config.tools instanceof ToolRegistry) {
4945
+ return config.tools;
4946
+ } else if (Array.isArray(config.tools)) {
4947
+ const registry = ToolRegistry.create({}, config.logger);
4948
+ registry.registerAll(config.tools);
4949
+ return registry;
4950
+ }
4951
+ return undefined;
4952
+ },
4953
+ executeWith: async (llm, strategy, tokenBudget)=>{
4954
+ const prompt = await cook(config);
4955
+ const conversation = ConversationBuilder.create({
4956
+ model: 'gpt-4o'
4957
+ }, config.logger);
4958
+ conversation.fromPrompt(prompt, 'gpt-4o');
4959
+ if (tokenBudget) {
4960
+ conversation.withTokenBudget(tokenBudget);
4961
+ }
4962
+ const registry = builder.getToolRegistry();
4963
+ if (!registry) {
4964
+ throw new Error('Tools must be configured to use executeWith');
4965
+ }
4966
+ const executor = new StrategyExecutor(llm, config.logger);
4967
+ return executor.execute(conversation, registry, strategy);
4968
+ }
1653
4969
  };
1654
4970
  return builder;
1655
4971
  };
@@ -1658,6 +4974,7 @@ const recipes = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
1658
4974
  __proto__: null,
1659
4975
  clearTemplates,
1660
4976
  cook,
4977
+ generateToolGuidance,
1661
4978
  getTemplates,
1662
4979
  recipe,
1663
4980
  registerTemplates
@@ -1665,11 +4982,24 @@ const recipes = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
1665
4982
 
1666
4983
  exports.Builder = builder;
1667
4984
  exports.Chat = chat;
4985
+ exports.ContextManager = ContextManager;
4986
+ exports.ConversationBuilder = ConversationBuilder;
4987
+ exports.ConversationLogger = ConversationLogger;
4988
+ exports.ConversationReplayer = ConversationReplayer;
1668
4989
  exports.Formatter = formatter;
4990
+ exports.IterationStrategyFactory = IterationStrategyFactory;
1669
4991
  exports.Loader = loader;
4992
+ exports.MessageBuilder = MessageBuilder;
4993
+ exports.MessageTemplates = MessageTemplates;
4994
+ exports.MetricsCollector = MetricsCollector;
1670
4995
  exports.Override = override;
1671
4996
  exports.Parser = parser;
1672
4997
  exports.Recipes = recipes;
4998
+ exports.ReflectionReportGenerator = ReflectionReportGenerator;
4999
+ exports.StrategyExecutor = StrategyExecutor;
5000
+ exports.TokenBudgetManager = TokenBudgetManager;
5001
+ exports.TokenCounter = TokenCounter;
5002
+ exports.ToolRegistry = ToolRegistry;
1673
5003
  exports.clearTemplates = clearTemplates;
1674
5004
  exports.cook = cook;
1675
5005
  exports.createContent = create$b;
@@ -1680,6 +5010,7 @@ exports.createPrompt = create$6;
1680
5010
  exports.createSection = create$8;
1681
5011
  exports.createTrait = create$7;
1682
5012
  exports.createWeighted = create$c;
5013
+ exports.generateToolGuidance = generateToolGuidance;
1683
5014
  exports.getTemplates = getTemplates;
1684
5015
  exports.recipe = recipe;
1685
5016
  exports.registerTemplates = registerTemplates;