fix(session): title user /skill invocations from name and args

Session titles ignored /skill:<name> <args> because the invocation is a
custom skill-prompt, not a user turn. Feed the chip or reconstructed
/skill line into first-title generation and replan context, never the
expanded SKILL.md body.
This commit is contained in:
qiyi71w
2026-08-19 00:59:31 +08:00
parent 8500092296
commit ecd800ef8f
6 changed files with 185 additions and 2 deletions
@@ -308,6 +308,7 @@ import {
sanitizeAssistantForReparentedHistory,
USER_INTERRUPT_LABEL,
} from "./messages";
import { skillPromptTitleInput } from "./skill-title-input";
import { ModelControls, type ModelControlsHost } from "./model-controls";
import { isPrewalkPlanNudge, PrewalkCoordinator, type PrewalkCoordinatorHost } from "./prewalk";
import {
@@ -5466,11 +5467,20 @@ export class AgentSession {
let keywordNotices: CustomMessage[] = [];
if (message.customType === SKILL_PROMPT_MESSAGE_TYPE && message.attribution === "user") {
const details = message.details;
let skillName: string | undefined;
let skillArgs = "";
if (details && typeof details === "object" && "args" in details && typeof details.args === "string") {
skillArgs = details.args;
if (details && typeof details === "object") {
if ("name" in details && typeof details.name === "string") skillName = details.name;
if ("args" in details && typeof details.args === "string") skillArgs = details.args;
}
keywordNotices = this.#createMagicKeywordNotices(skillArgs);
this.maybeStartTitleGeneration(
skillPromptTitleInput({
name: skillName,
args: skillArgs,
queueChipText: options?.queueChipText,
}),
);
}
if (options?.queueOnly) {
@@ -38,6 +38,7 @@ export {
import type { OutputMeta } from "../tools/output-meta";
import { formatOutputNotice } from "../tools/output-meta";
import { titleTextFromSkillPrompt } from "./skill-title-input";
export const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
export const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
@@ -163,6 +164,11 @@ function thinkingFromContent(content: unknown): string {
}
function titleConversationTurnFromMessage(message: AgentMessage): TitleConversationTurn | undefined {
if (message.role === "custom") {
const text = titleTextFromSkillPrompt(message);
if (!text) return undefined;
return { role: "user", text };
}
if (message.role !== "user" && message.role !== "assistant") return undefined;
const text = textFromContent(message.content);
const thinking = message.role === "assistant" ? thinkingFromContent(message.content) : undefined;
@@ -0,0 +1,36 @@
/** Compact title-model input for a user-invoked `/skill:<name>` prompt. */
export function skillPromptTitleInput(input: {
name?: string;
args?: string;
queueChipText?: string;
}): string {
const chip = input.queueChipText?.trim();
if (chip) return chip;
const name = input.name?.trim();
const args = input.args?.trim();
if (name && args) return `/skill:${name} ${args}`;
if (name) return `/skill:${name}`;
return args ?? "";
}
/** Title text for a persisted skill-prompt custom message. Never the expanded SKILL.md body. */
export function titleTextFromSkillPrompt(message: {
role: string;
customType?: string;
attribution?: string;
details?: unknown;
}): string | undefined {
if (message.role !== "custom" || message.customType !== "skill-prompt" || message.attribution !== "user") {
return undefined;
}
let name: string | undefined;
let args: string | undefined;
let queueChipText: string | undefined;
if (message.details && typeof message.details === "object") {
const details = message.details as Record<string, unknown>;
if (typeof details.name === "string") name = details.name;
if (typeof details.args === "string") args = details.args;
if (typeof details.__queueChipText === "string") queueChipText = details.__queueChipText;
}
return skillPromptTitleInput({ name, args, queueChipText }) || undefined;
}