Skip to content

Commit 2edc06f

Browse files
authored
Merge pull request #13 from DefectDojo/harden/disable-session-title-egress
feat: generate session titles locally, without sending an excerpt
2 parents 71f5c61 + 27ef2ba commit 2edc06f

9 files changed

Lines changed: 273 additions & 397 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@moonshot-ai/kimi-code': minor
3+
---
4+
5+
Session titles are generated locally. The `chat_title` request, which sent an
6+
excerpt of the conversation to the managed platform purely to produce a
7+
display string, is no longer made.
8+
9+
Sessions are still titled: the first prompt already supplies a local title,
10+
and that text is run through the secret redactor first.

‎HARDENING.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,25 @@ rationale. Line numbers are from the commit that introduced the note and will dr
343343
**`staticEnableError: REMOTE_CONTROL_DISABLED_MESSAGE,`**
344344

345345
> Upstream sets this only for a non-loopback bind or `--dangerous-bypass-auth`, which leaves `POST /api/v1/remote-control {"enabled":true}` working for a plain `kimi web`: anything holding the local server token could start a public tunnel with no terminal interaction and no second confirmation. Making it unconditional turns that route into a clean refusal.
346+
347+
### `packages/oauth/src/managed-tools.ts`
348+
349+
**`export async function fetchChatTitle(`**
350+
351+
> Session-title generation is disabled in this fork.
352+
>
353+
> `chat_title` posts an excerpt of the conversation, the user's prompt and the assistant's reply, to the managed platform purely to produce a display string for the session list. It is the only call in the product that sends conversation content anywhere other than the configured model provider, so with a third-party model backend it is a second, unrelated destination for the same text.
354+
>
355+
> Upstream gated this behind an `auto_session_title` experimental flag. That flag no longer exists anywhere in the tree, and the bundled web UI fires the request unprompted on the first turn, so there was nothing left to turn off.
356+
>
357+
> Nothing is lost by refusing. `applyPromptMetadataUpdate` already sets a `replaceable` title locally from the first prompt via `titleFromPromptMetadataText`, and that text passes through the secret redactor first. Sessions stay titled; the title is a truncated prompt rather than a generated phrase.
358+
>
359+
> This is the chokepoint: it is the only function that sends `chat_title`, so refusing here also covers any caller a later upstream merge introduces. Upstream's implementation is kept as `fetchChatTitleRemote`, unreachable, so its tests keep running and upstream changes still merge cleanly.
360+
361+
### `packages/agent-core-v2/src/session/sessionTitle/sessionTitleService.ts`
362+
363+
**`private async generateAndApply(`**
364+
365+
> A second gate in front of the network seal, so the disabled path does no provider lookup and never asks the OAuth token provider for an access token. Upstream's body is kept as `generateAndApplyRemote`, unreachable.
366+
>
367+
> `composeTitleInput` is exported so its budget and digest-elision behaviour stays under test as a pure function, rather than being asserted through a request body that is no longer sent.

‎packages/agent-core-v2/src/session/sessionTitle/sessionTitleService.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
OAuthError,
44
fetchChatTitle,
55
kimiCodeToolsUrl,
6+
SESSION_TITLE_EGRESS_DISABLED_MESSAGE,
67
parseKimiCodeCustomHeaders,
78
resolveKimiCodeRuntimeAuth,
89
} from '@moonshot-ai/kimi-code-oauth';
@@ -88,6 +89,14 @@ export class SessionTitleService implements ISessionTitleService {
8889
}
8990

9091
private async generateAndApply(
92+
_chatContent: string,
93+
_force: boolean,
94+
): Promise<string | undefined> {
95+
this.log.debug(SESSION_TITLE_EGRESS_DISABLED_MESSAGE);
96+
return undefined;
97+
}
98+
99+
private async generateAndApplyRemote(
91100
chatContent: string,
92101
force: boolean,
93102
): Promise<string | undefined> {
@@ -166,7 +175,7 @@ function titleInputFromPrompts(prompts: readonly string[]): string | undefined {
166175
.slice(0, MAX_TITLE_INPUT_LENGTH);
167176
}
168177

169-
async function composeTitleInput(
178+
export async function composeTitleInput(
170179
promptSource: IAgentTitlePromptSource,
171180
source: SessionTitleSource,
172181
): Promise<string | undefined> {

0 commit comments

Comments
 (0)