diff options
| author | Aiden Cline <[email protected]> | 2026-01-06 13:30:31 -0600 |
|---|---|---|
| committer | Aiden Cline <[email protected]> | 2026-01-06 13:30:39 -0600 |
| commit | 5db78f20e9ae83c9708b9b0a490f10659e3ce229 (patch) | |
| tree | adaff901d12717045ef7b332e1e989c12e272e9e | |
| parent | 5fc4472921f216318c5fc28d9391f93a7c936955 (diff) | |
| download | opencode-5db78f20e9ae83c9708b9b0a490f10659e3ce229.tar.gz opencode-5db78f20e9ae83c9708b9b0a490f10659e3ce229.zip | |
core: fix title generation for subtask-only messages to extract actual user prompts instead of generic tool execution descriptions
| -rw-r--r-- | packages/opencode/src/session/prompt.ts | 43 |
1 files changed, 22 insertions, 21 deletions
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 1f5fc9884..b669e9567 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -306,7 +306,6 @@ export namespace SessionPrompt { session, modelID: lastUser.model.modelID, providerID: lastUser.model.providerID, - message: msgs.find((m) => m.info.role === "user")!, history: msgs, }) @@ -1565,22 +1564,39 @@ export namespace SessionPrompt { async function ensureTitle(input: { session: Session.Info - message: MessageV2.WithParts history: MessageV2.WithParts[] providerID: string modelID: string }) { if (input.session.parentID) return if (!Session.isDefaultTitle(input.session.title)) return + + // Find first non-synthetic user message + const firstRealUserIdx = input.history.findIndex( + (m) => m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic), + ) + if (firstRealUserIdx === -1) return + const isFirst = input.history.filter((m) => m.info.role === "user" && !m.parts.every((p) => "synthetic" in p && p.synthetic)) .length === 1 if (!isFirst) return + + // Gather all messages up to and including the first real user message for context + // This includes any shell/subtask executions that preceded the user's first prompt + const contextMessages = input.history.slice(0, firstRealUserIdx + 1) + const firstRealUser = contextMessages[firstRealUserIdx] + + // For subtask-only messages (from command invocations), extract the prompt directly + // since toModelMessage converts subtask parts to generic "The following tool was executed by the user" + const subtaskParts = firstRealUser.parts.filter((p) => p.type === "subtask") as MessageV2.SubtaskPart[] + const hasOnlySubtaskParts = subtaskParts.length > 0 && firstRealUser.parts.every((p) => p.type === "subtask") + const agent = await Agent.get("title") if (!agent) return const result = await LLM.stream({ agent, - user: input.message.info as MessageV2.User, + user: firstRealUser.info as MessageV2.User, system: [], small: true, tools: {}, @@ -1598,24 +1614,9 @@ export namespace SessionPrompt { role: "user", content: "Generate a title for this conversation:\n", }, - ...MessageV2.toModelMessage([ - { - info: { - id: Identifier.ascending("message"), - role: "user", - sessionID: input.session.id, - time: { - created: Date.now(), - }, - agent: input.message.info.role === "user" ? input.message.info.agent : await Agent.defaultAgent(), - model: { - providerID: input.providerID, - modelID: input.modelID, - }, - }, - parts: input.message.parts, - }, - ]), + ...(hasOnlySubtaskParts + ? [{ role: "user" as const, content: subtaskParts.map((p) => p.prompt).join("\n") }] + : MessageV2.toModelMessage(contextMessages)), ], }) const text = await result.text.catch((err) => log.error("failed to generate title", { error: err })) |
