Skip to content

fix(agent-core): keep dotted file names readable in session titles - #2872

Open
sailist wants to merge 13 commits into
MoonshotAI:mainfrom
sailist:fix/session-title-redact-filename
Open

fix(agent-core): keep dotted file names readable in session titles#2872
sailist wants to merge 13 commits into
MoonshotAI:mainfrom
sailist:fix/session-title-redact-filename

Conversation

@sailist

@sailist sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below.

Problem

Session titles and lastPrompt metadata are sanitized to avoid leaking secrets pasted into prompts. The catch-all "long token" patterns (the sk-… key pattern and the 40+ character base64/base64url pattern) also match ordinary file names, because their character classes include - and _. A prompt like "帮我看看 refact-000-08-12-external-hooks-feature-scopes.ts" ends up titled "帮我看看 [redacted].ts", and even names like sk-project-notes-2024.md are redacted — which makes session list titles useless for exactly the kind of prompts developers write most.

What changed

  • Added a file-extension-aware negative lookahead to the two token patterns in the prompt-metadata sanitizers (both agent-core and the parallel agent-core-v2 port): a match followed by . + a short (1–8 char) alphanumeric extension is treated as a file name and kept readable. The extension length cap plus a trailing alphanumeric check ensures dot-joined secrets such as JWT segments (payload.signature) are still redacted.
  • Behavior that stays redacted on purpose: bare long tokens, real sk-… keys, and 40-char hex strings such as git SHAs (indistinguishable from tokens without context).
  • Added tests in both packages pinning all of the above, plus a patch changeset for the CLI.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e023c6a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 13, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@3210caa
npx https://pkg.pr.new/@moonshot-ai/kimi-code@3210caa

