(text: string)
| 124 | * Validate output before sending to Slack |
| 125 | */ |
| 126 | export function validateOutput(text: string): SanitizationResult { |
| 127 | let flagged = false; |
| 128 | let reason: string | undefined; |
| 129 | |
| 130 | // Check for forbidden patterns |
| 131 | for (const pattern of FORBIDDEN_OUTPUT_PATTERNS) { |
| 132 | if (pattern.test(text)) { |
| 133 | flagged = true; |
| 134 | reason = `Output may contain sensitive content`; |
| 135 | logger.warn({ pattern: pattern.source.substring(0, 30) }, 'Addie: Suspicious output pattern'); |
| 136 | break; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | // Truncate very long outputs (increased to 10000 to support web search responses) |
| 141 | const MAX_OUTPUT_LENGTH = 10000; |
| 142 | let sanitized = text; |
| 143 | if (text.length > MAX_OUTPUT_LENGTH) { |
| 144 | sanitized = text.substring(0, MAX_OUTPUT_LENGTH) + '\n\n_[Response truncated]_'; |
| 145 | if (!flagged) { |
| 146 | flagged = true; |
| 147 | reason = 'Output truncated due to length'; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | // Note: Link format conversion removed - Claude now outputs correct format |
| 152 | // based on channel context (Slack mrkdwn or web markdown) |
| 153 | |
| 154 | return { |
| 155 | valid: !flagged || (reason?.includes('truncated') ?? false), |
| 156 | sanitized, |
| 157 | flagged, |
| 158 | reason, |
| 159 | }; |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Strip bot mention from message text |
no test coverage detected