feat(hooks): use first user prompt as fallback session/agent descriptor#205
feat(hooks): use first user prompt as fallback session/agent descriptor#205hoangsonww wants to merge 2 commits into
Conversation
Sessions that never receive a custom-title or ai-title (and sessions imported from existing JSONL transcripts) showed up as "Session <id8>" and "Main Agent - Session <id8>" forever, making it hard to tell what each session is doing at a glance (issue #201). TranscriptCache now captures the first real user message of a transcript (tool-result, meta/caveat, slash-command plumbing, compact-summary, and interrupt entries skipped; whitespace-collapsed; capped at 500 chars) and surfaces it as `firstUserMessage` alongside the existing extract fields. First value wins across incremental reads, unlike the last-wins titles. The hook ingestor applies it via an idempotent helper (applyFirstUserDescriptor) that fills sessions.name and the main agent's name/task ONLY while those fields still hold auto-generated placeholders. It runs strictly after syncSessionName on every transcript-bearing event (live on UserPromptSubmit, backfill on any later event) and in the 15 s watchdog. Precedence is preserved: custom-title always wins, ai-title fills placeholders — and can still replace a descriptor-filled name later — and the descriptor only fills what remains. The main-agent fill passes the existing current_tool through verbatim (the shared updateAgent statement writes that column without COALESCE), so an in-flight tool is never wiped mid-turn. import-history captures the same descriptor (shared extractFirstUserText) so newly imported sessions are named from their first prompt when no title exists, their main agent gets the prompt as its task, and the re-import backfill pass renames placeholder rows from earlier imports — dead imported sessions never fire hooks, so this is their only path to a meaningful label. User-picked names are never clobbered anywhere. Closes #201 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeseRLX17wL3iTqc1trq
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
Code Review
This pull request implements a fallback descriptor mechanism using the session's first user prompt (addressing issue #201). When neither a custom nor an AI-generated title is available, the system extracts, normalizes, and truncates the first real user prompt to fill placeholder session names, main agent names, and agent tasks. This behavior is integrated into the live hook ingestor, the 15-second watchdog, and the batch history importer (covering both new imports and re-import backfills). The changes are supported by comprehensive unit tests in a new test file, along with updated documentation across several markdown files. There are no review comments to address, and the implementation is clean and well-tested.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Adds a new “fallback descriptor” for sessions (and their main agent) based on the first real user prompt when no custom-title / ai-title is ever present—improving readability for long-running untitled sessions and imported transcripts while preserving existing title precedence.
Changes:
- Extend
TranscriptCacheto extract and persistfirstUserMessage(first-wins, whitespace-collapsed, capped) and expose a shared extraction helper for reuse by imports. - Apply the descriptor in hook ingestion + watchdog to fill placeholder-only
sessions.nameand main agentname/task, without clobbering explicit names/titles and without wipingcurrent_tool. - Update importer naming/backfill behavior and add a comprehensive server test suite + documentation updates describing the new precedence.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| server/routes/hooks.js | Applies the first-user-prompt descriptor as a placeholder-only fallback (after title sync) and in the watchdog; preserves current_tool. |
| server/lib/transcript-cache.js | Extracts firstUserMessage (first-wins) and exports extractFirstUserText for shared reuse. |
| scripts/import-history.js | Uses the same extracted descriptor for imported sessions’ fallback naming and backfills prior placeholder rows. |
| server/tests/first-user-descriptor.test.js | Adds coverage for extraction rules, hook behavior/precedence/idempotency, current_tool preservation, and import behavior. |
| README.md | Documents new naming precedence and descriptor behavior in user-facing feature list. |
| server/README.md | Documents server-side naming sync + descriptor fallback behavior and precedence. |
| docs/HOOKS.md | Documents hook ingestion naming behavior including descriptor fallback and current_tool preservation. |
| docs/DATABASE.md | Updates sessions.name description to include the descriptor fallback. |
| ARCHITECTURE.md | Updates architecture notes to include custom-title > ai-title > first user prompt precedence. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…i, landing Mirror the session/agent descriptor naming docs (custom-title > ai-title > first user prompt) to README-VN.md and README-CN.md feature rows, extend the wiki Sessions Table and Transcript Cache blurbs (with re-keyed zh/vi entries in i18n-content.js and a cache bump: wiki-v27 -> wiki-v28, i18n ?v=18 -> 19), and add one line to the landing page's History import card. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeseRLX17wL3iTqc1trq
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Summary
Implements #201: sessions (and their main agent) that never receive a
custom-title/ai-title— including sessions imported from existing JSONL transcripts — are now described by their first user prompt instead of showingSession <id8>/Main Agent - Session <id8>forever.Closes #201
Changes
server/lib/transcript-cache.js—TranscriptCachenow captures the first real user message per transcript and surfaces it asfirstUserMessagealongside the existing extract fields. Tool-result entries,isMeta/caveat lines, local slash-command plumbing (<command-name>,<local-command-stdout>, …), compact summaries, and user-interrupt markers are all skipped; text is whitespace-collapsed and capped at 500 chars. Unlike the last-wins titles, the first value wins and is carried through both full and incremental reads (_mergeprefers the cached value). The extraction helper is exported (extractFirstUserText) so live and imported sessions derive the identical descriptor.server/routes/hooks.js— new idempotentapplyFirstUserDescriptorhelper fillssessions.nameand the main agent's name/task only while those fields still hold auto-generated placeholders. It runs strictly aftersyncSessionNameon every transcript-bearing event (live onUserPromptSubmit, backfill on any later event, including for imported sessions) and in the 15 s watchdog. Precedence is fully preserved:custom-titlealways wins;ai-titlefills a placeholder name — and can still replace a descriptor-filled name later (syncSessionNametreats a name equal to the descriptor label as replaceable);current_toolfootgun handled — the sharedupdateAgentstatement writescurrent_toolverbatim (noCOALESCE), so the helper passes the agent's existingcurrent_toolthrough; an in-flight tool is never wiped mid-turn (covered by a dedicated test that fills the descriptor inside the same transaction as aPreToolUse).scripts/import-history.js—parseSessionFilecaptures the same descriptor; new imports use it in the name fallback chain below titles (customTitle || aiTitle || firstUserLabel || cwd-derived fallback), set it as the main agent's task, and the re-import backfill pass renames placeholder session/main-agent rows from earlier imports — dead imported sessions never fire hooks, so the startup re-import is their only path to a meaningful label. User-picked names are never clobbered anywhere.ARCHITECTURE.md,server/README.md,docs/HOOKS.md,docs/DATABASE.mdupdated to describe the new naming precedence.server/__tests__/first-user-descriptor.test.js(16 tests): cache extraction/skipping/truncation/incremental first-wins, hook fill + idempotency + title precedence + later-ai-title replacement + user-set-name preservation +current_toolpreservation, and import (new rows, backfill, user-picked names kept).Type of Change
How to Test
npm run test:server— 552/552 pass (includes the 16 new tests).claudeand just type a prompt) — the Kanban/session list shows the prompt text as the session name andMain Agent - <prompt>with the prompt as the agent task, instead ofSession <id8>./renameor wait for the autoai-title— the title takes over the session name; a name you set in the dashboard is never overwritten.<folder> (<slug>),Main Agent - <folder> …) are renamed from their first prompt on the re-import pass.Checklist
🖋️ CLA Assistantbot will prompt me on my first PR)npm test)npm run format:check)🤖 Generated with Claude Code
https://claude.ai/code/session_0146oeseRLX17wL3iTqc1trq