@riotprompt/riotprompt 0.0.7 → 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 (52) hide show
  1. package/.kodrdriv-test-cache.json +6 -0
  2. package/README.md +2 -2
  3. package/dist/builder.d.ts +3 -15
  4. package/dist/builder.js +3 -0
  5. package/dist/builder.js.map +1 -1
  6. package/dist/context-manager.d.ts +135 -0
  7. package/dist/context-manager.js +220 -0
  8. package/dist/context-manager.js.map +1 -0
  9. package/dist/conversation-logger.d.ts +283 -0
  10. package/dist/conversation-logger.js +454 -0
  11. package/dist/conversation-logger.js.map +1 -0
  12. package/dist/conversation.d.ts +271 -0
  13. package/dist/conversation.js +622 -0
  14. package/dist/conversation.js.map +1 -0
  15. package/dist/formatter.d.ts +27 -57
  16. package/dist/formatter.js +2 -2
  17. package/dist/formatter.js.map +1 -1
  18. package/dist/items/parameters.d.ts +1 -1
  19. package/dist/items/section.d.ts +2 -12
  20. package/dist/items/weighted.d.ts +3 -15
  21. package/dist/iteration-strategy.d.ts +231 -0
  22. package/dist/iteration-strategy.js +486 -0
  23. package/dist/iteration-strategy.js.map +1 -0
  24. package/dist/loader.d.ts +3 -11
  25. package/dist/loader.js +3 -0
  26. package/dist/loader.js.map +1 -1
  27. package/dist/message-builder.d.ts +156 -0
  28. package/dist/message-builder.js +254 -0
  29. package/dist/message-builder.js.map +1 -0
  30. package/dist/override.d.ts +3 -13
  31. package/dist/override.js +3 -0
  32. package/dist/override.js.map +1 -1
  33. package/dist/parser.d.ts +2 -8
  34. package/dist/recipes.d.ts +70 -268
  35. package/dist/recipes.js +189 -4
  36. package/dist/recipes.js.map +1 -1
  37. package/dist/reflection.d.ts +250 -0
  38. package/dist/reflection.js +416 -0
  39. package/dist/reflection.js.map +1 -0
  40. package/dist/riotprompt.cjs +3551 -220
  41. package/dist/riotprompt.cjs.map +1 -1
  42. package/dist/riotprompt.d.ts +18 -2
  43. package/dist/riotprompt.js +9 -1
  44. package/dist/riotprompt.js.map +1 -1
  45. package/dist/token-budget.d.ts +177 -0
  46. package/dist/token-budget.js +404 -0
  47. package/dist/token-budget.js.map +1 -0
  48. package/dist/tools.d.ts +239 -0
  49. package/dist/tools.js +324 -0
  50. package/dist/tools.js.map +1 -0
  51. package/package.json +35 -36
  52. package/.cursor/rules/focus-on-prompt.mdc +0 -5
