-
Notifications
You must be signed in to change notification settings - Fork 1k
fix(agent-core): keep dotted file names readable in session titles #2872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6ef1f84
9454699
40d2097
7205bd2
3210caa
b3ba50f
317e0a8
87c3e84
12f7bfd
735b853
ea4623a
9dcd7aa
e023c6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Fix session titles redacting long hyphenated or underscored file names as if they were secret tokens. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,15 @@ export function promptMetadataTextFromText(text: string): string | undefined { | |
| '$1=[redacted]', | ||
| ) | ||
| .replaceAll(/\bsk-[A-Za-z0-9_-]{12,}\b/g, '[redacted]') | ||
| .replaceAll(/\b[A-Za-z0-9][A-Za-z0-9+/=_-]{39,}\b/g, '[redacted]') | ||
| .replaceAll( | ||
| /\b[A-Za-z0-9][A-Za-z0-9+/=_-]{39,}\b/g, | ||
| (match: string, offset: number, source: string) => { | ||
| const following = source.slice(offset + match.length); | ||
| return isFileNameStem(match, following) || isPathLike(match, offset, source) | ||
| ? match | ||
| : '[redacted]'; | ||
| }, | ||
| ) | ||
| .replaceAll(/\p{Cc}+/gu, ' ') | ||
| .replaceAll(/\s+/g, ' ') | ||
| .trim(); | ||
|
|
@@ -48,6 +56,51 @@ export function promptMetadataTextFromText(text: string): string | undefined { | |
| return sanitized.slice(0, MAX_LAST_PROMPT_LENGTH); | ||
| } | ||
|
|
||
| const SAFE_FILENAME_EXTENSIONS = new Set([ | ||
| 'md', 'markdown', 'txt', 'ts', 'tsx', 'mts', 'cts', 'js', 'jsx', 'mjs', 'cjs', | ||
| 'py', 'rb', 'go', 'rs', 'java', 'kt', 'swift', 'c', 'h', 'cc', 'cpp', 'hpp', | ||
| 'cs', 'css', 'scss', 'less', 'html', 'vue', 'svelte', 'php', 'sh', 'sql', | ||
| 'graphql', 'proto', 'lua', 'dart', | ||
| ]); | ||
|
|
||
| function isFileNameStem(stem: string, following: string): boolean { | ||
| if (!/^(?=.*[-_])[a-z0-9_-]+$/.test(stem)) return false; | ||
| return safeSuffixFollows(following); | ||
| } | ||
|
|
||
| function safeSuffixFollows(following: string): boolean { | ||
| const suffix = /^((?:\.[A-Za-z0-9]{1,8})+)(?![.A-Za-z0-9+/=_-])/.exec(following)?.[1]; | ||
| if (suffix === undefined) return false; | ||
| const extension = suffix.slice(suffix.lastIndexOf('.') + 1); | ||
| return SAFE_FILENAME_EXTENSIONS.has(extension.toLowerCase()); | ||
| } | ||
|
|
||
| function isPathLike(match: string, offset: number, source: string): boolean { | ||
| if (!match.includes('/')) return false; | ||
| const segments = match.split('/'); | ||
| const directories = segments.slice(0, -1); | ||
| const base = segments[segments.length - 1]; | ||
| if (!directories.every(isWordShapedSegment)) return false; | ||
| const following = source.slice(offset + match.length); | ||
| if (isFileNameStem(base, following)) return true; | ||
| if (base.length < 40 && /^[A-Za-z][A-Za-z0-9_-]*$/.test(base) && safeSuffixFollows(following)) { | ||
| return true; | ||
| } | ||
| if (!isWordShapedSegment(base)) return false; | ||
| if (segments.length < 3 || !segments.every((segment) => segment.length <= 24)) return false; | ||
| if (offset === 0 || source[offset - 1] !== '/') return false; | ||
| return PATH_ROOT_SEGMENTS.has(segments[0]) || source[offset - 2] === '~'; | ||
| } | ||
|
|
||
| const PATH_ROOT_SEGMENTS = new Set([ | ||
| 'Users', 'Volumes', 'home', 'tmp', 'var', 'opt', 'usr', 'etc', 'root', 'mnt', | ||
| 'media', 'srv', | ||
| ]); | ||
|
|
||
| function isWordShapedSegment(segment: string): boolean { | ||
| return segment.length < 40 && /^([A-Z]?[a-z0-9_-]*|[A-Z0-9_-]+)$/.test(segment); | ||
|
Comment on lines
+100
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the prompt mentions a long path whose basename is camelCase or PascalCase, such as Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| function promptPartText(part: ContentPart): string | undefined { | ||
| switch (part.type) { | ||
| case 'text': { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fresh evidence in this revision is that the narrowed root allow-list still preserves lowercase/hex token chunks when they sit under an allowed filesystem root:
secret /tmp/${'a'.repeat(20)}/${'b'.repeat(20)}reaches this return withtmpallow-listed and both opaque segments under the 24-char cap, so the catch-all leaves the full value inlastPrompt/session titles instead of redacting it. Require stronger local filesystem context than root + short word-shaped segments, or fail closed for repeated opaque segments here and in the copied v1 helper.Useful? React with 👍 / 👎.