commit: 3210caa

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ef1f84adc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +42 to +46
/\bsk-[A-Za-z0-9_-]{12,}\b(?![A-Za-z0-9_-]*\.[A-Za-z0-9]{1,8}(?![A-Za-z0-9_]))/g,
'[redacted]',
)
.replaceAll(
/\b[A-Za-z0-9][A-Za-z0-9+/=_-]{39,}\b(?![A-Za-z0-9+/=_-]*\.[A-Za-z0-9]{1,8}(?![A-Za-z0-9_]))/g,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not exempt extension-suffixed tokens from redaction

When a prompt contains a token-looking path segment such as sk-abcdefghijklmnop1234.env or a 40+ character access token followed by .json, these new negative lookaheads classify it as a filename and skip both catch-all redactions, so the full value is persisted in lastPrompt/title and shown in session lists. Since this sanitizer is the metadata privacy boundary, keep known secret/token-shaped strings redacted even when a short extension follows, or require a safer filename context and mirror the fix in the v1 copy.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9454699d0e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


function isFileNameStem(stem: string, following: string): boolean {
if (!/[-_]/.test(stem)) return false;
const extension = /^\.([A-Za-z0-9]{1,8})(?![A-Za-z0-9_])/.exec(following)?.[1];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not accept hyphenated dot segments as extensions

When a dotted token/JWT segment starts with an allow-listed extension followed by -, this lookahead treats it as a filename extension because - is not excluded. Fresh evidence in this revision is that a value like <40+ chars with _>.ts-<more token chars> now leaves the first long segment visible in lastPrompt/title while only redacting the second segment, even though the new tests intend dot-joined JWT-style tokens to stay redacted. Require a real filename boundary here, and mirror the fix in the legacy sanitizer copy.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40d2097b61

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


function isFileNameStem(stem: string, following: string): boolean {
if (!/[-_]/.test(stem)) return false;
const extension = /^\.([A-Za-z0-9]{1,8})(?![A-Za-z0-9+/=_-])/.exec(following)?.[1];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle compound TypeScript filename suffixes

When the long slug uses common compound code suffixes such as .test.ts, .spec.ts, or .d.ts, this helper only evaluates the first dotted component (test, spec, or d) as the extension. Those are not in the allow-list, so prompts like refact-000-08-12-external-hooks-feature-scopes.test.ts still become [redacted].test.ts, leaving session titles unreadable for common test/declaration files; consider recognizing safe compound suffixes or checking the final code extension in both sanitizer copies.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7205bd2e21

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +63 to +67
if (!/[-_]/.test(stem)) return false;
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());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail closed on safe-suffix token stems

When an unlabeled long base64/base64url token contains - or _ and is followed by an allow-listed code suffix (for example a signed URL path segment like <40+ chars>_.ts?download=1), this helper classifies it as a filename and returns the raw match, so the secret still lands in lastPrompt/title; the same logic is copied in packages/agent-core/src/session/prompt-metadata.ts. Fresh evidence after the earlier comments is that the current allow-list fixes .json/.env cases but still exempts .ts/.js/other code suffixes, so ambiguous token-shaped stems should fail closed or require stronger human-filename context.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 3210caa8c6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3ba50f7e1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

function isAbsolutePath(match: string, offset: number, source: string): boolean {
if (offset === 0 || source[offset - 1] !== '/') return false;
if (!match.includes('/')) return false;
return match.split('/').every((segment) => segment.length < 40);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact slash-delimited tokens before preserving paths

When the pasted secret is a base64/base64url-looking value that begins with / and contains another / before any segment reaches 40 chars, this absolute-path exception returns true and the replacement keeps the full token, e.g. /Ab1c…/Z9x8… remains visible in lastPrompt/titles. The catch-all regex previously redacted these slash-containing tokens, so require stronger local-path context or fail closed for slash-delimited token-shaped values in both sanitizer copies.

Useful? React with 👍 / 👎.

});

it('keeps absolute paths readable but redacts token-looking segments', () => {
expect(sanitize('cd /Users/moonshot/Projects/kimi-code-workspace/')).toBe(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Replace internal names in prompt fixtures

These new absolute-path fixtures add moonshot as a username in public test data, while the repository guide asks public text and test data to use neutral placeholders instead. Please replace the user/path with neutral values in both prompt-metadata test copies so the tests do not introduce context-specific internal identifiers.

AGENTS.md reference: AGENTS.md:L80-L80

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 317e0a838b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

]);

function isFileNameStem(stem: string, following: string): boolean {
if (!/^(?=.*[-_])[a-z0-9_/-]+$/.test(stem)) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve long file names after path prefixes

For prompts that mention a long slug file under an absolute path with capitalized directories, such as /Users/alice/Projects/.../refact-000-08-12-external-hooks-feature-scopes.ts, the catch-all match includes the directory prefix, so this all-lowercase stem check fails before looking at the safe .ts suffix; isAbsolutePath then also rejects the match because the basename segment is at least 40 chars, and the title becomes /[redacted].ts. Since this is the main filename shape the patch is trying to keep readable, evaluate the basename separately, and mirror the fix in the legacy sanitizer.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87c3e84b80

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

]);

function isFileNameStem(stem: string, following: string): boolean {
if (!/^(?=.*[-_])[a-z0-9_/-]+$/.test(stem)) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact token-like path segments before preserving filenames

When a long lowercase token appears as a path segment before a safe hyphenated filename, this check treats the whole token/refact-... prefix as a filename stem because / is allowed and the - can come from the basename. For example, open ${'a'.repeat(44)}/refact-000-08-12-external-hooks-feature-scopes.ts is kept verbatim in lastPrompt/titles even though the same 40+ token would be redacted when bare; the copied v1 helper has the same behavior. Validate only the basename as the readable filename (and fail closed on token-length path segments), or remove path prefixes from this filename-stem exemption.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12f7bfd906

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const directories = segments.slice(0, -1);
const base = segments[segments.length - 1];
if (!directories.every(isWordShapedSegment)) return false;
if (isWordShapedSegment(base)) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact lowercase slash-delimited tokens before preserving paths

Fresh evidence beyond the earlier mixed-case slash-token comment is that all-lowercase or hex slash-delimited tokens still take this branch: a prompt like token ${'a'.repeat(32)}/${'b'.repeat(32)} is preserved verbatim in lastPrompt/title because both segments are under 40 chars and match isWordShapedSegment, even though the same token material without / is redacted. For token IDs split across URL/path segments and pasted without :=, require stronger local-path context or fail closed for extensionless multi-segment matches; the legacy packages/agent-core/src/session/prompt-metadata.ts copy has the same issue.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 735b853452

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +85 to +86
function isWordShapedSegment(segment: string): boolean {
return segment.length < 40 && /^([A-Z]?[a-z0-9_-]*|[A-Z0-9_-]+)$/.test(segment);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve camelCase file paths

When the prompt mentions a long path whose basename is camelCase or PascalCase, such as packages/agent-core-v2/src/agent/prompt/promptMetadataText.ts, isPathLike() rejects it here because the segment regex only accepts lowercase, one initial capital, or all-caps words. The catch-all token regex then still collapses the whole path to [redacted].ts, so titles remain unreadable for common TypeScript files in this repo; allow normal code filename casing or decide path basenames from their safe extension before this word-shape filter. The legacy copy in packages/agent-core/src/session/prompt-metadata.ts has the same behavior.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea4623ae9d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return true;
}
if (!isWordShapedSegment(base)) return false;
return segments.length >= 3 && segments.every((segment) => segment.length <= 24);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Redact short multi-segment tokens before path preservation

Fresh evidence in this revision is that the new extensionless path exemption preserves any 3+ word-shaped segments of at most 24 characters, so a pasted token split like aaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbb/cccccccccccccccccccc is kept verbatim in lastPrompt/session titles instead of being redacted by the catch-all pattern. This still affects the v1 copy as well, and because these metadata fields are displayed in session lists, slash-delimited opaque IDs should fail closed unless there is stronger local-path context.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9dcd7aacbe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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].toLowerCase()) || source[offset - 2] === '~';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail closed on API-shaped rooted token paths

Fresh evidence in this revision is that the new root allow-list still preserves extensionless matches whose first segment is also a common API route, so a pasted value like /users/${'a'.repeat(20)}/${'b'.repeat(20)}/${'c'.repeat(20)} or /data/... is kept verbatim in lastPrompt/session titles instead of being redacted. Since these metadata fields are the privacy boundary and slash-delimited opaque IDs often appear under /users or /data, require stronger filesystem context than just the first segment name; the copied v1 helper has the same behavior.

Useful? React with 👍 / 👎.

@sailist

sailist commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e023c6a9ce

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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] === '~';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail closed for rooted slash-delimited tokens

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 with tmp allow-listed and both opaque segments under the 24-char cap, so the catch-all leaves the full value in lastPrompt/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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant