diff --git a/.changeset/skill-first-message-title.md b/.changeset/skill-first-message-title.md new file mode 100644 index 0000000000..e04defe911 --- /dev/null +++ b/.changeset/skill-first-message-title.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +web: Fix the session title not being generated when the first message is a skill slash command. diff --git a/packages/kap-server/src/routes/skills.ts b/packages/kap-server/src/routes/skills.ts index f52a36015f..fcaf578567 100644 --- a/packages/kap-server/src/routes/skills.ts +++ b/packages/kap-server/src/routes/skills.ts @@ -42,6 +42,9 @@ * `skill_activation` origin. The returned `Turn` handle is * discarded; clients follow progress via the `skill.activated` * + `turn.*` events emitted by the service on the WS stream. + * The edge then applies the prompt-metadata update + * (`applyPromptMetadataUpdate`) so a first `/` + * message titles the session, matching the native RPC path. * * **Model projection**: `SkillDefinition` (v2) → protocol `SkillDescriptor`, * byte-for-byte with v1's `toProtocolSkill` @@ -72,9 +75,11 @@ import { IAgentSkillService, IBootstrapService, IConfigService, + IEventService, IPluginService, ISessionIndex, ISessionLifecycleService, + ISessionMetadata, ISessionSkillCatalog, ISkillCatalogRuntimeOptions, ISkillDiscovery, @@ -83,8 +88,10 @@ import { isError2, MERGE_ALL_AVAILABLE_SKILLS_SECTION, SKILL_SOURCE_PRIORITY, + applyPromptMetadataUpdate, configuredRoots, projectRoots, + promptMetadataTextFromSkill, userRoots, type ISessionScopeHandle, type Scope, @@ -284,6 +291,16 @@ export function registerSkillsRoutes(app: SkillsRouteHost, core: Scope): void { await agent.accessor .get(IAgentSkillService) .activate({ name: parsed.id, args: req.body.args }); + // Keep the easy-title behavior of the native RPC / TUI path: a first + // `/` message titles the session (same as routes/prompts.ts). + await applyPromptMetadataUpdate( + { + metadata: resolved.handle.accessor.get(ISessionMetadata), + eventService: core.accessor.get(IEventService), + sessionId: session_id, + }, + promptMetadataTextFromSkill({ name: parsed.id, args: req.body.args }), + ); requestLog(req)?.info({ session_id, skill_name: parsed.id }, 'skill activated'); reply.send(okEnvelope({ activated: true, skill_name: parsed.id }, req.id)); } catch (err) { diff --git a/packages/kap-server/test/skills.test.ts b/packages/kap-server/test/skills.test.ts index 0ace87c21b..71e1056a43 100644 --- a/packages/kap-server/test/skills.test.ts +++ b/packages/kap-server/test/skills.test.ts @@ -217,6 +217,21 @@ describe('server-v2 /api/v1 skills', () => { }); }); + it('derives the session title from the first skill activation', async () => { + const id = await createSession(); + await createMainAgent(id); + + const activated = await postJson<{ activated: boolean; skill_name: string }>( + `/api/v1/sessions/${id}/skills/update-config:activate`, + { args: '--help' }, + ); + expect(activated.body.code).toBe(0); + + const got = await getJson<{ title: string }>(`/api/v1/sessions/${id}`); + expect(got.body.code).toBe(0); + expect(got.body.data.title).toBe('/update-config --help'); + }); + it('returns 40415 for an unknown skill', async () => { const id = await createSession(); await createMainAgent(id);