@riotprompt/riotprompt 0.0.20 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/MIGRATION.md +235 -0
- package/README.md +2 -0
- package/SECURITY.md +132 -0
- package/dist/builder.js +6 -0
- package/dist/builder.js.map +1 -1
- package/dist/{cli.cjs → cli.js} +658 -216
- package/dist/context-manager.js +1 -1
- package/dist/conversation-logger.d.ts +17 -1
- package/dist/conversation-logger.js +21 -17
- package/dist/conversation-logger.js.map +1 -1
- package/dist/conversation.js +1 -1
- package/dist/error-handling.d.ts +52 -0
- package/dist/error-handling.js +132 -0
- package/dist/error-handling.js.map +1 -0
- package/dist/formatter.js +1 -1
- package/dist/iteration-strategy.js +1 -1
- package/dist/loader.js +60 -12
- package/dist/loader.js.map +1 -1
- package/dist/logger.d.ts +52 -0
- package/dist/logger.js +114 -14
- package/dist/logger.js.map +1 -1
- package/dist/logging-config.d.ts +84 -0
- package/dist/logging-config.js +116 -0
- package/dist/logging-config.js.map +1 -0
- package/dist/message-builder.js +1 -1
- package/dist/model-config.js +1 -1
- package/dist/override.js +10 -4
- package/dist/override.js.map +1 -1
- package/dist/recipes.js +6 -0
- package/dist/recipes.js.map +1 -1
- package/dist/reflection.js +1 -1
- package/dist/riotprompt.d.ts +9 -0
- package/dist/riotprompt.js +8 -0
- package/dist/riotprompt.js.map +1 -1
- package/dist/security/audit-logger.d.ts +61 -0
- package/dist/security/audit-logger.js +281 -0
- package/dist/security/audit-logger.js.map +1 -0
- package/dist/security/cli-security.d.ts +143 -0
- package/dist/security/cli-security.js +302 -0
- package/dist/security/cli-security.js.map +1 -0
- package/dist/security/defaults.d.ts +31 -0
- package/dist/security/defaults.js +72 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/events.d.ts +8 -0
- package/dist/security/index.d.ts +27 -0
- package/dist/security/index.js +22 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/path-guard.d.ts +161 -0
- package/dist/security/path-guard.js +327 -0
- package/dist/security/path-guard.js.map +1 -0
- package/dist/security/rate-limiter.d.ts +117 -0
- package/dist/security/rate-limiter.js +165 -0
- package/dist/security/rate-limiter.js.map +1 -0
- package/dist/security/serialization-schemas.d.ts +183 -0
- package/dist/security/serialization-schemas.js +174 -0
- package/dist/security/serialization-schemas.js.map +1 -0
- package/dist/security/timeout-guard.d.ts +123 -0
- package/dist/security/timeout-guard.js +223 -0
- package/dist/security/timeout-guard.js.map +1 -0
- package/dist/security/types.d.ts +86 -0
- package/dist/security/types.js +80 -0
- package/dist/security/types.js.map +1 -0
- package/dist/token-budget.js +1 -1
- package/dist/tools.js +1 -1
- package/dist/util/storage.js.map +1 -1
- package/guide/index.md +2 -0
- package/guide/integration.md +1109 -0
- package/guide/security.md +237 -0
- package/package.json +23 -17
- package/vite.config.cli.ts +9 -18
- package/dist/riotprompt.cjs +0 -6169
- package/dist/riotprompt.cjs.map +0 -1
package/dist/context-manager.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MaskingConfig } from './logging-config';
|
|
1
2
|
import { ConversationMessage, ToolCall } from './conversation';
|
|
2
3
|
/**
|
|
3
4
|
* Log format
|
|
@@ -13,8 +14,14 @@ export interface LogConfig {
|
|
|
13
14
|
filenameTemplate?: string;
|
|
14
15
|
includeMetadata?: boolean;
|
|
15
16
|
includePrompt?: boolean;
|
|
17
|
+
/** Enable sensitive data redaction (defaults to true) */
|
|
16
18
|
redactSensitive?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use maskingConfig instead. Custom regex patterns for redaction.
|
|
21
|
+
*/
|
|
17
22
|
redactPatterns?: RegExp[];
|
|
23
|
+
/** Masking configuration for @fjell/logging integration */
|
|
24
|
+
maskingConfig?: MaskingConfig;
|
|
18
25
|
onSaved?: (path: string) => void;
|
|
19
26
|
onError?: (error: Error) => void;
|
|
20
27
|
}
|
|
@@ -178,7 +185,16 @@ export declare class ConversationLogger {
|
|
|
178
185
|
*/
|
|
179
186
|
private appendToJSONL;
|
|
180
187
|
/**
|
|
181
|
-
* Redact sensitive content
|
|
188
|
+
* Redact sensitive content using @fjell/logging masking
|
|
189
|
+
*
|
|
190
|
+
* Automatically masks:
|
|
191
|
+
* - API keys (OpenAI, Anthropic, AWS, GitHub, etc.)
|
|
192
|
+
* - Passwords and secrets
|
|
193
|
+
* - Email addresses
|
|
194
|
+
* - SSNs
|
|
195
|
+
* - Private keys
|
|
196
|
+
* - JWTs
|
|
197
|
+
* - Large base64 blobs
|
|
182
198
|
*/
|
|
183
199
|
private redactContent;
|
|
184
200
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs__default from 'fs/promises';
|
|
2
2
|
import path__default from 'path';
|
|
3
3
|
import { wrapLogger, DEFAULT_LOGGER } from './logger.js';
|
|
4
|
+
import { maskSensitive } from './logging-config.js';
|
|
4
5
|
|
|
5
6
|
function _define_property(obj, key, value) {
|
|
6
7
|
if (key in obj) {
|
|
@@ -252,22 +253,24 @@ function _define_property(obj, key, value) {
|
|
|
252
253
|
await fs__default.appendFile(outputPath, line, 'utf-8');
|
|
253
254
|
}
|
|
254
255
|
/**
|
|
255
|
-
* Redact sensitive content
|
|
256
|
+
* Redact sensitive content using @fjell/logging masking
|
|
257
|
+
*
|
|
258
|
+
* Automatically masks:
|
|
259
|
+
* - API keys (OpenAI, Anthropic, AWS, GitHub, etc.)
|
|
260
|
+
* - Passwords and secrets
|
|
261
|
+
* - Email addresses
|
|
262
|
+
* - SSNs
|
|
263
|
+
* - Private keys
|
|
264
|
+
* - JWTs
|
|
265
|
+
* - Large base64 blobs
|
|
256
266
|
*/ redactContent(content) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
/api[_-]?key[\s:="']+[\w-]+/gi,
|
|
265
|
-
/password[\s:="']+[\w-]+/gi,
|
|
266
|
-
/Bearer\s+[\w-]+/gi,
|
|
267
|
-
/sk-[a-zA-Z0-9]{48}/g
|
|
268
|
-
];
|
|
269
|
-
for (const pattern of defaultPatterns){
|
|
270
|
-
redacted = redacted.replace(pattern, '[REDACTED]');
|
|
267
|
+
// Use Fjell's comprehensive masking
|
|
268
|
+
let redacted = maskSensitive(content, this.config.maskingConfig);
|
|
269
|
+
// Also apply any legacy custom patterns for backwards compatibility
|
|
270
|
+
if (this.config.redactPatterns && this.config.redactPatterns.length > 0) {
|
|
271
|
+
for (const pattern of this.config.redactPatterns){
|
|
272
|
+
redacted = redacted.replace(pattern, '[REDACTED]');
|
|
273
|
+
}
|
|
271
274
|
}
|
|
272
275
|
return redacted;
|
|
273
276
|
}
|
|
@@ -288,8 +291,9 @@ function _define_property(obj, key, value) {
|
|
|
288
291
|
filenameTemplate: 'conversation-{timestamp}',
|
|
289
292
|
includeMetadata: true,
|
|
290
293
|
includePrompt: false,
|
|
291
|
-
redactSensitive:
|
|
294
|
+
redactSensitive: true,
|
|
292
295
|
redactPatterns: [],
|
|
296
|
+
maskingConfig: undefined,
|
|
293
297
|
onSaved: ()=>{},
|
|
294
298
|
onError: ()=>{},
|
|
295
299
|
...config
|
|
@@ -492,5 +496,5 @@ function _define_property(obj, key, value) {
|
|
|
492
496
|
}
|
|
493
497
|
}
|
|
494
498
|
|
|
495
|
-
export { ConversationLogger, ConversationReplayer
|
|
499
|
+
export { ConversationLogger, ConversationReplayer };
|
|
496
500
|
//# sourceMappingURL=conversation-logger.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-logger.js","sources":["../src/conversation-logger.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport { DEFAULT_LOGGER, wrapLogger } from \"./logger\";\nimport type { ConversationMessage, ToolCall } from \"./conversation\";\n\n// ===== TYPE DEFINITIONS =====\n\n/**\n * Log format\n */\nexport type LogFormat = 'json' | 'markdown' | 'jsonl';\n\n/**\n * Log configuration\n */\nexport interface LogConfig {\n enabled: boolean;\n outputPath?: string;\n format?: LogFormat;\n filenameTemplate?: string;\n includeMetadata?: boolean;\n includePrompt?: boolean;\n redactSensitive?: boolean;\n redactPatterns?: RegExp[];\n onSaved?: (path: string) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Logged conversation structure\n */\nexport interface LoggedConversation {\n id: string;\n metadata: ConversationLogMetadata;\n prompt?: PromptSnapshot;\n messages: LoggedMessage[];\n summary: ConversationSummary;\n}\n\n/**\n * Conversation metadata for logging\n */\nexport interface ConversationLogMetadata {\n startTime: Date;\n endTime?: Date;\n duration?: number;\n model: string;\n template?: string;\n userContext?: Record<string, any>;\n}\n\n/**\n * Snapshot of prompt configuration\n */\nexport interface PromptSnapshot {\n persona?: string;\n instructions?: string;\n content?: string[];\n context?: string[];\n}\n\n/**\n * Logged message with metadata\n */\nexport interface LoggedMessage {\n index: number;\n timestamp: string;\n role: string;\n content: string | null;\n tool_calls?: ToolCall[];\n tool_call_id?: string;\n metadata?: MessageLogMetadata;\n}\n\n/**\n * Message metadata for logging\n */\nexport interface MessageLogMetadata {\n tokens?: number;\n source?: string;\n latency?: number;\n tool?: string;\n duration?: number;\n success?: boolean;\n [key: string]: any;\n}\n\n/**\n * Conversation summary\n */\nexport interface ConversationSummary {\n totalMessages: number;\n totalTokens?: number;\n toolCallsExecuted: number;\n iterations: number;\n finalOutput?: string;\n success: boolean;\n}\n\n/**\n * Tool call log entry\n */\nexport interface ToolCallLog {\n callId: string;\n toolName: string;\n timestamp: string;\n iteration: number;\n arguments: any;\n result: any;\n duration: number;\n success: boolean;\n error?: string;\n}\n\n// ===== CONVERSATION LOGGER =====\n\n/**\n * ConversationLogger logs conversations to various formats.\n *\n * Features:\n * - Multiple formats (JSON, Markdown, JSONL)\n * - Automatic timestamping\n * - Metadata tracking\n * - Sensitive data redaction\n * - Streaming support (JSONL)\n *\n * @example\n * ```typescript\n * const logger = new ConversationLogger({\n * enabled: true,\n * outputPath: 'logs/conversations',\n * format: 'json',\n * includeMetadata: true\n * });\n *\n * logger.onConversationStart({ model: 'gpt-4o', startTime: new Date() });\n * logger.onMessageAdded(message);\n * const path = await logger.save();\n * ```\n */\nexport class ConversationLogger {\n private config: Required<LogConfig>;\n private conversationId: string;\n private metadata: ConversationLogMetadata;\n private messages: LoggedMessage[];\n private toolCalls: ToolCallLog[];\n private startTime: Date;\n private logger: any;\n private messageIndex: number;\n private cachedOutputPath?: string;\n private writeQueue: Promise<void> = Promise.resolve();\n\n constructor(config: LogConfig, logger?: any) {\n this.config = {\n outputPath: 'logs/conversations',\n format: 'json',\n filenameTemplate: 'conversation-{timestamp}',\n includeMetadata: true,\n includePrompt: false,\n redactSensitive: false,\n redactPatterns: [],\n onSaved: () => {},\n onError: () => {},\n ...config,\n } as Required<LogConfig>;\n\n this.conversationId = this.generateId();\n this.messages = [];\n this.toolCalls = [];\n this.startTime = new Date();\n this.messageIndex = 0;\n this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationLogger');\n\n this.metadata = {\n startTime: this.startTime,\n model: 'unknown',\n };\n }\n\n /**\n * Start conversation logging\n */\n onConversationStart(metadata: Partial<ConversationLogMetadata>): void {\n this.metadata = {\n ...this.metadata,\n ...metadata,\n startTime: this.startTime,\n };\n\n // Reset cached output path to prevent file collision if logger is reused\n this.cachedOutputPath = undefined;\n\n this.logger.debug('Conversation logging started', { id: this.conversationId });\n }\n\n /**\n * Log a message\n */\n onMessageAdded(message: ConversationMessage, metadata?: MessageLogMetadata): void {\n let content = message.content;\n\n // Redact sensitive data if enabled\n if (this.config.redactSensitive && content && typeof content === 'string') {\n content = this.redactContent(content);\n }\n\n const loggedMessage: LoggedMessage = {\n index: this.messageIndex++,\n timestamp: new Date().toISOString(),\n role: message.role,\n content,\n tool_calls: message.tool_calls,\n tool_call_id: message.tool_call_id,\n metadata,\n };\n\n this.messages.push(loggedMessage);\n\n // For JSONL format, append immediately with write queue\n if (this.config.format === 'jsonl') {\n this.writeQueue = this.writeQueue\n .then(() => this.appendToJSONL(loggedMessage))\n .catch((error) => {\n this.logger.error('Failed to write JSONL message', { error });\n try {\n this.config.onError?.(error);\n } catch (callbackError) {\n this.logger.error('onError callback failed', { callbackError });\n }\n });\n }\n }\n\n /**\n * Log a tool call\n */\n onToolCall(\n callId: string,\n toolName: string,\n iteration: number,\n args: any,\n result: any,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n this.toolCalls.push({\n callId,\n toolName,\n timestamp: new Date().toISOString(),\n iteration,\n arguments: args,\n result,\n duration,\n success,\n error,\n });\n }\n\n /**\n * End conversation logging\n */\n onConversationEnd(_summary: ConversationSummary): void {\n this.metadata.endTime = new Date();\n this.metadata.duration = this.metadata.endTime.getTime() - this.startTime.getTime();\n\n this.logger.debug('Conversation logging ended', {\n messages: this.messages.length,\n duration: this.metadata.duration\n });\n }\n\n /**\n * Save conversation to disk\n */\n async save(): Promise<string> {\n if (!this.config.enabled) {\n return '';\n }\n\n try {\n const outputPath = await this.getOutputPath();\n\n switch (this.config.format) {\n case 'json':\n await this.saveAsJSON(outputPath);\n break;\n case 'markdown':\n await this.saveAsMarkdown(outputPath);\n break;\n case 'jsonl':\n // Already saved during execution\n break;\n }\n\n this.config.onSaved(outputPath);\n this.logger.info('Conversation saved', { path: outputPath });\n\n return outputPath;\n } catch (error) {\n this.config.onError(error as Error);\n this.logger.error('Failed to save conversation', { error });\n throw error;\n }\n }\n\n /**\n * Get logged conversation object\n */\n getConversation(): LoggedConversation {\n return {\n id: this.conversationId,\n metadata: this.metadata,\n messages: this.messages,\n summary: {\n totalMessages: this.messages.length,\n toolCallsExecuted: this.toolCalls.length,\n iterations: 0, // Would need to be tracked externally\n success: true,\n },\n };\n }\n\n /**\n * Generate unique conversation ID\n */\n private generateId(): string {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const random = Math.random().toString(36).substring(2, 8);\n return `conv-${timestamp}-${random}`;\n }\n\n /**\n * Get output file path (cached for JSONL to avoid recalculation)\n */\n private async getOutputPath(): Promise<string> {\n if (this.cachedOutputPath) {\n return this.cachedOutputPath;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = this.config.filenameTemplate\n .replace('{timestamp}', timestamp)\n .replace('{id}', this.conversationId)\n .replace('{template}', this.metadata.template || 'default');\n\n let ext = '.json';\n if (this.config.format === 'markdown') {\n ext = '.md';\n } else if (this.config.format === 'jsonl') {\n ext = '.jsonl';\n }\n \n const fullPath = path.join(this.config.outputPath, filename + ext);\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(fullPath), { recursive: true });\n\n // Cache path for JSONL format to ensure consistent file writes\n if (this.config.format === 'jsonl') {\n this.cachedOutputPath = fullPath;\n }\n\n return fullPath;\n }\n\n /**\n * Save as JSON\n */\n private async saveAsJSON(outputPath: string): Promise<void> {\n const data: LoggedConversation = {\n id: this.conversationId,\n metadata: this.metadata,\n messages: this.messages,\n summary: {\n totalMessages: this.messages.length,\n toolCallsExecuted: this.toolCalls.length,\n iterations: 0,\n success: true,\n },\n };\n\n await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf-8');\n }\n\n /**\n * Save as Markdown\n */\n private async saveAsMarkdown(outputPath: string): Promise<void> {\n let markdown = `# Conversation Log\\n\\n`;\n markdown += `**ID**: ${this.conversationId}\\n`;\n markdown += `**Started**: ${this.metadata.startTime.toISOString()}\\n`;\n if (this.metadata.duration) {\n markdown += `**Duration**: ${(this.metadata.duration / 1000).toFixed(1)}s\\n`;\n }\n markdown += `**Model**: ${this.metadata.model}\\n`;\n if (this.metadata.template) {\n markdown += `**Template**: ${this.metadata.template}\\n`;\n }\n markdown += `\\n## Conversation\\n\\n`;\n\n for (const msg of this.messages) {\n const time = new Date(msg.timestamp).toLocaleTimeString();\n markdown += `### Message ${msg.index + 1} (${time}) - ${msg.role}\\n\\n`;\n\n if (msg.content) {\n markdown += `\\`\\`\\`\\n${msg.content}\\n\\`\\`\\`\\n\\n`;\n }\n\n if (msg.tool_calls) {\n markdown += `**Tool Calls:**\\n`;\n for (const call of msg.tool_calls) {\n markdown += `- ${call.function.name}: \\`${call.function.arguments}\\`\\n`;\n }\n markdown += `\\n`;\n }\n\n if (msg.metadata) {\n markdown += `*Metadata: ${JSON.stringify(msg.metadata)}*\\n\\n`;\n }\n }\n\n markdown += `## Summary\\n\\n`;\n markdown += `- **Total Messages**: ${this.messages.length}\\n`;\n markdown += `- **Tool Calls**: ${this.toolCalls.length}\\n`;\n\n await fs.writeFile(outputPath, markdown, 'utf-8');\n }\n\n /**\n * Append to JSONL file (streaming)\n */\n private async appendToJSONL(message: LoggedMessage): Promise<void> {\n const outputPath = await this.getOutputPath();\n const line = JSON.stringify(message) + '\\n';\n await fs.appendFile(outputPath, line, 'utf-8');\n }\n\n /**\n * Redact sensitive content\n */\n private redactContent(content: string): string {\n let redacted = content;\n\n // Apply custom patterns\n for (const pattern of this.config.redactPatterns) {\n redacted = redacted.replace(pattern, '[REDACTED]');\n }\n\n // Default patterns\n const defaultPatterns = [\n /api[_-]?key[\\s:=\"']+[\\w-]+/gi,\n /password[\\s:=\"']+[\\w-]+/gi,\n /Bearer\\s+[\\w-]+/gi,\n /sk-[a-zA-Z0-9]{48}/g,\n ];\n\n for (const pattern of defaultPatterns) {\n redacted = redacted.replace(pattern, '[REDACTED]');\n }\n\n return redacted;\n }\n}\n\n// ===== CONVERSATION REPLAYER =====\n\n/**\n * Replay options\n */\nexport interface ReplayOptions {\n model?: string;\n maxIterations?: number;\n retryFailedTools?: boolean;\n toolTimeout?: number;\n expectSimilarOutput?: boolean;\n}\n\n/**\n * Replay result\n */\nexport interface ReplayResult {\n success: boolean;\n conversation: LoggedConversation;\n errors?: Error[];\n}\n\n/**\n * Comparison result\n */\nexport interface ComparisonResult {\n messageDiff: number;\n toolCallDiff: number;\n tokenDiff?: number;\n outputSimilarity: number;\n costSavings?: number;\n}\n\n/**\n * ConversationReplayer loads and replays logged conversations.\n *\n * Features:\n * - Load from various formats\n * - Replay conversations\n * - Compare replays with originals\n * - Export to different formats\n *\n * @example\n * ```typescript\n * const replayer = await ConversationReplayer.load('logs/conv.json');\n *\n * console.log('Messages:', replayer.messages.length);\n * console.log('Tool calls:', replayer.getToolCalls().length);\n *\n * const timeline = replayer.getTimeline();\n * console.log('Events:', timeline.length);\n * ```\n */\nexport class ConversationReplayer {\n private conversation: LoggedConversation;\n private logger: any;\n\n private constructor(conversation: LoggedConversation, logger?: any) {\n this.conversation = conversation;\n this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');\n }\n\n /**\n * Load conversation from file\n */\n static async load(filePath: string, logger?: any): Promise<ConversationReplayer> {\n const wlogger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');\n wlogger.debug('Loading conversation', { path: filePath });\n\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n\n // Determine format by extension\n if (filePath.endsWith('.json')) {\n const data: LoggedConversation = JSON.parse(content);\n return new ConversationReplayer(data, logger);\n } else if (filePath.endsWith('.jsonl')) {\n const lines = content.trim().split('\\n');\n const messages = lines.map(line => JSON.parse(line));\n\n const conversation: LoggedConversation = {\n id: `replayer-${Date.now()}`,\n metadata: {\n startTime: new Date(),\n model: 'unknown'\n },\n messages,\n summary: {\n totalMessages: messages.length,\n toolCallsExecuted: 0,\n iterations: 0,\n success: true\n }\n };\n\n return new ConversationReplayer(conversation, logger);\n } else {\n throw new Error(`Unsupported format: ${filePath}`);\n }\n } catch (error) {\n wlogger.error('Failed to load conversation', { path: filePath, error });\n throw error;\n }\n }\n\n /**\n * Load latest conversation from directory\n */\n static async loadLatest(directory: string, logger?: any): Promise<ConversationReplayer> {\n const files = await fs.readdir(directory);\n const jsonFiles = files.filter(f => f.endsWith('.json')).sort().reverse();\n\n if (jsonFiles.length === 0) {\n throw new Error(`No conversation logs found in ${directory}`);\n }\n\n const latestPath = path.join(directory, jsonFiles[0]);\n return ConversationReplayer.load(latestPath, logger);\n }\n\n /**\n * Get all messages\n */\n get messages(): LoggedMessage[] {\n return this.conversation.messages;\n }\n\n /**\n * Get conversation metadata\n */\n getMetadata(): ConversationLogMetadata {\n return { ...this.conversation.metadata };\n }\n\n /**\n * Get tool calls\n */\n getToolCalls(): ToolCallLog[] {\n const toolCalls: ToolCallLog[] = [];\n\n for (const msg of this.conversation.messages) {\n if (msg.tool_calls) {\n for (const call of msg.tool_calls) {\n // Parse arguments with error handling\n let parsedArgs: any;\n try {\n parsedArgs = JSON.parse(call.function.arguments);\n } catch (error) {\n this.logger.warn('Failed to parse tool call arguments', {\n callId: call.id,\n error: error instanceof Error ? error.message : String(error)\n });\n parsedArgs = { __parse_error: true, raw: call.function.arguments };\n }\n\n toolCalls.push({\n callId: call.id,\n toolName: call.function.name,\n timestamp: msg.timestamp,\n iteration: 0, // Would need to be calculated\n arguments: parsedArgs,\n result: null, // Would need to find corresponding tool message\n duration: 0,\n success: true,\n });\n }\n }\n }\n\n return toolCalls;\n }\n\n /**\n * Get message at index\n */\n getMessageAt(index: number): LoggedMessage | undefined {\n return this.conversation.messages[index];\n }\n\n /**\n * Get timeline of events\n */\n getTimeline(): TimelineEvent[] {\n const events: TimelineEvent[] = [];\n\n for (const msg of this.conversation.messages) {\n events.push({\n timestamp: msg.timestamp,\n iteration: 0, // Would need iteration tracking\n type: 'message',\n description: `${msg.role} message`,\n });\n }\n\n return events;\n }\n\n /**\n * Export to format\n */\n async exportToFormat(format: LogFormat, outputPath: string): Promise<string> {\n this.logger.debug('Exporting to format', { format, path: outputPath });\n\n switch (format) {\n case 'json':\n await fs.writeFile(outputPath, JSON.stringify(this.conversation, null, 2), 'utf-8');\n break;\n case 'markdown':\n await this.exportMarkdown(outputPath);\n break;\n case 'jsonl': {\n const lines = this.messages.map(m => JSON.stringify(m)).join('\\n');\n await fs.writeFile(outputPath, lines, 'utf-8');\n break;\n }\n }\n\n return outputPath;\n }\n\n /**\n * Export as markdown\n */\n private async exportMarkdown(outputPath: string): Promise<void> {\n let markdown = `# Conversation Log\\n\\n`;\n markdown += `**ID**: ${this.conversation.id}\\n`;\n\n const startTime = typeof this.conversation.metadata.startTime === 'string'\n ? this.conversation.metadata.startTime\n : this.conversation.metadata.startTime.toISOString();\n\n markdown += `**Started**: ${startTime}\\n\\n`;\n\n for (const msg of this.conversation.messages) {\n markdown += `## ${msg.role.toUpperCase()} (${msg.index})\\n\\n`;\n if (msg.content) {\n markdown += `${msg.content}\\n\\n`;\n }\n }\n\n await fs.writeFile(outputPath, markdown, 'utf-8');\n }\n}\n\n/**\n * Timeline event interface\n */\ninterface TimelineEvent {\n timestamp: string;\n iteration: number;\n type: string;\n description: string;\n duration?: number;\n success?: boolean;\n}\n\nexport default ConversationLogger;\n\n"],"names":["ConversationLogger","onConversationStart","metadata","startTime","cachedOutputPath","undefined","logger","debug","id","conversationId","onMessageAdded","message","content","config","redactSensitive","redactContent","loggedMessage","index","messageIndex","timestamp","Date","toISOString","role","tool_calls","tool_call_id","messages","push","format","writeQueue","then","appendToJSONL","catch","error","onError","callbackError","onToolCall","callId","toolName","iteration","args","result","duration","success","toolCalls","arguments","onConversationEnd","_summary","endTime","getTime","length","save","enabled","outputPath","getOutputPath","saveAsJSON","saveAsMarkdown","onSaved","info","path","getConversation","summary","totalMessages","toolCallsExecuted","iterations","generateId","replace","random","Math","toString","substring","filename","filenameTemplate","template","ext","fullPath","join","fs","mkdir","dirname","recursive","data","writeFile","JSON","stringify","markdown","toFixed","model","msg","time","toLocaleTimeString","call","function","name","line","appendFile","redacted","pattern","redactPatterns","defaultPatterns","Promise","resolve","includeMetadata","includePrompt","wrapLogger","DEFAULT_LOGGER","ConversationReplayer","load","filePath","wlogger","readFile","endsWith","parse","lines","trim","split","map","conversation","now","Error","loadLatest","directory","files","readdir","jsonFiles","filter","f","sort","reverse","latestPath","getMetadata","getToolCalls","parsedArgs","warn","String","__parse_error","raw","getMessageAt","getTimeline","events","type","description","exportToFormat","exportMarkdown","m","toUpperCase"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversation-logger.js","sources":["../src/conversation-logger.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport { DEFAULT_LOGGER, wrapLogger } from \"./logger\";\nimport { maskSensitive, type MaskingConfig } from \"./logging-config\";\nimport type { ConversationMessage, ToolCall } from \"./conversation\";\n\n// ===== TYPE DEFINITIONS =====\n\n/**\n * Log format\n */\nexport type LogFormat = 'json' | 'markdown' | 'jsonl';\n\n/**\n * Log configuration\n */\nexport interface LogConfig {\n enabled: boolean;\n outputPath?: string;\n format?: LogFormat;\n filenameTemplate?: string;\n includeMetadata?: boolean;\n includePrompt?: boolean;\n /** Enable sensitive data redaction (defaults to true) */\n redactSensitive?: boolean;\n /** \n * @deprecated Use maskingConfig instead. Custom regex patterns for redaction.\n */\n redactPatterns?: RegExp[];\n /** Masking configuration for @fjell/logging integration */\n maskingConfig?: MaskingConfig;\n onSaved?: (path: string) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Logged conversation structure\n */\nexport interface LoggedConversation {\n id: string;\n metadata: ConversationLogMetadata;\n prompt?: PromptSnapshot;\n messages: LoggedMessage[];\n summary: ConversationSummary;\n}\n\n/**\n * Conversation metadata for logging\n */\nexport interface ConversationLogMetadata {\n startTime: Date;\n endTime?: Date;\n duration?: number;\n model: string;\n template?: string;\n userContext?: Record<string, any>;\n}\n\n/**\n * Snapshot of prompt configuration\n */\nexport interface PromptSnapshot {\n persona?: string;\n instructions?: string;\n content?: string[];\n context?: string[];\n}\n\n/**\n * Logged message with metadata\n */\nexport interface LoggedMessage {\n index: number;\n timestamp: string;\n role: string;\n content: string | null;\n tool_calls?: ToolCall[];\n tool_call_id?: string;\n metadata?: MessageLogMetadata;\n}\n\n/**\n * Message metadata for logging\n */\nexport interface MessageLogMetadata {\n tokens?: number;\n source?: string;\n latency?: number;\n tool?: string;\n duration?: number;\n success?: boolean;\n [key: string]: any;\n}\n\n/**\n * Conversation summary\n */\nexport interface ConversationSummary {\n totalMessages: number;\n totalTokens?: number;\n toolCallsExecuted: number;\n iterations: number;\n finalOutput?: string;\n success: boolean;\n}\n\n/**\n * Tool call log entry\n */\nexport interface ToolCallLog {\n callId: string;\n toolName: string;\n timestamp: string;\n iteration: number;\n arguments: any;\n result: any;\n duration: number;\n success: boolean;\n error?: string;\n}\n\n// ===== CONVERSATION LOGGER =====\n\n/**\n * ConversationLogger logs conversations to various formats.\n *\n * Features:\n * - Multiple formats (JSON, Markdown, JSONL)\n * - Automatic timestamping\n * - Metadata tracking\n * - Sensitive data redaction\n * - Streaming support (JSONL)\n *\n * @example\n * ```typescript\n * const logger = new ConversationLogger({\n * enabled: true,\n * outputPath: 'logs/conversations',\n * format: 'json',\n * includeMetadata: true\n * });\n *\n * logger.onConversationStart({ model: 'gpt-4o', startTime: new Date() });\n * logger.onMessageAdded(message);\n * const path = await logger.save();\n * ```\n */\nexport class ConversationLogger {\n private config: Required<LogConfig>;\n private conversationId: string;\n private metadata: ConversationLogMetadata;\n private messages: LoggedMessage[];\n private toolCalls: ToolCallLog[];\n private startTime: Date;\n private logger: any;\n private messageIndex: number;\n private cachedOutputPath?: string;\n private writeQueue: Promise<void> = Promise.resolve();\n\n constructor(config: LogConfig, logger?: any) {\n this.config = {\n outputPath: 'logs/conversations',\n format: 'json',\n filenameTemplate: 'conversation-{timestamp}',\n includeMetadata: true,\n includePrompt: false,\n redactSensitive: true, // Now defaults to true for security\n redactPatterns: [],\n maskingConfig: undefined,\n onSaved: () => {},\n onError: () => {},\n ...config,\n } as Required<LogConfig>;\n\n this.conversationId = this.generateId();\n this.messages = [];\n this.toolCalls = [];\n this.startTime = new Date();\n this.messageIndex = 0;\n this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationLogger');\n\n this.metadata = {\n startTime: this.startTime,\n model: 'unknown',\n };\n }\n\n /**\n * Start conversation logging\n */\n onConversationStart(metadata: Partial<ConversationLogMetadata>): void {\n this.metadata = {\n ...this.metadata,\n ...metadata,\n startTime: this.startTime,\n };\n\n // Reset cached output path to prevent file collision if logger is reused\n this.cachedOutputPath = undefined;\n\n this.logger.debug('Conversation logging started', { id: this.conversationId });\n }\n\n /**\n * Log a message\n */\n onMessageAdded(message: ConversationMessage, metadata?: MessageLogMetadata): void {\n let content = message.content;\n\n // Redact sensitive data if enabled\n if (this.config.redactSensitive && content && typeof content === 'string') {\n content = this.redactContent(content);\n }\n\n const loggedMessage: LoggedMessage = {\n index: this.messageIndex++,\n timestamp: new Date().toISOString(),\n role: message.role,\n content,\n tool_calls: message.tool_calls,\n tool_call_id: message.tool_call_id,\n metadata,\n };\n\n this.messages.push(loggedMessage);\n\n // For JSONL format, append immediately with write queue\n if (this.config.format === 'jsonl') {\n this.writeQueue = this.writeQueue\n .then(() => this.appendToJSONL(loggedMessage))\n .catch((error) => {\n this.logger.error('Failed to write JSONL message', { error });\n try {\n this.config.onError?.(error);\n } catch (callbackError) {\n this.logger.error('onError callback failed', { callbackError });\n }\n });\n }\n }\n\n /**\n * Log a tool call\n */\n onToolCall(\n callId: string,\n toolName: string,\n iteration: number,\n args: any,\n result: any,\n duration: number,\n success: boolean,\n error?: string\n ): void {\n this.toolCalls.push({\n callId,\n toolName,\n timestamp: new Date().toISOString(),\n iteration,\n arguments: args,\n result,\n duration,\n success,\n error,\n });\n }\n\n /**\n * End conversation logging\n */\n onConversationEnd(_summary: ConversationSummary): void {\n this.metadata.endTime = new Date();\n this.metadata.duration = this.metadata.endTime.getTime() - this.startTime.getTime();\n\n this.logger.debug('Conversation logging ended', {\n messages: this.messages.length,\n duration: this.metadata.duration\n });\n }\n\n /**\n * Save conversation to disk\n */\n async save(): Promise<string> {\n if (!this.config.enabled) {\n return '';\n }\n\n try {\n const outputPath = await this.getOutputPath();\n\n switch (this.config.format) {\n case 'json':\n await this.saveAsJSON(outputPath);\n break;\n case 'markdown':\n await this.saveAsMarkdown(outputPath);\n break;\n case 'jsonl':\n // Already saved during execution\n break;\n }\n\n this.config.onSaved(outputPath);\n this.logger.info('Conversation saved', { path: outputPath });\n\n return outputPath;\n } catch (error) {\n this.config.onError(error as Error);\n this.logger.error('Failed to save conversation', { error });\n throw error;\n }\n }\n\n /**\n * Get logged conversation object\n */\n getConversation(): LoggedConversation {\n return {\n id: this.conversationId,\n metadata: this.metadata,\n messages: this.messages,\n summary: {\n totalMessages: this.messages.length,\n toolCallsExecuted: this.toolCalls.length,\n iterations: 0, // Would need to be tracked externally\n success: true,\n },\n };\n }\n\n /**\n * Generate unique conversation ID\n */\n private generateId(): string {\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const random = Math.random().toString(36).substring(2, 8);\n return `conv-${timestamp}-${random}`;\n }\n\n /**\n * Get output file path (cached for JSONL to avoid recalculation)\n */\n private async getOutputPath(): Promise<string> {\n if (this.cachedOutputPath) {\n return this.cachedOutputPath;\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const filename = this.config.filenameTemplate\n .replace('{timestamp}', timestamp)\n .replace('{id}', this.conversationId)\n .replace('{template}', this.metadata.template || 'default');\n\n let ext = '.json';\n if (this.config.format === 'markdown') {\n ext = '.md';\n } else if (this.config.format === 'jsonl') {\n ext = '.jsonl';\n }\n \n const fullPath = path.join(this.config.outputPath, filename + ext);\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(fullPath), { recursive: true });\n\n // Cache path for JSONL format to ensure consistent file writes\n if (this.config.format === 'jsonl') {\n this.cachedOutputPath = fullPath;\n }\n\n return fullPath;\n }\n\n /**\n * Save as JSON\n */\n private async saveAsJSON(outputPath: string): Promise<void> {\n const data: LoggedConversation = {\n id: this.conversationId,\n metadata: this.metadata,\n messages: this.messages,\n summary: {\n totalMessages: this.messages.length,\n toolCallsExecuted: this.toolCalls.length,\n iterations: 0,\n success: true,\n },\n };\n\n await fs.writeFile(outputPath, JSON.stringify(data, null, 2), 'utf-8');\n }\n\n /**\n * Save as Markdown\n */\n private async saveAsMarkdown(outputPath: string): Promise<void> {\n let markdown = `# Conversation Log\\n\\n`;\n markdown += `**ID**: ${this.conversationId}\\n`;\n markdown += `**Started**: ${this.metadata.startTime.toISOString()}\\n`;\n if (this.metadata.duration) {\n markdown += `**Duration**: ${(this.metadata.duration / 1000).toFixed(1)}s\\n`;\n }\n markdown += `**Model**: ${this.metadata.model}\\n`;\n if (this.metadata.template) {\n markdown += `**Template**: ${this.metadata.template}\\n`;\n }\n markdown += `\\n## Conversation\\n\\n`;\n\n for (const msg of this.messages) {\n const time = new Date(msg.timestamp).toLocaleTimeString();\n markdown += `### Message ${msg.index + 1} (${time}) - ${msg.role}\\n\\n`;\n\n if (msg.content) {\n markdown += `\\`\\`\\`\\n${msg.content}\\n\\`\\`\\`\\n\\n`;\n }\n\n if (msg.tool_calls) {\n markdown += `**Tool Calls:**\\n`;\n for (const call of msg.tool_calls) {\n markdown += `- ${call.function.name}: \\`${call.function.arguments}\\`\\n`;\n }\n markdown += `\\n`;\n }\n\n if (msg.metadata) {\n markdown += `*Metadata: ${JSON.stringify(msg.metadata)}*\\n\\n`;\n }\n }\n\n markdown += `## Summary\\n\\n`;\n markdown += `- **Total Messages**: ${this.messages.length}\\n`;\n markdown += `- **Tool Calls**: ${this.toolCalls.length}\\n`;\n\n await fs.writeFile(outputPath, markdown, 'utf-8');\n }\n\n /**\n * Append to JSONL file (streaming)\n */\n private async appendToJSONL(message: LoggedMessage): Promise<void> {\n const outputPath = await this.getOutputPath();\n const line = JSON.stringify(message) + '\\n';\n await fs.appendFile(outputPath, line, 'utf-8');\n }\n\n /**\n * Redact sensitive content using @fjell/logging masking\n * \n * Automatically masks:\n * - API keys (OpenAI, Anthropic, AWS, GitHub, etc.)\n * - Passwords and secrets\n * - Email addresses\n * - SSNs\n * - Private keys\n * - JWTs\n * - Large base64 blobs\n */\n private redactContent(content: string): string {\n // Use Fjell's comprehensive masking\n let redacted = maskSensitive(content, this.config.maskingConfig);\n\n // Also apply any legacy custom patterns for backwards compatibility\n if (this.config.redactPatterns && this.config.redactPatterns.length > 0) {\n for (const pattern of this.config.redactPatterns) {\n redacted = redacted.replace(pattern, '[REDACTED]');\n }\n }\n\n return redacted;\n }\n}\n\n// ===== CONVERSATION REPLAYER =====\n\n/**\n * Replay options\n */\nexport interface ReplayOptions {\n model?: string;\n maxIterations?: number;\n retryFailedTools?: boolean;\n toolTimeout?: number;\n expectSimilarOutput?: boolean;\n}\n\n/**\n * Replay result\n */\nexport interface ReplayResult {\n success: boolean;\n conversation: LoggedConversation;\n errors?: Error[];\n}\n\n/**\n * Comparison result\n */\nexport interface ComparisonResult {\n messageDiff: number;\n toolCallDiff: number;\n tokenDiff?: number;\n outputSimilarity: number;\n costSavings?: number;\n}\n\n/**\n * ConversationReplayer loads and replays logged conversations.\n *\n * Features:\n * - Load from various formats\n * - Replay conversations\n * - Compare replays with originals\n * - Export to different formats\n *\n * @example\n * ```typescript\n * const replayer = await ConversationReplayer.load('logs/conv.json');\n *\n * console.log('Messages:', replayer.messages.length);\n * console.log('Tool calls:', replayer.getToolCalls().length);\n *\n * const timeline = replayer.getTimeline();\n * console.log('Events:', timeline.length);\n * ```\n */\nexport class ConversationReplayer {\n private conversation: LoggedConversation;\n private logger: any;\n\n private constructor(conversation: LoggedConversation, logger?: any) {\n this.conversation = conversation;\n this.logger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');\n }\n\n /**\n * Load conversation from file\n */\n static async load(filePath: string, logger?: any): Promise<ConversationReplayer> {\n const wlogger = wrapLogger(logger || DEFAULT_LOGGER, 'ConversationReplayer');\n wlogger.debug('Loading conversation', { path: filePath });\n\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n\n // Determine format by extension\n if (filePath.endsWith('.json')) {\n const data: LoggedConversation = JSON.parse(content);\n return new ConversationReplayer(data, logger);\n } else if (filePath.endsWith('.jsonl')) {\n const lines = content.trim().split('\\n');\n const messages = lines.map(line => JSON.parse(line));\n\n const conversation: LoggedConversation = {\n id: `replayer-${Date.now()}`,\n metadata: {\n startTime: new Date(),\n model: 'unknown'\n },\n messages,\n summary: {\n totalMessages: messages.length,\n toolCallsExecuted: 0,\n iterations: 0,\n success: true\n }\n };\n\n return new ConversationReplayer(conversation, logger);\n } else {\n throw new Error(`Unsupported format: ${filePath}`);\n }\n } catch (error) {\n wlogger.error('Failed to load conversation', { path: filePath, error });\n throw error;\n }\n }\n\n /**\n * Load latest conversation from directory\n */\n static async loadLatest(directory: string, logger?: any): Promise<ConversationReplayer> {\n const files = await fs.readdir(directory);\n const jsonFiles = files.filter(f => f.endsWith('.json')).sort().reverse();\n\n if (jsonFiles.length === 0) {\n throw new Error(`No conversation logs found in ${directory}`);\n }\n\n const latestPath = path.join(directory, jsonFiles[0]);\n return ConversationReplayer.load(latestPath, logger);\n }\n\n /**\n * Get all messages\n */\n get messages(): LoggedMessage[] {\n return this.conversation.messages;\n }\n\n /**\n * Get conversation metadata\n */\n getMetadata(): ConversationLogMetadata {\n return { ...this.conversation.metadata };\n }\n\n /**\n * Get tool calls\n */\n getToolCalls(): ToolCallLog[] {\n const toolCalls: ToolCallLog[] = [];\n\n for (const msg of this.conversation.messages) {\n if (msg.tool_calls) {\n for (const call of msg.tool_calls) {\n // Parse arguments with error handling\n let parsedArgs: any;\n try {\n parsedArgs = JSON.parse(call.function.arguments);\n } catch (error) {\n this.logger.warn('Failed to parse tool call arguments', {\n callId: call.id,\n error: error instanceof Error ? error.message : String(error)\n });\n parsedArgs = { __parse_error: true, raw: call.function.arguments };\n }\n\n toolCalls.push({\n callId: call.id,\n toolName: call.function.name,\n timestamp: msg.timestamp,\n iteration: 0, // Would need to be calculated\n arguments: parsedArgs,\n result: null, // Would need to find corresponding tool message\n duration: 0,\n success: true,\n });\n }\n }\n }\n\n return toolCalls;\n }\n\n /**\n * Get message at index\n */\n getMessageAt(index: number): LoggedMessage | undefined {\n return this.conversation.messages[index];\n }\n\n /**\n * Get timeline of events\n */\n getTimeline(): TimelineEvent[] {\n const events: TimelineEvent[] = [];\n\n for (const msg of this.conversation.messages) {\n events.push({\n timestamp: msg.timestamp,\n iteration: 0, // Would need iteration tracking\n type: 'message',\n description: `${msg.role} message`,\n });\n }\n\n return events;\n }\n\n /**\n * Export to format\n */\n async exportToFormat(format: LogFormat, outputPath: string): Promise<string> {\n this.logger.debug('Exporting to format', { format, path: outputPath });\n\n switch (format) {\n case 'json':\n await fs.writeFile(outputPath, JSON.stringify(this.conversation, null, 2), 'utf-8');\n break;\n case 'markdown':\n await this.exportMarkdown(outputPath);\n break;\n case 'jsonl': {\n const lines = this.messages.map(m => JSON.stringify(m)).join('\\n');\n await fs.writeFile(outputPath, lines, 'utf-8');\n break;\n }\n }\n\n return outputPath;\n }\n\n /**\n * Export as markdown\n */\n private async exportMarkdown(outputPath: string): Promise<void> {\n let markdown = `# Conversation Log\\n\\n`;\n markdown += `**ID**: ${this.conversation.id}\\n`;\n\n const startTime = typeof this.conversation.metadata.startTime === 'string'\n ? this.conversation.metadata.startTime\n : this.conversation.metadata.startTime.toISOString();\n\n markdown += `**Started**: ${startTime}\\n\\n`;\n\n for (const msg of this.conversation.messages) {\n markdown += `## ${msg.role.toUpperCase()} (${msg.index})\\n\\n`;\n if (msg.content) {\n markdown += `${msg.content}\\n\\n`;\n }\n }\n\n await fs.writeFile(outputPath, markdown, 'utf-8');\n }\n}\n\n/**\n * Timeline event interface\n */\ninterface TimelineEvent {\n timestamp: string;\n iteration: number;\n type: string;\n description: string;\n duration?: number;\n success?: boolean;\n}\n\nexport default ConversationLogger;\n\n"],"names":["ConversationLogger","onConversationStart","metadata","startTime","cachedOutputPath","undefined","logger","debug","id","conversationId","onMessageAdded","message","content","config","redactSensitive","redactContent","loggedMessage","index","messageIndex","timestamp","Date","toISOString","role","tool_calls","tool_call_id","messages","push","format","writeQueue","then","appendToJSONL","catch","error","onError","callbackError","onToolCall","callId","toolName","iteration","args","result","duration","success","toolCalls","arguments","onConversationEnd","_summary","endTime","getTime","length","save","enabled","outputPath","getOutputPath","saveAsJSON","saveAsMarkdown","onSaved","info","path","getConversation","summary","totalMessages","toolCallsExecuted","iterations","generateId","replace","random","Math","toString","substring","filename","filenameTemplate","template","ext","fullPath","join","fs","mkdir","dirname","recursive","data","writeFile","JSON","stringify","markdown","toFixed","model","msg","time","toLocaleTimeString","call","function","name","line","appendFile","redacted","maskSensitive","maskingConfig","redactPatterns","pattern","Promise","resolve","includeMetadata","includePrompt","wrapLogger","DEFAULT_LOGGER","ConversationReplayer","load","filePath","wlogger","readFile","endsWith","parse","lines","trim","split","map","conversation","now","Error","loadLatest","directory","files","readdir","jsonFiles","filter","f","sort","reverse","latestPath","getMetadata","getToolCalls","parsedArgs","warn","String","__parse_error","raw","getMessageAt","getTimeline","events","type","description","exportToFormat","exportMarkdown","m","toUpperCase"],"mappings":"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"}
|
package/dist/conversation.js
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { configureErrorSanitizer, configurePathSanitizer, configureSecretGuard, ErrorSanitizerConfig, SanitizedErrorResult, Logger as SpotcleanLogger, ErrorHandlingOptions } from '@theunwalked/spotclean';
|
|
2
|
+
export { configureErrorSanitizer, configurePathSanitizer, configureSecretGuard, type ErrorSanitizerConfig, type SanitizedErrorResult, type ErrorHandlingOptions, };
|
|
3
|
+
/**
|
|
4
|
+
* Initialize error handling with secure defaults.
|
|
5
|
+
* Call this at application startup.
|
|
6
|
+
*/
|
|
7
|
+
export declare function initializeErrorHandling(options?: {
|
|
8
|
+
basePaths?: string[];
|
|
9
|
+
environment?: 'production' | 'development' | 'test';
|
|
10
|
+
}): void;
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize an error for safe external exposure.
|
|
13
|
+
*
|
|
14
|
+
* @param error - The error to sanitize
|
|
15
|
+
* @param context - Optional context for debugging
|
|
16
|
+
* @returns Sanitized error result with external and internal details
|
|
17
|
+
*/
|
|
18
|
+
export declare function sanitize(error: unknown, context?: Record<string, unknown>): SanitizedErrorResult;
|
|
19
|
+
/**
|
|
20
|
+
* Create a safe error that can be thrown externally.
|
|
21
|
+
*
|
|
22
|
+
* @param error - The error to make safe
|
|
23
|
+
* @param context - Optional context for debugging
|
|
24
|
+
* @returns New Error with sanitized message
|
|
25
|
+
*/
|
|
26
|
+
export declare function createSafeError(error: unknown, context?: Record<string, unknown>): Error & {
|
|
27
|
+
correlationId?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Wrap an async function with automatic error handling.
|
|
31
|
+
*
|
|
32
|
+
* @param fn - The async function to wrap
|
|
33
|
+
* @param options - Error handling options
|
|
34
|
+
* @returns Wrapped function that sanitizes errors
|
|
35
|
+
*/
|
|
36
|
+
export declare function withErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T, options?: ErrorHandlingOptions): T;
|
|
37
|
+
/**
|
|
38
|
+
* Handle an error by logging internally and throwing a sanitized version.
|
|
39
|
+
*
|
|
40
|
+
* @param error - The error that occurred
|
|
41
|
+
* @param context - Optional context for debugging
|
|
42
|
+
* @param logger - Optional logger for internal logging
|
|
43
|
+
*/
|
|
44
|
+
export declare function handleError(error: unknown, context?: Record<string, unknown>, logger?: SpotcleanLogger): never;
|
|
45
|
+
/**
|
|
46
|
+
* Format an error for display to users.
|
|
47
|
+
* Returns a user-friendly message with optional correlation ID.
|
|
48
|
+
*
|
|
49
|
+
* @param error - The error to format
|
|
50
|
+
* @returns User-friendly error string
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatErrorForDisplay(error: unknown): string;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createSafeError as createSafeError$1, configureErrorSanitizer, configurePathSanitizer, configureSecretGuard, sanitize as sanitize$1, withErrorHandling as withErrorHandling$1 } from '@theunwalked/spotclean';
|
|
2
|
+
export { configureErrorSanitizer, configurePathSanitizer, configureSecretGuard } from '@theunwalked/spotclean';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Initialize error handling with secure defaults.
|
|
6
|
+
* Call this at application startup.
|
|
7
|
+
*/ function initializeErrorHandling(options = {}) {
|
|
8
|
+
const isProduction = options.environment === 'production' || process.env.NODE_ENV === 'production';
|
|
9
|
+
// Configure the error sanitizer
|
|
10
|
+
configureErrorSanitizer({
|
|
11
|
+
enabled: true,
|
|
12
|
+
environment: isProduction ? 'production' : 'development',
|
|
13
|
+
includeCorrelationId: true,
|
|
14
|
+
sanitizeStackTraces: isProduction,
|
|
15
|
+
maxMessageLength: 500
|
|
16
|
+
});
|
|
17
|
+
// Configure path sanitization
|
|
18
|
+
configurePathSanitizer({
|
|
19
|
+
enabled: true,
|
|
20
|
+
basePaths: options.basePaths || [
|
|
21
|
+
process.cwd()
|
|
22
|
+
],
|
|
23
|
+
redactSystemPaths: isProduction
|
|
24
|
+
});
|
|
25
|
+
// Configure secret guard with kjerneverk-specific patterns
|
|
26
|
+
configureSecretGuard({
|
|
27
|
+
enabled: true,
|
|
28
|
+
redactionText: '[REDACTED]',
|
|
29
|
+
preservePartial: false,
|
|
30
|
+
preserveLength: 0,
|
|
31
|
+
customPatterns: [
|
|
32
|
+
// OpenAI API keys
|
|
33
|
+
{
|
|
34
|
+
name: 'openai',
|
|
35
|
+
pattern: /sk-[a-zA-Z0-9]{20,}/g,
|
|
36
|
+
description: 'OpenAI API key'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'openai-proj',
|
|
40
|
+
pattern: /sk-proj-[a-zA-Z0-9_-]+/g,
|
|
41
|
+
description: 'OpenAI project key'
|
|
42
|
+
},
|
|
43
|
+
// Anthropic API keys
|
|
44
|
+
{
|
|
45
|
+
name: 'anthropic',
|
|
46
|
+
pattern: /sk-ant-[a-zA-Z0-9_-]+/g,
|
|
47
|
+
description: 'Anthropic API key'
|
|
48
|
+
},
|
|
49
|
+
// Gemini API keys
|
|
50
|
+
{
|
|
51
|
+
name: 'gemini',
|
|
52
|
+
pattern: /AIza[a-zA-Z0-9_-]{35}/g,
|
|
53
|
+
description: 'Google Gemini API key'
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sanitize an error for safe external exposure.
|
|
60
|
+
*
|
|
61
|
+
* @param error - The error to sanitize
|
|
62
|
+
* @param context - Optional context for debugging
|
|
63
|
+
* @returns Sanitized error result with external and internal details
|
|
64
|
+
*/ function sanitize(error, context) {
|
|
65
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
66
|
+
return sanitize$1(err, context);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a safe error that can be thrown externally.
|
|
70
|
+
*
|
|
71
|
+
* @param error - The error to make safe
|
|
72
|
+
* @param context - Optional context for debugging
|
|
73
|
+
* @returns New Error with sanitized message
|
|
74
|
+
*/ function createSafeError(error, context) {
|
|
75
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
76
|
+
return createSafeError$1(err, context);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Wrap an async function with automatic error handling.
|
|
80
|
+
*
|
|
81
|
+
* @param fn - The async function to wrap
|
|
82
|
+
* @param options - Error handling options
|
|
83
|
+
* @returns Wrapped function that sanitizes errors
|
|
84
|
+
*/ function withErrorHandling(fn, options) {
|
|
85
|
+
return withErrorHandling$1(fn, options);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Handle an error by logging internally and throwing a sanitized version.
|
|
89
|
+
*
|
|
90
|
+
* @param error - The error that occurred
|
|
91
|
+
* @param context - Optional context for debugging
|
|
92
|
+
* @param logger - Optional logger for internal logging
|
|
93
|
+
*/ function handleError(error, context, logger) {
|
|
94
|
+
const { external, internal } = sanitize(error, context);
|
|
95
|
+
// Log internal details
|
|
96
|
+
if (logger) {
|
|
97
|
+
logger.error('Error occurred', {
|
|
98
|
+
correlationId: internal.correlationId,
|
|
99
|
+
message: internal.originalMessage,
|
|
100
|
+
context: internal.context,
|
|
101
|
+
timestamp: internal.timestamp
|
|
102
|
+
});
|
|
103
|
+
} else if (process.env.NODE_ENV !== 'production') {
|
|
104
|
+
// Fallback to console in development
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
console.error('Error occurred', {
|
|
107
|
+
correlationId: internal.correlationId,
|
|
108
|
+
message: internal.originalMessage,
|
|
109
|
+
context: internal.context
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Throw sanitized error
|
|
113
|
+
const safeError = new Error(external.message);
|
|
114
|
+
safeError.correlationId = external.correlationId;
|
|
115
|
+
throw safeError;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Format an error for display to users.
|
|
119
|
+
* Returns a user-friendly message with optional correlation ID.
|
|
120
|
+
*
|
|
121
|
+
* @param error - The error to format
|
|
122
|
+
* @returns User-friendly error string
|
|
123
|
+
*/ function formatErrorForDisplay(error) {
|
|
124
|
+
const { external } = sanitize(error);
|
|
125
|
+
if (external.correlationId) {
|
|
126
|
+
return `${external.message}\nReference: ${external.correlationId}`;
|
|
127
|
+
}
|
|
128
|
+
return external.message;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { createSafeError, formatErrorForDisplay, handleError, initializeErrorHandling, sanitize, withErrorHandling };
|
|
132
|
+
//# sourceMappingURL=error-handling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handling.js","sources":["../src/error-handling.ts"],"sourcesContent":["/**\n * Error Handling Utilities\n * \n * Provides centralized error sanitization using @theunwalked/spotclean\n * to prevent information disclosure in error messages.\n * \n * @packageDocumentation\n */\n\nimport {\n sanitize as spotcleanSanitize,\n createSafeError as spotcleanCreateSafeError,\n configureErrorSanitizer,\n configurePathSanitizer,\n configureSecretGuard,\n withErrorHandling as spotcleanWithErrorHandling,\n type ErrorSanitizerConfig,\n type SanitizedErrorResult,\n type Logger as SpotcleanLogger,\n type ErrorHandlingOptions,\n} from '@theunwalked/spotclean';\n\n// Re-export spotclean types and functions for convenience\nexport {\n configureErrorSanitizer,\n configurePathSanitizer,\n configureSecretGuard,\n type ErrorSanitizerConfig,\n type SanitizedErrorResult,\n type ErrorHandlingOptions,\n};\n\n/**\n * Initialize error handling with secure defaults.\n * Call this at application startup.\n */\nexport function initializeErrorHandling(options: {\n basePaths?: string[];\n environment?: 'production' | 'development' | 'test';\n} = {}): void {\n const isProduction = options.environment === 'production' || \n process.env.NODE_ENV === 'production';\n\n // Configure the error sanitizer\n configureErrorSanitizer({\n enabled: true,\n environment: isProduction ? 'production' : 'development',\n includeCorrelationId: true,\n sanitizeStackTraces: isProduction,\n maxMessageLength: 500,\n });\n\n // Configure path sanitization\n configurePathSanitizer({\n enabled: true,\n basePaths: options.basePaths || [process.cwd()],\n redactSystemPaths: isProduction,\n });\n\n // Configure secret guard with kjerneverk-specific patterns\n configureSecretGuard({\n enabled: true,\n redactionText: '[REDACTED]',\n preservePartial: false,\n preserveLength: 0,\n customPatterns: [\n // OpenAI API keys\n { name: 'openai', pattern: /sk-[a-zA-Z0-9]{20,}/g, description: 'OpenAI API key' },\n { name: 'openai-proj', pattern: /sk-proj-[a-zA-Z0-9_-]+/g, description: 'OpenAI project key' },\n // Anthropic API keys\n { name: 'anthropic', pattern: /sk-ant-[a-zA-Z0-9_-]+/g, description: 'Anthropic API key' },\n // Gemini API keys\n { name: 'gemini', pattern: /AIza[a-zA-Z0-9_-]{35}/g, description: 'Google Gemini API key' },\n ],\n });\n}\n\n/**\n * Sanitize an error for safe external exposure.\n * \n * @param error - The error to sanitize\n * @param context - Optional context for debugging\n * @returns Sanitized error result with external and internal details\n */\nexport function sanitize(error: unknown, context?: Record<string, unknown>): SanitizedErrorResult {\n const err = error instanceof Error ? error : new Error(String(error));\n return spotcleanSanitize(err, context);\n}\n\n/**\n * Create a safe error that can be thrown externally.\n * \n * @param error - The error to make safe\n * @param context - Optional context for debugging\n * @returns New Error with sanitized message\n */\nexport function createSafeError(error: unknown, context?: Record<string, unknown>): Error & { correlationId?: string } {\n const err = error instanceof Error ? error : new Error(String(error));\n return spotcleanCreateSafeError(err, context);\n}\n\n/**\n * Wrap an async function with automatic error handling.\n * \n * @param fn - The async function to wrap\n * @param options - Error handling options\n * @returns Wrapped function that sanitizes errors\n */\nexport function withErrorHandling<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n options?: ErrorHandlingOptions\n): T {\n return spotcleanWithErrorHandling(fn, options);\n}\n\n/**\n * Handle an error by logging internally and throwing a sanitized version.\n * \n * @param error - The error that occurred\n * @param context - Optional context for debugging\n * @param logger - Optional logger for internal logging\n */\nexport function handleError(\n error: unknown,\n context?: Record<string, unknown>,\n logger?: SpotcleanLogger\n): never {\n const { external, internal } = sanitize(error, context);\n\n // Log internal details\n if (logger) {\n logger.error('Error occurred', {\n correlationId: internal.correlationId,\n message: internal.originalMessage,\n context: internal.context,\n timestamp: internal.timestamp,\n });\n } else if (process.env.NODE_ENV !== 'production') {\n // Fallback to console in development\n // eslint-disable-next-line no-console\n console.error('Error occurred', {\n correlationId: internal.correlationId,\n message: internal.originalMessage,\n context: internal.context,\n });\n }\n\n // Throw sanitized error\n const safeError = new Error(external.message) as Error & { correlationId?: string };\n safeError.correlationId = external.correlationId;\n throw safeError;\n}\n\n/**\n * Format an error for display to users.\n * Returns a user-friendly message with optional correlation ID.\n * \n * @param error - The error to format\n * @returns User-friendly error string\n */\nexport function formatErrorForDisplay(error: unknown): string {\n const { external } = sanitize(error);\n \n if (external.correlationId) {\n return `${external.message}\\nReference: ${external.correlationId}`;\n }\n \n return external.message;\n}\n\n"],"names":["initializeErrorHandling","options","isProduction","environment","process","env","NODE_ENV","configureErrorSanitizer","enabled","includeCorrelationId","sanitizeStackTraces","maxMessageLength","configurePathSanitizer","basePaths","cwd","redactSystemPaths","configureSecretGuard","redactionText","preservePartial","preserveLength","customPatterns","name","pattern","description","sanitize","error","context","err","Error","String","spotcleanSanitize","createSafeError","spotcleanCreateSafeError","withErrorHandling","fn","spotcleanWithErrorHandling","handleError","logger","external","internal","correlationId","message","originalMessage","timestamp","console","safeError","formatErrorForDisplay"],"mappings":"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"}
|
package/dist/formatter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { getPersonaRole, createRequest } from './chat.js';
|
|
3
3
|
import { DEFAULT_FORMAT_OPTIONS } from './constants.js';
|
|
4
4
|
import { DEFAULT_LOGGER, wrapLogger } from './logger.js';
|
|
5
5
|
import { clean, stringifyJSON } from './util/general.js';
|
|
@@ -516,5 +516,5 @@ function _define_property(obj, key, value) {
|
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
export { IterationStrategyFactory, StrategyExecutor
|
|
519
|
+
export { IterationStrategyFactory, StrategyExecutor };
|
|
520
520
|
//# sourceMappingURL=iteration-strategy.js.map
|
package/dist/loader.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path__default from 'path';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { SafeRegex } from '@theunwalked/pressurelid';
|
|
3
4
|
import { DEFAULT_IGNORE_PATTERNS } from './constants.js';
|
|
4
5
|
import { ParametersSchema } from './items/parameters.js';
|
|
5
6
|
import { create as create$2, SectionOptionsSchema } from './items/section.js';
|
|
@@ -12,10 +13,16 @@ import './builder.js';
|
|
|
12
13
|
import './recipes.js';
|
|
13
14
|
import './conversation.js';
|
|
14
15
|
import 'tiktoken';
|
|
16
|
+
import './logging-config.js';
|
|
15
17
|
import './tools.js';
|
|
16
18
|
import 'openai';
|
|
17
19
|
import '@anthropic-ai/sdk';
|
|
18
20
|
import '@google/generative-ai';
|
|
21
|
+
import './security/types.js';
|
|
22
|
+
import './security/defaults.js';
|
|
23
|
+
import { getAuditLogger } from './security/audit-logger.js';
|
|
24
|
+
import './security/serialization-schemas.js';
|
|
25
|
+
import { createSafeError } from './error-handling.js';
|
|
19
26
|
import { create as create$1 } from './util/storage.js';
|
|
20
27
|
|
|
21
28
|
const OptionsSchema = z.object({
|
|
@@ -23,6 +30,52 @@ const OptionsSchema = z.object({
|
|
|
23
30
|
ignorePatterns: z.array(z.string()).optional().default(DEFAULT_IGNORE_PATTERNS),
|
|
24
31
|
parameters: ParametersSchema.optional().default({})
|
|
25
32
|
});
|
|
33
|
+
/**
|
|
34
|
+
* Create safe regex patterns from ignore patterns using pressurelid
|
|
35
|
+
* to prevent ReDoS attacks from malicious patterns.
|
|
36
|
+
*
|
|
37
|
+
* @param patterns - Array of ignore patterns (can be regex or glob-like)
|
|
38
|
+
* @param logger - Logger instance for warnings
|
|
39
|
+
* @returns Array of safe RegExp objects
|
|
40
|
+
*/ function createSafeIgnorePatterns(patterns, logger) {
|
|
41
|
+
const auditLogger = getAuditLogger();
|
|
42
|
+
// Create SafeRegex instance with security callbacks
|
|
43
|
+
const safeRegex = new SafeRegex({
|
|
44
|
+
maxLength: 500,
|
|
45
|
+
timeoutMs: 1000,
|
|
46
|
+
onBlock: (message, pattern)=>{
|
|
47
|
+
logger.warn(`Blocked unsafe ignore pattern: ${message}`, {
|
|
48
|
+
patternLength: pattern.length
|
|
49
|
+
});
|
|
50
|
+
auditLogger.log({
|
|
51
|
+
type: 'regex_blocked',
|
|
52
|
+
severity: 'warning',
|
|
53
|
+
message: `Blocked unsafe regex pattern: ${message}`,
|
|
54
|
+
context: {
|
|
55
|
+
patternLength: pattern.length
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
onWarning: (message, _pattern)=>{
|
|
60
|
+
logger.debug(`Regex warning: ${message}`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return patterns.map((pattern)=>{
|
|
64
|
+
// Try to create a safe regex from the pattern
|
|
65
|
+
const result = safeRegex.create(pattern, 'i');
|
|
66
|
+
if (result.safe && result.regex) {
|
|
67
|
+
return result.regex;
|
|
68
|
+
}
|
|
69
|
+
// If direct regex fails, try as glob pattern
|
|
70
|
+
const globResult = safeRegex.globToRegex(pattern);
|
|
71
|
+
if (globResult.safe && globResult.regex) {
|
|
72
|
+
return globResult.regex;
|
|
73
|
+
}
|
|
74
|
+
// Log the failure and return a pattern that matches nothing
|
|
75
|
+
logger.warn(`Invalid or unsafe ignore pattern "${pattern}": ${result.error || globResult.error}`);
|
|
76
|
+
return null;
|
|
77
|
+
}).filter((regex)=>regex !== null);
|
|
78
|
+
}
|
|
26
79
|
/**
|
|
27
80
|
* Extracts the first header from Markdown text
|
|
28
81
|
* @param markdownText The Markdown text to parse
|
|
@@ -118,17 +171,7 @@ const create = (loaderOptions)=>{
|
|
|
118
171
|
}
|
|
119
172
|
// Get all other files in the directory
|
|
120
173
|
const files = await storage.listFiles(contextDir);
|
|
121
|
-
const ignorePatternsRegex = ignorePatterns
|
|
122
|
-
try {
|
|
123
|
-
return new RegExp(pattern, 'i');
|
|
124
|
-
} catch (error) {
|
|
125
|
-
logger.error(`Invalid ignore pattern: ${pattern}`, {
|
|
126
|
-
error
|
|
127
|
-
});
|
|
128
|
-
// Return a pattern that matches nothing
|
|
129
|
-
return /(?!)/; // Negative lookahead that always fails
|
|
130
|
-
}
|
|
131
|
-
});
|
|
174
|
+
const ignorePatternsRegex = createSafeIgnorePatterns(ignorePatterns, logger);
|
|
132
175
|
const filteredFiles = files.filter((file)=>{
|
|
133
176
|
const fullPath = path__default.join(contextDir, file);
|
|
134
177
|
// Test against both filename and full path for flexibility
|
|
@@ -169,7 +212,12 @@ const create = (loaderOptions)=>{
|
|
|
169
212
|
}
|
|
170
213
|
contextSections.push(mainContextSection);
|
|
171
214
|
} catch (error) {
|
|
172
|
-
|
|
215
|
+
// Create a safe error that doesn't leak path information
|
|
216
|
+
const safeError = createSafeError(error, {
|
|
217
|
+
operation: 'load',
|
|
218
|
+
directory: path__default.basename(contextDir)
|
|
219
|
+
});
|
|
220
|
+
logger.error(`Error processing context directory: ${safeError.message}`);
|
|
173
221
|
}
|
|
174
222
|
}
|
|
175
223
|
return contextSections;
|