-
Notifications
You must be signed in to change notification settings - Fork 17
feat: prefill the input with the ?q= prompt while provisioning (chat#1848 follow-up) #1849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+149
−24
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
f36c460
feat: prefill the input with the ?q= prompt while provisioning (chat#…
sweetmantech 886e82d
refactor: extract useInitialMessageAutoSend — deep-link behavior as a…
sweetmantech 1a4f98a
refactor: source auth state inside useInitialMessageAutoSend (KISS)
sweetmantech File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import { useEffect, useRef } from "react"; | ||
| import type { UIMessage } from "ai"; | ||
| import { usePrivy } from "@privy-io/react-auth"; | ||
| import { useUserProvider } from "@/providers/UserProvder"; | ||
| import { generateUUID } from "@/lib/generateUUID"; | ||
| import { getInitialMessageText } from "@/lib/chat/getInitialMessageText"; | ||
| import { shouldAutoSendInitialMessage } from "@/lib/chat/shouldAutoSendInitialMessage"; | ||
|
|
||
| interface UseInitialMessageAutoSendParams { | ||
| /** The ?q= deep-link message from /chat?q=… (AGENTS cards). */ | ||
| initialMessages?: UIMessage[]; | ||
| /** useChat transport status — only "ready" may send. */ | ||
| status: string; | ||
| messagesLength: number; | ||
| /** Api-minted session id; absent until the bootstrap provisions. */ | ||
| sessionId?: string; | ||
| input: string; | ||
| setInput: (value: string) => void; | ||
| /** The programmatic send path (handleSendQueryMessages). */ | ||
| send: (message: UIMessage) => void; | ||
| } | ||
|
|
||
| /** | ||
| * The whole ?q= deep-link behavior, self-contained so useVercelChat | ||
| * stays closed against changes here (OCP — this hook is the extension | ||
| * point, like useChatTransport / usePersistSelectedModel). Auth state | ||
| * is sourced from usePrivy/useUserProvider directly; params carry only | ||
| * what is chat-instance-scoped. | ||
| * | ||
| * 1. Prefills the input with the prompt immediately — instant feedback | ||
| * that the AGENTS click landed while the workspace provisions. | ||
| * 2. Auto-fires once provisioning lands (chat#1847: sending earlier | ||
| * POSTs /api/chat without a sessionId and 400s), sending whatever is | ||
| * in the input at that moment — an edit made while waiting sends the | ||
| * edited text, a cleared input sends nothing — exactly like | ||
| * pressing Send. One-shot refs guard re-prefill and double-sends. | ||
| */ | ||
| export function useInitialMessageAutoSend({ | ||
| initialMessages, | ||
| status, | ||
| messagesLength, | ||
| sessionId, | ||
| input, | ||
| setInput, | ||
| send, | ||
| }: UseInitialMessageAutoSendParams): void { | ||
| const { authenticated } = usePrivy(); | ||
| const { userData } = useUserProvider(); | ||
| const userId = userData?.account_id || userData?.id; | ||
|
|
||
| const initialMessageText = getInitialMessageText(initialMessages); | ||
| const didPrefillRef = useRef(false); | ||
| const didAutoFireRef = useRef(false); | ||
|
|
||
| useEffect(() => { | ||
| if (!initialMessageText || didPrefillRef.current) return; | ||
| if (messagesLength > 0 || input) return; | ||
| didPrefillRef.current = true; | ||
| setInput(initialMessageText); | ||
| }, [initialMessageText, messagesLength, input, setInput]); | ||
|
|
||
| useEffect(() => { | ||
| const mayAutoSend = shouldAutoSendInitialMessage({ | ||
| hasInitialMessages: Boolean(initialMessageText), | ||
| status, | ||
| messagesLength, | ||
| userId, | ||
| authenticated, | ||
| sessionId, | ||
| }); | ||
| if (!mayAutoSend || didAutoFireRef.current) return; | ||
| didAutoFireRef.current = true; | ||
| const text = input.trim() ? input : ""; | ||
| if (!text) return; | ||
| send({ id: generateUUID(), role: "user", parts: [{ type: "text", text }] }); | ||
| setInput(""); | ||
| }, [ | ||
| initialMessageText, | ||
| status, | ||
| messagesLength, | ||
| userId, | ||
| authenticated, | ||
| sessionId, | ||
| input, | ||
| setInput, | ||
| send, | ||
| ]); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { describe, it, expect } from "vitest"; | ||
| import type { UIMessage } from "ai"; | ||
| import { getInitialMessageText } from "@/lib/chat/getInitialMessageText"; | ||
|
|
||
| const message = (parts: UIMessage["parts"]): UIMessage[] => [ | ||
| { id: "m1", role: "user", parts }, | ||
| ]; | ||
|
|
||
| describe("getInitialMessageText", () => { | ||
| it("extracts the text of the first text part", () => { | ||
| expect( | ||
| getInitialMessageText(message([{ type: "text", text: "hello" }])), | ||
| ).toBe("hello"); | ||
| }); | ||
|
|
||
| it("skips non-text parts", () => { | ||
| expect( | ||
| getInitialMessageText( | ||
| message([ | ||
| { type: "file", url: "https://x/y.png", mediaType: "image/png" }, | ||
| { type: "text", text: "caption" }, | ||
| ]), | ||
| ), | ||
| ).toBe("caption"); | ||
| }); | ||
|
|
||
| it("returns undefined when there is nothing to prefill", () => { | ||
| expect(getInitialMessageText(undefined)).toBeUndefined(); | ||
| expect(getInitialMessageText([])).toBeUndefined(); | ||
| expect(getInitialMessageText(message([]))).toBeUndefined(); | ||
| expect( | ||
| getInitialMessageText(message([{ type: "text", text: "" }])), | ||
| ).toBeUndefined(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import type { UIMessage } from "ai"; | ||
|
|
||
| /** | ||
| * Text of the first text part of a ?q= deep-link's initial message — | ||
| * used to prefill the chat input while the workspace provisions, so the | ||
| * click gives instant feedback instead of dead space (chat#1848 | ||
| * follow-up). Undefined when there is nothing meaningful to prefill. | ||
| */ | ||
| export function getInitialMessageText( | ||
| initialMessages?: UIMessage[], | ||
| ): string | undefined { | ||
| const parts = initialMessages?.[0]?.parts ?? []; | ||
| for (const part of parts) { | ||
| if (part.type === "text" && part.text) return part.text; | ||
| } | ||
| return undefined; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P0: The auto-send can silently fail when the session is already provisioned before the prefill's
setInputcommits. MovedidAutoFireRef.current = truebelow theif (!text) returnguard so the effect can fire on the next render when the prefilled text is available.Prompt for AI agents