fix: harden stripToolCallLeaks regex for CJK/non-ASCII garbage tokens

The previous regex used \w+ for descriptor tokens between the function
name and JSON body, which only matches ASCII word characters. Model
hallucinations often inject CJK/non-ASCII text (e.g. 彩神争霸) in this
position. Switch to (?:\s+[^\s{}]+)+ to match any whitespace-separated
non-brace tokens regardless of character set.

Also removes stale backward-compat re-exports from index.ts and updates
the test import for composeDashboardContent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eyejoker
2026-03-25 18:06:31 +09:00
parent 6806001ed6
commit c89242b6dd
4 changed files with 26 additions and 8 deletions

View File

@@ -58,16 +58,20 @@ function redactSecrets(text: string): string {
*
* When a model (especially Codex) enters a degenerate loop, it emits
* tool-call intent as plaintext instead of actual tool calls. The format is:
* to=functions.<name> <type> {<json>}
* to=functions.<name> <arbitrary tokens> {<json>}
* e.g. `to=functions.exec_command code {"cmd":"git status","yield_time_ms":1000}`
*
* The tokens between the function name and JSON body can include non-ASCII
* characters (CJK, etc.) when the model hallucinates. The regex allows one or
* more non-whitespace descriptor tokens before the JSON brace.
*
* This function removes such fragments so they never reach Discord.
*/
export function stripToolCallLeaks(text: string): string {
// Match tool-call serialization: to=functions.<name> <word> {<json>}
// Match tool-call serialization: to=functions.<name> <descriptor tokens> {<json>}
// Handles up to one level of nested braces in the JSON body.
const stripped = text.replace(
/to=functions\.\w+\s+\w+\s+\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g,
/to=functions\.\w+(?:\s+[^\s{}]+)+\s+\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g,
'',
);
// Collapse excessive blank lines left after stripping