@@ -0,0 +1,622 @@
1
+ import { z } from 'zod';
2
+ import { ContextManager } from './context-manager.js';
3
+ import { ConversationLogger } from './conversation-logger.js';
4
+ import { wrapLogger, DEFAULT_LOGGER } from './logger.js';
5
+ import { MessageBuilder } from './message-builder.js';
6
+ import { create } from './formatter.js';
7
+ import { TokenBudgetManager } from './token-budget.js';
8
+
9
+ function _define_property(obj, key, value) {
10
+ if (key in obj) {
11
+ Object.defineProperty(obj, key, {
12
+ value: value,
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true
16
+ });
17
+ } else {
18
+ obj[key] = value;
19
+ }
20
+ return obj;
21
+ }
22
+ // ===== SCHEMAS =====
23
+ const ConversationBuilderConfigSchema = z.object({
24
+ model: z.string(),
25
+ formatter: z.any().optional(),
26
+ trackContext: z.boolean().optional().default(true),
27
+ deduplicateContext: z.boolean().optional().default(true)
28
+ });
29
+ // ===== CONVERSATION BUILDER =====
30
+ /**
31
+ * ConversationBuilder manages multi-turn conversations with full lifecycle support.
32
+ *
33
+ * Features:
34
+ * - Initialize from RiotPrompt prompts
35
+ * - Add messages of any type (system, user, assistant, tool)
36
+ * - Handle tool calls and results
37
+ * - Inject dynamic context
38
+ * - Clone for parallel exploration
39
+ * - Serialize/deserialize for persistence
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Create from prompt
44
+ * const conversation = ConversationBuilder.create()
45
+ * .fromPrompt(prompt, 'gpt-4o')
46
+ * .build();
47
+ *
48
+ * // Add messages
49
+ * conversation.addUserMessage('Analyze this code');
50
+ *
51
+ * // Handle tool calls
52
+ * conversation.addAssistantWithToolCalls(null, toolCalls);
53
+ * conversation.addToolResult(toolCallId, result);
54
+ *
55
+ * // Export
56
+ * const messages = conversation.toMessages();
57
+ * ```
58
+ */ class ConversationBuilder {
59
+ /**
60
+ * Create a new ConversationBuilder instance
61
+ */ static create(config, logger) {
62
+ const defaultConfig = {
63
+ model: 'gpt-4o',
64
+ trackContext: true,
65
+ deduplicateContext: true,
66
+ ...config
67
+ };
68
+ return new ConversationBuilder(defaultConfig, logger);
69
+ }
70
+ /**
71
+ * Initialize conversation from a RiotPrompt prompt
72
+ */ fromPrompt(prompt, model) {
73
+ const targetModel = model || this.config.model;
74
+ this.logger.debug('Initializing from prompt', {
75
+ model: targetModel
76
+ });
77
+ // Use formatter (provided or create new one)
78
+ const formatter$1 = this.config.formatter || create();
79
+ const request = formatter$1.formatPrompt(targetModel, prompt);
80
+ // Add all messages from formatted request
81
+ request.messages.forEach((msg)=>{
82
+ this.state.messages.push(msg);
83
+ });
84
+ this.updateMetadata();
85
+ this.logger.debug('Initialized from prompt', {
86
+ messageCount: this.state.messages.length
87
+ });
88
+ return this;
89
+ }
90
+ /**
91
+ * Add a system message
92
+ */ addSystemMessage(content) {
93
+ this.logger.debug('Adding system message');
94
+ let messageContent;
95
+ if (typeof content === 'string') {
96
+ messageContent = content;
97
+ } else {
98
+ // Format section using formatter
99
+ const formatter$1 = this.config.formatter || create();
100
+ messageContent = formatter$1.format(content);
101
+ }
102
+ this.state.messages.push({
103
+ role: 'system',
104
+ content: messageContent
105
+ });
106
+ this.updateMetadata();
107
+ return this;
108
+ }
109
+ /**
110
+ * Add a user message (with automatic budget management)
111
+ */ addUserMessage(content) {
112
+ this.logger.debug('Adding user message');
113
+ let messageContent;
114
+ if (typeof content === 'string') {
115
+ messageContent = content;
116
+ } else {
117
+ // Format section using formatter
118
+ const formatter$1 = this.config.formatter || create();
119
+ messageContent = formatter$1.format(content);
120
+ }
121
+ const message = {
122
+ role: 'user',
123
+ content: messageContent
124
+ };
125
+ // Check budget if enabled
126
+ if (this.budgetManager) {
127
+ if (!this.budgetManager.canAddMessage(message, this.state.messages)) {
128
+ this.logger.warn('Budget exceeded, compressing conversation');
129
+ this.state.messages = this.budgetManager.compress(this.state.messages);
130
+ }
131
+ }
132
+ this.state.messages.push(message);
133
+ this.updateMetadata();
134
+ return this;
135
+ }
136
+ /**
137
+ * Add an assistant message
138
+ */ addAssistantMessage(content) {
139
+ this.logger.debug('Adding assistant message');
140
+ this.state.messages.push({
141
+ role: 'assistant',
142
+ content: content || ''
143
+ });
144
+ this.updateMetadata();
145
+ return this;
146
+ }
147
+ /**
148
+ * Add an assistant message with tool calls
149
+ */ addAssistantWithToolCalls(content, toolCalls) {
150
+ this.logger.debug('Adding assistant message with tool calls', {
151
+ toolCount: toolCalls.length
152
+ });
153
+ this.state.messages.push({
154
+ role: 'assistant',
155
+ content: content,
156
+ tool_calls: toolCalls
157
+ });
158
+ this.state.metadata.toolCallCount += toolCalls.length;
159
+ this.updateMetadata();
160
+ return this;
161
+ }
162
+ /**
163
+ * Add a tool result message
164
+ */ addToolResult(toolCallId, content, toolName) {
165
+ this.logger.debug('Adding tool result', {
166
+ toolCallId,
167
+ toolName
168
+ });
169
+ const message = {
170
+ role: 'tool',
171
+ tool_call_id: toolCallId,
172
+ content: content
173
+ };
174
+ if (toolName) {
175
+ message.name = toolName;
176
+ }
177
+ this.state.messages.push(message);
178
+ this.updateMetadata();
179
+ return this;
180
+ }
181
+ /**
182
+ * Alias for addToolResult (more intuitive naming)
183
+ */ addToolMessage(toolCallId, content, toolName) {
184
+ return this.addToolResult(toolCallId, content, toolName);
185
+ }
186
+ /**
187
+ * Inject context into the conversation with advanced options
188
+ *
189
+ * @param context - Array of content items to inject
190
+ * @param options - Injection options (position, format, deduplication, etc.)
191
+ */ injectContext(context, options) {
192
+ var _this_config_deduplicateContext;
193
+ const opts = {
194
+ position: 'end',
195
+ format: 'structured',
196
+ deduplicate: (_this_config_deduplicateContext = this.config.deduplicateContext) !== null && _this_config_deduplicateContext !== void 0 ? _this_config_deduplicateContext : true,
197
+ deduplicateBy: 'id',
198
+ priority: 'medium',
199
+ weight: 1.0,
200
+ category: undefined,
201
+ source: undefined,
202
+ ...options
203
+ };
204
+ this.logger.debug('Injecting context', {
205
+ itemCount: context.length,
206
+ options: opts
207
+ });
208
+ // Filter out duplicates if enabled
209
+ const itemsToAdd = [];
210
+ for (const item of context){
211
+ const enrichedItem = {
212
+ ...item,
213
+ priority: item.priority || opts.priority,
214
+ weight: item.weight || opts.weight,
215
+ category: item.category || opts.category,
216
+ source: item.source || opts.source,
217
+ timestamp: item.timestamp || new Date()
218
+ };
219
+ // Check deduplication
220
+ if (opts.deduplicate) {
221
+ let skip = false;
222
+ switch(opts.deduplicateBy){
223
+ case 'id':
224
+ if (enrichedItem.id && this.state.contextManager.hasContext(enrichedItem.id)) {
225
+ this.logger.debug('Skipping duplicate context by ID', {
226
+ id: enrichedItem.id
227
+ });
228
+ skip = true;
229
+ }
230
+ break;
231
+ case 'hash':
232
+ if (this.state.contextManager.hasContentHash(enrichedItem.content)) {
233
+ this.logger.debug('Skipping duplicate context by hash');
234
+ skip = true;
235
+ }
236
+ break;
237
+ case 'content':
238
+ if (this.state.contextManager.hasSimilarContent(enrichedItem.content)) {
239
+ this.logger.debug('Skipping duplicate context by content');
240
+ skip = true;
241
+ }
242
+ break;
243
+ }
244
+ if (skip) {
245
+ continue;
246
+ }
247
+ }
248
+ itemsToAdd.push(enrichedItem);
249
+ }
250
+ // Only proceed if we have items to add
251
+ if (itemsToAdd.length === 0) {
252
+ return this;
253
+ }
254
+ // Calculate position
255
+ const position = this.calculatePosition(opts.position);
256
+ // Format and inject
257
+ for (const item of itemsToAdd){
258
+ const formatted = this.formatContextItem(item, opts.format);
259
+ const contextMessage = {
260
+ role: 'user',
261
+ content: formatted
262
+ };
263
+ this.state.messages.splice(position, 0, contextMessage);
264
+ // Track in context manager
265
+ this.state.contextManager.track(item, position);
266
+ }
267
+ this.updateMetadata();
268
+ return this;
269
+ }
270
+ /**
271
+ * Inject system-level context
272
+ */ injectSystemContext(context) {
273
+ this.logger.debug('Injecting system context');
274
+ let messageContent;
275
+ if (typeof context === 'string') {
276
+ messageContent = context;
277
+ } else {
278
+ const formatter$1 = this.config.formatter || create();
279
+ messageContent = formatter$1.format(context);
280
+ }
281
+ this.state.messages.push({
282
+ role: 'system',
283
+ content: messageContent
284
+ });
285
+ this.updateMetadata();
286
+ return this;
287
+ }
288
+ /**
289
+ * Get the number of messages in the conversation
290
+ */ getMessageCount() {
291
+ return this.state.messages.length;
292
+ }
293
+ /**
294
+ * Get the last message in the conversation
295
+ */ getLastMessage() {
296
+ return this.state.messages[this.state.messages.length - 1];
297
+ }
298
+ /**
299
+ * Get all messages
300
+ */ getMessages() {
301
+ return [
302
+ ...this.state.messages
303
+ ];
304
+ }
305
+ /**
306
+ * Check if conversation has any tool calls
307
+ */ hasToolCalls() {
308
+ return this.state.metadata.toolCallCount > 0;
309
+ }
310
+ /**
311
+ * Get conversation metadata
312
+ */ getMetadata() {
313
+ return {
314
+ ...this.state.metadata
315
+ };
316
+ }
317
+ /**
318
+ * Export messages in OpenAI format
319
+ */ toMessages() {
320
+ return this.state.messages.map((msg)=>({
321
+ ...msg
322
+ }));
323
+ }
324
+ /**
325
+ * Serialize conversation to JSON
326
+ */ toJSON() {
327
+ const serialized = {
328
+ messages: this.state.messages,
329
+ metadata: {
330
+ ...this.state.metadata,
331
+ created: this.state.metadata.created.toISOString(),
332
+ lastModified: this.state.metadata.lastModified.toISOString()
333
+ },
334
+ contextProvided: Array.from(this.state.contextProvided)
335
+ };
336
+ return JSON.stringify(serialized, null, 2);
337
+ }
338
+ /**
339
+ * Restore conversation from JSON
340
+ */ static fromJSON(json, config, logger) {
341
+ const parsed = JSON.parse(json);
342
+ const builder = ConversationBuilder.create({
343
+ model: parsed.metadata.model,
344
+ ...config
345
+ }, logger);
346
+ // Restore state
347
+ builder.state.messages = parsed.messages;
348
+ builder.state.metadata = {
349
+ ...parsed.metadata,
350
+ created: new Date(parsed.metadata.created),
351
+ lastModified: new Date(parsed.metadata.lastModified)
352
+ };
353
+ builder.state.contextProvided = new Set(parsed.contextProvided);
354
+ return builder;
355
+ }
356
+ /**
357
+ * Clone the conversation for parallel exploration
358
+ */ clone() {
359
+ this.logger.debug('Cloning conversation');
360
+ const cloned = ConversationBuilder.create({
361
+ ...this.config
362
+ }, this.logger);
363
+ // Deep copy state (note: contextManager is already created in constructor)
364
+ cloned.state.messages = this.state.messages.map((msg)=>({
365
+ ...msg
366
+ }));
367
+ cloned.state.metadata = {
368
+ ...this.state.metadata
369
+ };
370
+ cloned.state.contextProvided = new Set(this.state.contextProvided);
371
+ // Copy context manager state
372
+ const allContext = this.state.contextManager.getAll();
373
+ allContext.forEach((item)=>{
374
+ cloned.state.contextManager.track(item, item.position);
375
+ });
376
+ return cloned;
377
+ }
378
+ /**
379
+ * Truncate conversation to last N messages
380
+ */ truncate(maxMessages) {
381
+ this.logger.debug('Truncating conversation', {
382
+ maxMessages,
383
+ current: this.state.messages.length
384
+ });
385
+ if (this.state.messages.length > maxMessages) {
386
+ this.state.messages = this.state.messages.slice(-maxMessages);
387
+ this.updateMetadata();
388
+ }
389
+ return this;
390
+ }
391
+ /**
392
+ * Remove all messages of a specific type
393
+ */ removeMessagesOfType(role) {
394
+ this.logger.debug('Removing messages of type', {
395
+ role
396
+ });
397
+ this.state.messages = this.state.messages.filter((msg)=>msg.role !== role);
398
+ this.updateMetadata();
399
+ return this;
400
+ }
401
+ /**
402
+ * Get the context manager
403
+ */ getContextManager() {
404
+ return this.state.contextManager;
405
+ }
406
+ /**
407
+ * Get conversation state (for conditional injection)
408
+ */ getState() {
409
+ return {
410
+ messages: [
411
+ ...this.state.messages
412
+ ],
413
+ metadata: {
414
+ ...this.state.metadata
415
+ },
416
+ contextProvided: new Set(this.state.contextProvided),
417
+ contextManager: this.state.contextManager
418
+ };
419
+ }
420
+ // ===== SEMANTIC MESSAGE METHODS (Feature 5) =====
421
+ /**
422
+ * Add a system message using semantic builder
423
+ */ asSystem(content) {
424
+ const message = MessageBuilder.system(this.logger).withContent(content).withFormatter(this.config.formatter || create()).buildForModel(this.config.model);
425
+ this.state.messages.push(message);
426
+ this.updateMetadata();
427
+ return this;
428
+ }
429
+ /**
430
+ * Add a user message using semantic builder
431
+ */ asUser(content) {
432
+ const message = MessageBuilder.user(this.logger).withContent(content).withFormatter(this.config.formatter || create()).buildForModel(this.config.model);
433
+ // Check budget if enabled
434
+ if (this.budgetManager) {
435
+ if (!this.budgetManager.canAddMessage(message, this.state.messages)) {
436
+ this.logger.warn('Budget exceeded, compressing conversation');
437
+ this.state.messages = this.budgetManager.compress(this.state.messages);
438
+ }
439
+ }
440
+ this.state.messages.push(message);
441
+ this.updateMetadata();
442
+ return this;
443
+ }
444
+ /**
445
+ * Add an assistant message using semantic builder
446
+ */ asAssistant(content, toolCalls) {
447
+ const builder = MessageBuilder.assistant(this.logger).withFormatter(this.config.formatter || create());
448
+ if (content) {
449
+ builder.withContent(content);
450
+ }
451
+ if (toolCalls) {
452
+ builder.withToolCalls(toolCalls);
453
+ }
454
+ const message = builder.buildForModel(this.config.model);
455
+ if (toolCalls) {
456
+ this.state.metadata.toolCallCount += toolCalls.length;
457
+ }
458
+ this.state.messages.push(message);
459
+ this.updateMetadata();
460
+ return this;
461
+ }
462
+ /**
463
+ * Add a tool result message using semantic builder
464
+ */ asTool(callId, result, metadata) {
465
+ const builder = MessageBuilder.tool(callId, this.logger).withResult(result);
466
+ if (metadata) {
467
+ builder.withMetadata(metadata);
468
+ }
469
+ const message = builder.buildForModel(this.config.model);
470
+ this.state.messages.push(message);
471
+ this.updateMetadata();
472
+ return this;
473
+ }
474
+ /**
475
+ * Configure token budget
476
+ */ withTokenBudget(config) {
477
+ this.logger.debug('Configuring token budget', {
478
+ max: config.max
479
+ });
480
+ this.budgetManager = new TokenBudgetManager(config, this.config.model, this.logger);
481
+ return this;
482
+ }
483
+ /**
484
+ * Configure conversation logging
485
+ */ withLogging(config) {
486
+ this.logger.debug('Configuring conversation logging');
487
+ this.conversationLogger = new ConversationLogger(config, this.logger);
488
+ this.conversationLogger.onConversationStart({
489
+ model: this.config.model,
490
+ startTime: new Date()
491
+ });
492
+ return this;
493
+ }
494
+ /**
495
+ * Save conversation log
496
+ */ async saveLog() {
497
+ if (!this.conversationLogger) {
498
+ throw new Error('Logging not enabled. Call withLogging() first.');
499
+ }
500
+ this.conversationLogger.onConversationEnd({
501
+ totalMessages: this.state.messages.length,
502
+ toolCallsExecuted: this.state.metadata.toolCallCount,
503
+ iterations: 0,
504
+ success: true
505
+ });
506
+ return await this.conversationLogger.save();
507
+ }
508
+ /**
509
+ * Get current token usage
510
+ */ getTokenUsage() {
511
+ if (!this.budgetManager) {
512
+ return {
513
+ used: 0,
514
+ max: Infinity,
515
+ remaining: Infinity,
516
+ percentage: 0
517
+ };
518
+ }
519
+ return this.budgetManager.getCurrentUsage(this.state.messages);
520
+ }
521
+ /**
522
+ * Manually compress conversation
523
+ */ compress(_strategy) {
524
+ if (this.budgetManager) {
525
+ this.state.messages = this.budgetManager.compress(this.state.messages);
526
+ }
527
+ return this;
528
+ }
529
+ /**
530
+ * Build and return the builder (for fluent API compatibility)
531
+ */ build() {
532
+ return this;
533
+ }
534
+ /**
535
+ * Calculate position for context injection
536
+ */ calculatePosition(position) {
537
+ if (typeof position === 'number') {
538
+ return Math.max(0, Math.min(position, this.state.messages.length));
539
+ }
540
+ switch(position){
541
+ case 'end':
542
+ return this.state.messages.length;
543
+ case 'before-last':
544
+ return Math.max(0, this.state.messages.length - 1);
545
+ case 'after-system':
546
+ {
547
+ // Find last system message (reverse search for compatibility)
548
+ let lastSystemIdx = -1;
549
+ for(let i = this.state.messages.length - 1; i >= 0; i--){
550
+ if (this.state.messages[i].role === 'system') {
551
+ lastSystemIdx = i;
552
+ break;
553
+ }
554
+ }
555
+ return lastSystemIdx >= 0 ? lastSystemIdx + 1 : 0;
556
+ }
557
+ default:
558
+ return this.state.messages.length;
559
+ }
560
+ }
561
+ /**
562
+ * Format context item based on format option
563
+ */ formatContextItem(item, format) {
564
+ switch(format){
565
+ case 'structured':
566
+ {
567
+ let result = `## ${item.title || 'Context'}\n\n${item.content}`;
568
+ // Add metadata if available
569
+ const metadata = [];
570
+ if (item.source) {
571
+ metadata.push(`Source: ${item.source}`);
572
+ }
573
+ if (item.timestamp) {
574
+ metadata.push(`Timestamp: ${item.timestamp.toISOString()}`);
575
+ }
576
+ if (metadata.length > 0) {
577
+ result += `\n\n_${metadata.join(' | ')}_`;
578
+ }
579
+ return result;
580
+ }
581
+ case 'inline':
582
+ return `Note: ${item.title ? `${item.title}: ` : ''}${item.content}`;
583
+ case 'reference':
584
+ return `[Context Reference: ${item.id || 'unknown'}]\nSee attached context${item.title ? ` for ${item.title}` : ''}`;
585
+ default:
586
+ return item.content;
587
+ }
588
+ }
589
+ /**
590
+ * Update metadata after state changes
591
+ */ updateMetadata() {
592
+ this.state.metadata.messageCount = this.state.messages.length;
593
+ this.state.metadata.lastModified = new Date();
594
+ }
595
+ constructor(config, logger){
596
+ _define_property(this, "state", void 0);
597
+ _define_property(this, "config", void 0);
598
+ _define_property(this, "logger", void 0);
599
+ _define_property(this, "budgetManager", void 0);
600
+ _define_property(this, "conversationLogger", void 0);
601
+ this.config = ConversationBuilderConfigSchema.parse(config);
602
+ this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationBuilder');
603
+ this.state = {
604
+ messages: [],
605
+ metadata: {
606
+ model: this.config.model,
607
+ created: new Date(),
608
+ lastModified: new Date(),
609
+ messageCount: 0,
610
+ toolCallCount: 0
611
+ },
612
+ contextProvided: new Set(),
613
+ contextManager: new ContextManager(logger)
614
+ };
615
+ this.logger.debug('Created ConversationBuilder', {
616
+ model: this.config.model
617
+ });
618
+ }
619
+ }
620
+
621
+ export { ConversationBuilder, ConversationBuilder as default };
622
+ //# sourceMappingURL=conversation.js.map