fix(sessions): name tasks from pasted text-file contents#2850
Conversation
When a prompt is pasted it is auto-converted to a `pasted-text.txt` attachment with no typed text, so the task title must come from the file's contents. This worked for local tasks (the prompt event carries the `<file .../>` path inline) but not for cloud tasks: by the time the title generator runs, the original local path is gone — the stored description is reduced to `Attached files: <name>`, and the echoed prompt event points at the remote sandbox path (`file:///workspace/.posthog/attachments/...`), which is not readable locally. The only place the local path exists is at submit time, before the file is uploaded as an artifact, so the generator had nothing but the filename and produced "Untitled". Stash the prompt's local attachment paths at creation time (keyed by task id), hand them to the title generator so it reads the file contents, and clear them once a title is produced. Also widen the attachment-summary matcher so the `1. [Attached files: ...]` form (produced when titling from prompts) is treated as "no real text", falling through to the file contents. Typed-text prompts are unaffected — attachments are only read when there is no real text. Generated-By: PostHog Code Task-Id: 931c5770-c4d5-49d7-92d5-4e73b824e75a
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "fix(sessions): name tasks from pasted te..." | Re-trigger Greptile |
| export const useTitleAttachmentStore = create<TitleAttachmentStore>( | ||
| (set, get) => ({ | ||
| byTaskId: {}, | ||
| set: (taskId, filePaths) => | ||
| set((state) => ({ | ||
| byTaskId: { ...state.byTaskId, [taskId]: filePaths }, | ||
| })), | ||
| get: (taskId) => get().byTaskId[taskId], | ||
| clear: (taskId) => | ||
| set((state) => { | ||
| if (!(taskId in state.byTaskId)) return state; | ||
| const { [taskId]: _removed, ...rest } = state.byTaskId; | ||
| return { byTaskId: rest }; | ||
| }), | ||
| }), | ||
| ); |
There was a problem hiding this comment.
useTitleAttachmentStore is exported but never imported anywhere in the codebase (confirmed: only appears in its own file). The analogous usePendingTaskPromptStore in pendingTaskPromptStore.ts is at least wrapped by a usePendingTaskPrompt helper that consumes it; there is no equivalent usage here. Per the simplicity rule of no superfluous parts, this export can be dropped — titleAttachmentStoreApi is sufficient for all current call sites.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| const result = await titleGenerator.generateTitleAndSummary(content); | ||
| if (result) { | ||
| // The seed is consumed once a title has been produced; drop it so the | ||
| // map doesn't grow across a long-lived session. | ||
| titleAttachmentStoreApi.clear(taskId); |
There was a problem hiding this comment.
Stash not cleared when LLM returns null
titleAttachmentStoreApi.clear(taskId) is only called inside if (result), so when generateTitleAndSummary returns null (on error or empty output), the stash entry survives. In the shouldGenerateFromTaskDescription path, initialDescriptionHandled is unconditionally set to true in the finally block, which prevents any description-path retry. The stash therefore stays populated for the rest of the session without any mechanism to consume or evict it other than the next prompt-based generation or a reload. This is acknowledged as best-effort, but moving the clear call to the finally block (only for the description path, not if a retry on prompts is still wanted) would make the lifecycle explicit.
Problem
When you paste a prompt into PHCode it gets auto-converted to a
pasted-text.txtattachment with no typed text, so the only signal for naming the task is the file's contents. The name generator wasn't reading them, so the task defaulted to Untitled.Tracing the flow, the breakage is specific to cloud tasks:
<file path="…"/>tag inline, which the title generator reads.Attached files: pasted-text.txt(no path), andfile:///workspace/.posthog/attachments/…), which can't be read on the user's machine.The local path only exists at submit time, before the file is uploaded as a cloud artifact — so the generator had only the filename and produced
Untitled.Fix
titleAttachmentStore.ts(new) — small UI store that stashes the prompt's local attachment paths keyed by task id.useTaskCreation.ts— on task creation, stash those local paths for the new task id.useChatTitleGenerator.ts— read the stashed paths, hand them to the title generator, and clear them once a title is produced.titleGeneratorService.ts— widened theAttached files:matcher so the1. [Attached files: …]form (produced when titling from prompts) is recognized as "no real text", falling through to the file contents.The generator already truncates to 500 chars, skips binary files, and ignores attachments when real text is typed — so normal prompts are unaffected. This also makes the local path more robust (it now gets the explicit path too).
Tests
titleGeneratorService.test.ts— cases for the cloud description form (Attached files: …) and the numbered prompt-list form, plus a guard that typed text still wins over attachments.useChatTitleGenerator.test.ts— a cloud-style case asserting stashed local paths are passed through and cleared after naming; updated existing assertions for the new arg.Verification
Typecheck, Biome lint (incl.
noRestrictedImports), and a fullpnpm buildpass; sessions + task-detail suites green (435 tests). Did not run a live cloud end-to-end (needs auth + cloud backend + a real LLM call) — covered by unit tests instead.Known limitation
If the app reloads in the few seconds between submitting and the title generating, the in-memory stash is lost and the title falls back to the attachment summary (same as other in-memory optimistic state). Can persist it if desired.