eve 0.43.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/src/channel/adapter.d.ts +2 -1
- package/dist/src/channel/instrumentation.js +1 -1
- package/dist/src/chunks/{use-eve-agent-C0SG1Ttk.js → use-eve-agent-BsLJiIbc.js} +224 -62
- package/dist/src/chunks/{use-eve-agent-D71oDil7.js → use-eve-agent-DpUEwS23.js} +222 -62
- package/dist/src/cli/commands/extension-init.js +1 -1
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/registry-project.d.ts +3 -0
- package/dist/src/cli/commands/registry-project.js +2 -1
- package/dist/src/cli/commands/registry.d.ts +2 -0
- package/dist/src/cli/commands/registry.js +1 -1
- package/dist/src/cli/dev/tui/setup-commands.js +1 -1
- package/dist/src/cli/dev/tui/traces/trace-view.js +1 -1
- package/dist/src/client/eve-agent-store-helpers.d.ts +12 -0
- package/dist/src/client/eve-agent-store-helpers.js +2 -0
- package/dist/src/client/eve-agent-store.d.ts +8 -6
- package/dist/src/client/eve-agent-store.js +1 -2
- package/dist/src/client/ndjson.d.ts +3 -1
- package/dist/src/client/ndjson.js +3 -3
- package/dist/src/client/open-stream.d.ts +3 -0
- package/dist/src/client/open-stream.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +1 -1
- package/dist/src/compiled/jsonc-parser/index.d.ts +19 -0
- package/dist/src/compiler/extension-compatibility.d.ts +2 -2
- package/dist/src/compiler/extension-compatibility.js +1 -1
- package/dist/src/context/build-dynamic-tools.js +1 -1
- package/dist/src/context/dynamic-tool-lifecycle.d.ts +5 -1
- package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
- package/dist/src/context/keys.d.ts +4 -2
- package/dist/src/context/keys.js +1 -1
- package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
- package/dist/src/execution/dispatch-turn-step.js +1 -1
- package/dist/src/execution/eve-workflow-attributes.d.ts +3 -0
- package/dist/src/execution/eve-workflow-attributes.js +1 -1
- package/dist/src/execution/pending-turn-state.d.ts +9 -0
- package/dist/src/execution/pending-turn-state.js +1 -0
- package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
- package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
- package/dist/src/execution/turn-workflow.js +1 -1
- package/dist/src/execution/workflow-steps.js +1 -1
- package/dist/src/execution/workflow-trace-context.js +1 -1
- package/dist/src/harness/channel-delivery-instrumentation.js +1 -1
- package/dist/src/harness/instrumentation/content-policy.d.ts +6 -0
- package/dist/src/harness/instrumentation/content-policy.js +1 -0
- package/dist/src/harness/instrumentation/lifecycle.d.ts +6 -0
- package/dist/src/harness/instrumentation/native-events.d.ts +2 -0
- package/dist/src/harness/instrumentation/native-events.js +1 -1
- package/dist/src/harness/instrumentation/runtime-context.js +1 -1
- package/dist/src/harness/prepare-trace-context.d.ts +2 -0
- package/dist/src/harness/prepare-trace-context.js +1 -1
- package/dist/src/harness/runtime-actions.js +1 -1
- package/dist/src/harness/tool-loop.js +2 -2
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/workflow-bundle/dynamic-remote-agent-transform.js +2 -2
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +4 -4
- package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +3 -4
- package/dist/src/internal/workflow-bundle/stable-module-id.d.ts +6 -0
- package/dist/src/internal/workflow-bundle/stable-module-id.js +1 -0
- package/dist/src/packages/eve-catalog/src/index.js +1 -1
- package/dist/src/public/channels/chat-sdk/audience.d.ts +2 -0
- package/dist/src/public/channels/chat-sdk/audience.js +1 -0
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.d.ts +2 -0
- package/dist/src/public/channels/chat-sdk/chatSdkChannel.js +2 -2
- package/dist/src/public/channels/discord/audience.d.ts +5 -0
- package/dist/src/public/channels/discord/audience.js +1 -0
- package/dist/src/public/channels/discord/discordChannel.d.ts +2 -0
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/discord/inbound.d.ts +1 -0
- package/dist/src/public/channels/discord/inbound.js +1 -1
- package/dist/src/public/channels/discord/index.d.ts +1 -0
- package/dist/src/public/channels/index.d.ts +3 -2
- package/dist/src/public/channels/slack/slackChannel.d.ts +4 -0
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/index.d.ts +1 -0
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/audience.d.ts +3 -0
- package/dist/src/public/channels/telegram/audience.js +1 -0
- package/dist/src/public/channels/telegram/index.d.ts +1 -0
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -0
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/channel.d.ts +1 -0
- package/dist/src/public/instrumentation/otel.d.ts +6 -10
- package/dist/src/public/instrumentation/otel.js +1 -1
- package/dist/src/public/tools/approval/approval-helpers.js +1 -1
- package/dist/src/react/use-eve-agent.d.ts +10 -1
- package/dist/src/react/use-eve-agent.js +1 -1
- package/dist/src/runtime/attributes/emit.d.ts +1 -1
- package/dist/src/runtime/attributes/normalize.d.ts +1 -1
- package/dist/src/runtime/attributes/normalize.js +1 -1
- package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
- package/dist/src/runtime/framework-tools/subagent/local.js +1 -1
- package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
- package/dist/src/setup/integrations/linq/connect.js +1 -1
- package/dist/src/setup/primitives/pm/npm.js +1 -1
- package/dist/src/setup/primitives/pm/pnpm.js +5 -9
- package/dist/src/setup/primitives/pm/types.d.ts +0 -2
- package/dist/src/setup/scaffold/create/project.js +1 -1
- package/dist/src/setup/scaffold/create/web-template.d.ts +10 -4
- package/dist/src/setup/scaffold/create/web-template.js +669 -74
- package/dist/src/shared/channel-audience.d.ts +7 -0
- package/dist/src/shared/channel-audience.js +1 -0
- package/dist/src/shared/channel-definition.d.ts +2 -1
- package/dist/src/shared/durable-dynamic-tool-callbacks.d.ts +43 -19
- package/dist/src/shared/durable-dynamic-tool-callbacks.js +1 -1
- package/dist/src/svelte/index.js +1 -1
- package/dist/src/svelte/use-eve-agent.d.ts +4 -0
- package/dist/src/svelte/use-eve-agent.js +1 -1
- package/dist/src/tasks/delivery-context.d.ts +9 -0
- package/dist/src/tasks/delivery-context.js +5 -2
- package/dist/src/tracing/agent-action-instrumentation.js +1 -1
- package/dist/src/tracing/agent-approval-instrumentation.js +1 -1
- package/dist/src/tracing/agent-channel-delivery-instrumentation.js +1 -1
- package/dist/src/tracing/agent-otel-provider.d.ts +2 -0
- package/dist/src/tracing/agent-otel-provider.js +2 -2
- package/dist/src/tracing/agent-otel-session-context.d.ts +2 -0
- package/dist/src/tracing/agent-otel-session-context.js +1 -1
- package/dist/src/tracing/agent-trace-context-store.js +1 -1
- package/dist/src/tracing/agent-trace-state.d.ts +3 -0
- package/dist/src/tracing/channel-audience-context.d.ts +4 -0
- package/dist/src/tracing/channel-audience-context.js +1 -0
- package/dist/src/tracing/content-attributes.js +1 -1
- package/dist/src/tracing/content-span-processor.d.ts +7 -7
- package/dist/src/tracing/content-span-processor.js +1 -1
- package/dist/src/tracing/install-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/local-instrumentation-runtime.d.ts +3 -0
- package/dist/src/tracing/local-instrumentation-runtime.js +1 -1
- package/dist/src/tracing/local-traces.d.ts +1 -10
- package/dist/src/tracing/local-traces.js +1 -1
- package/dist/src/tracing/otel-declaration.d.ts +34 -27
- package/dist/src/tracing/otel-declaration.js +1 -1
- package/dist/src/tracing/sampled-trace.d.ts +2 -0
- package/dist/src/tracing/sampled-trace.js +1 -0
- package/dist/src/tracing/span-export-policy.d.ts +41 -0
- package/dist/src/tracing/span-export-policy.js +1 -0
- package/dist/src/tracing/suppress-tracing.d.ts +2 -0
- package/dist/src/tracing/suppress-tracing.js +1 -0
- package/dist/src/vue/index.js +1 -1
- package/dist/src/vue/use-eve-agent.d.ts +4 -0
- package/dist/src/vue/use-eve-agent.js +1 -1
- package/docs/channels/custom.mdx +2 -0
- package/docs/concepts/sessions-runs-and-streaming.md +1 -1
- package/docs/guides/dynamic-capabilities.md +10 -2
- package/docs/guides/frontend/nextjs.mdx +23 -0
- package/docs/guides/frontend/overview.mdx +24 -9
- package/docs/guides/frontend/use-eve-agent-svelte.mdx +9 -9
- package/docs/guides/frontend/use-eve-agent-vue.mdx +9 -9
- package/docs/guides/instrumentation.md +2 -0
- package/docs/tools/overview.mdx +5 -4
- package/docs/tutorial/ship-it.mdx +1 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export default eveChannel({
|
|
|
17
17
|
|
|
18
18
|
import type { UserContent } from "ai";
|
|
19
19
|
import { useEveAgent } from "eve/react";
|
|
20
|
-
import { AlertCircleIcon, BrainIcon } from "lucide-react";
|
|
20
|
+
import { AlertCircleIcon, BrainIcon, PlusIcon, SquareIcon } from "lucide-react";
|
|
21
21
|
import { useState } from "react";
|
|
22
22
|
import {
|
|
23
23
|
Conversation,
|
|
@@ -27,20 +27,50 @@ import {
|
|
|
27
27
|
import { Message, MessageContent } from "@/components/ai-elements/message";
|
|
28
28
|
import {
|
|
29
29
|
PromptInput,
|
|
30
|
+
PromptInputButton,
|
|
30
31
|
type PromptInputMessage,
|
|
31
32
|
PromptInputSubmit,
|
|
32
33
|
PromptInputTextarea,
|
|
33
34
|
} from "@/components/ai-elements/prompt-input";
|
|
34
35
|
import { Shimmer } from "@/components/ai-elements/shimmer";
|
|
36
|
+
import { Button } from "@/components/ui/button";
|
|
35
37
|
import { cn } from "@/lib/utils";
|
|
36
38
|
import { AgentMessage } from "./agent-message";
|
|
37
39
|
|
|
38
40
|
const AGENT_NAME = "__EVE_INIT_APP_NAME__";
|
|
39
41
|
|
|
40
|
-
export function AgentChat(
|
|
42
|
+
export function AgentChat({
|
|
43
|
+
sessionId,
|
|
44
|
+
sessionless = false,
|
|
45
|
+
}: {
|
|
46
|
+
readonly sessionId?: string;
|
|
47
|
+
readonly sessionless?: boolean;
|
|
48
|
+
}) {
|
|
41
49
|
const [cancellationError, setCancellationError] = useState<string>();
|
|
42
|
-
const agent = useEveAgent(
|
|
50
|
+
const agent = useEveAgent({
|
|
51
|
+
initialSession:
|
|
52
|
+
sessionId === undefined
|
|
53
|
+
? undefined
|
|
54
|
+
: {
|
|
55
|
+
sessionId,
|
|
56
|
+
streamIndex: 0,
|
|
57
|
+
},
|
|
58
|
+
resume: sessionId !== undefined,
|
|
59
|
+
onSessionChange(session) {
|
|
60
|
+
if (sessionId === undefined && session !== undefined) {
|
|
61
|
+
// Next patches window.history to navigate, which would detach the active stream.
|
|
62
|
+
History.prototype.replaceState.call(
|
|
63
|
+
window.history,
|
|
64
|
+
window.history.state,
|
|
65
|
+
"",
|
|
66
|
+
\`/s/\${encodeURIComponent(session.sessionId)}\`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
43
72
|
const isBusy = agent.status === "submitted" || agent.status === "streaming";
|
|
73
|
+
const isRestoring = sessionId !== undefined && agent.events.length === 0 && isBusy;
|
|
44
74
|
const isEmpty = agent.data.messages.length === 0;
|
|
45
75
|
const lastMessage = agent.data.messages.at(-1);
|
|
46
76
|
const isPendingAssistantShell =
|
|
@@ -51,6 +81,9 @@ export function AgentChat() {
|
|
|
51
81
|
(agent.status === "submitted" || lastMessage?.role !== "assistant" || isPendingAssistantShell);
|
|
52
82
|
const turnFailure = isBusy ? undefined : getLatestTurnFailure(agent.events);
|
|
53
83
|
const errorMessage = cancellationError ?? agent.error?.message ?? turnFailure;
|
|
84
|
+
const hasConversationContent = sessionless || !isEmpty || errorMessage !== undefined;
|
|
85
|
+
const showConversationLayout = isRestoring || hasConversationContent;
|
|
86
|
+
const activeSessionId = sessionId ?? agent.session?.sessionId;
|
|
54
87
|
|
|
55
88
|
const requestCancellation = () => {
|
|
56
89
|
setCancellationError(undefined);
|
|
@@ -61,12 +94,13 @@ export function AgentChat() {
|
|
|
61
94
|
|
|
62
95
|
const handleSubmit = async (message: PromptInputMessage) => {
|
|
63
96
|
const text = message.text.trim();
|
|
64
|
-
if ((text.length === 0 && message.files.length === 0) ||
|
|
97
|
+
if ((text.length === 0 && message.files.length === 0) || isRestoring) return;
|
|
65
98
|
|
|
66
99
|
setCancellationError(undefined);
|
|
100
|
+
const options = isBusy ? { turnPolicy: "steer" as const } : undefined;
|
|
67
101
|
|
|
68
102
|
if (message.files.length === 0) {
|
|
69
|
-
await agent.send(text);
|
|
103
|
+
await agent.send(text, options);
|
|
70
104
|
return;
|
|
71
105
|
}
|
|
72
106
|
|
|
@@ -83,42 +117,44 @@ export function AgentChat() {
|
|
|
83
117
|
});
|
|
84
118
|
}
|
|
85
119
|
|
|
86
|
-
await agent.send(parts);
|
|
120
|
+
await agent.send(parts, options);
|
|
87
121
|
};
|
|
88
122
|
|
|
89
123
|
const composer = (
|
|
90
124
|
<PromptInput onSubmit={handleSubmit}>
|
|
91
|
-
<PromptInputTextarea disabled={
|
|
92
|
-
|
|
125
|
+
<PromptInputTextarea disabled={isRestoring} placeholder="Send a message…" />
|
|
126
|
+
{isBusy && !isRestoring ? (
|
|
127
|
+
<PromptInputButton
|
|
128
|
+
aria-label="Stop"
|
|
129
|
+
className="absolute right-12 bottom-2.5 rounded-full"
|
|
130
|
+
onClick={requestCancellation}
|
|
131
|
+
variant="default"
|
|
132
|
+
>
|
|
133
|
+
<SquareIcon className="size-3 fill-current" />
|
|
134
|
+
</PromptInputButton>
|
|
135
|
+
) : null}
|
|
136
|
+
<PromptInputSubmit disabled={isRestoring} status={isBusy ? undefined : agent.status} />
|
|
93
137
|
</PromptInput>
|
|
94
138
|
);
|
|
95
139
|
|
|
96
140
|
return (
|
|
97
141
|
<main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">
|
|
98
|
-
{
|
|
99
|
-
<
|
|
100
|
-
<span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>
|
|
101
|
-
</header>
|
|
102
|
-
)}
|
|
103
|
-
|
|
104
|
-
{errorMessage ? (
|
|
105
|
-
<div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">
|
|
106
|
-
<div
|
|
107
|
-
className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"
|
|
108
|
-
role="alert"
|
|
109
|
-
>
|
|
110
|
-
<AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />
|
|
111
|
-
<div>
|
|
112
|
-
<p className="font-medium">Request failed</p>
|
|
113
|
-
<p className="mt-0.5 text-muted-foreground">{errorMessage}</p>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
142
|
+
{showConversationLayout ? (
|
|
143
|
+
<ChatHeader canStartNewChat={activeSessionId !== undefined} />
|
|
117
144
|
) : null}
|
|
118
145
|
|
|
119
|
-
{
|
|
120
|
-
<Conversation
|
|
121
|
-
|
|
146
|
+
{showConversationLayout ? (
|
|
147
|
+
<Conversation
|
|
148
|
+
className="min-h-0 flex-1"
|
|
149
|
+
initial={sessionId === undefined ? undefined : false}
|
|
150
|
+
resize={activeSessionId === undefined ? "smooth" : "instant"}
|
|
151
|
+
scrollRestorationKey={
|
|
152
|
+
isEmpty || activeSessionId === undefined
|
|
153
|
+
? undefined
|
|
154
|
+
: \`eve:web-chat-scroll:\${activeSessionId}\`
|
|
155
|
+
}
|
|
156
|
+
>
|
|
157
|
+
<ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 pt-20 pb-36 sm:px-6">
|
|
122
158
|
{agent.data.messages.map((message, index) =>
|
|
123
159
|
showPendingThinking &&
|
|
124
160
|
isPendingAssistantShell &&
|
|
@@ -138,30 +174,73 @@ export function AgentChat() {
|
|
|
138
174
|
),
|
|
139
175
|
)}
|
|
140
176
|
{showPendingThinking ? <PendingThinking /> : null}
|
|
177
|
+
{errorMessage ? <ErrorMessage message={errorMessage} /> : null}
|
|
141
178
|
</ConversationContent>
|
|
142
179
|
<ConversationScrollButton />
|
|
143
180
|
</Conversation>
|
|
144
|
-
)}
|
|
181
|
+
) : null}
|
|
145
182
|
|
|
146
183
|
<div
|
|
147
184
|
className={cn(
|
|
148
185
|
"mx-auto w-full px-4 sm:px-6",
|
|
149
|
-
|
|
150
|
-
? "
|
|
151
|
-
: "max-w-
|
|
186
|
+
showConversationLayout
|
|
187
|
+
? "fixed bottom-0 left-1/2 z-20 max-w-3xl -translate-x-1/2 bg-gradient-to-t from-background via-background to-transparent pt-4 pb-6"
|
|
188
|
+
: "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]",
|
|
152
189
|
)}
|
|
153
190
|
>
|
|
154
|
-
{
|
|
191
|
+
{showConversationLayout ? null : (
|
|
155
192
|
<div className="flex flex-col items-center gap-3 text-center">
|
|
156
193
|
<h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>
|
|
157
194
|
</div>
|
|
158
|
-
)
|
|
195
|
+
)}
|
|
159
196
|
<div className="w-full">{composer}</div>
|
|
160
197
|
</div>
|
|
161
198
|
</main>
|
|
162
199
|
);
|
|
163
200
|
}
|
|
164
201
|
|
|
202
|
+
function ErrorMessage({ message }: { readonly message: string }) {
|
|
203
|
+
return (
|
|
204
|
+
<Message className="max-w-full" from="assistant">
|
|
205
|
+
<MessageContent>
|
|
206
|
+
<div
|
|
207
|
+
className="flex w-full items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm"
|
|
208
|
+
role="alert"
|
|
209
|
+
>
|
|
210
|
+
<AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />
|
|
211
|
+
<div>
|
|
212
|
+
<p className="font-medium">Request failed</p>
|
|
213
|
+
<p className="mt-0.5 text-muted-foreground">{message}</p>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</MessageContent>
|
|
217
|
+
</Message>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function ChatHeader({ canStartNewChat }: { readonly canStartNewChat: boolean }) {
|
|
222
|
+
return (
|
|
223
|
+
<header className="pointer-events-none fixed top-0 right-0 left-0 z-20 h-14">
|
|
224
|
+
<div className="relative mx-auto flex h-full w-full max-w-3xl items-center justify-center bg-background px-24">
|
|
225
|
+
<span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>
|
|
226
|
+
{canStartNewChat ? (
|
|
227
|
+
<Button
|
|
228
|
+
aria-label="Start a new chat"
|
|
229
|
+
className="pointer-events-auto fixed top-2 right-6"
|
|
230
|
+
onClick={() => window.location.assign("/s")}
|
|
231
|
+
size="sm"
|
|
232
|
+
type="button"
|
|
233
|
+
variant="ghost"
|
|
234
|
+
>
|
|
235
|
+
<PlusIcon className="size-4" />
|
|
236
|
+
<span className="hidden sm:inline">New chat</span>
|
|
237
|
+
</Button>
|
|
238
|
+
) : null}
|
|
239
|
+
</div>
|
|
240
|
+
</header>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
165
244
|
function PendingThinking() {
|
|
166
245
|
return (
|
|
167
246
|
<Message aria-live="polite" from="assistant">
|
|
@@ -208,6 +287,7 @@ import type {
|
|
|
208
287
|
EveAuthorizationPart,
|
|
209
288
|
EveDynamicToolPart,
|
|
210
289
|
EveMessage,
|
|
290
|
+
EveMessageInputRequest,
|
|
211
291
|
EveMessagePart,
|
|
212
292
|
} from "eve/react";
|
|
213
293
|
import {
|
|
@@ -219,6 +299,17 @@ import {
|
|
|
219
299
|
XCircleIcon,
|
|
220
300
|
} from "lucide-react";
|
|
221
301
|
import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";
|
|
302
|
+
import {
|
|
303
|
+
Question,
|
|
304
|
+
QuestionActions,
|
|
305
|
+
QuestionDescription,
|
|
306
|
+
QuestionInput,
|
|
307
|
+
QuestionOption,
|
|
308
|
+
QuestionOptions,
|
|
309
|
+
QuestionPrompt,
|
|
310
|
+
type QuestionResponse,
|
|
311
|
+
QuestionSubmit,
|
|
312
|
+
} from "@/components/ai-elements/question";
|
|
222
313
|
import { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";
|
|
223
314
|
import {
|
|
224
315
|
Tool,
|
|
@@ -253,6 +344,9 @@ export function AgentMessage({
|
|
|
253
344
|
(last, part, index) => (part.type === "text" ? index : last),
|
|
254
345
|
-1,
|
|
255
346
|
);
|
|
347
|
+
const hasAssistantText =
|
|
348
|
+
message.role === "assistant" &&
|
|
349
|
+
message.parts.some((part) => part.type === "text" && part.text.length > 0);
|
|
256
350
|
|
|
257
351
|
return (
|
|
258
352
|
<Message
|
|
@@ -260,15 +354,17 @@ export function AgentMessage({
|
|
|
260
354
|
from={message.role}
|
|
261
355
|
>
|
|
262
356
|
<MessageContent>
|
|
263
|
-
{message.parts.map((part, index) =>
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
357
|
+
{message.parts.map((part, index) =>
|
|
358
|
+
hasAssistantText && part.type === "reasoning" ? null : (
|
|
359
|
+
<AgentMessagePart
|
|
360
|
+
canRespond={canRespond}
|
|
361
|
+
key={partKey(part, index)}
|
|
362
|
+
onInputResponses={onInputResponses}
|
|
363
|
+
part={part}
|
|
364
|
+
showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}
|
|
365
|
+
/>
|
|
366
|
+
),
|
|
367
|
+
)}
|
|
272
368
|
</MessageContent>
|
|
273
369
|
</Message>
|
|
274
370
|
);
|
|
@@ -305,7 +401,19 @@ function AgentMessagePart({
|
|
|
305
401
|
return <AttachmentPart part={part} />;
|
|
306
402
|
case "authorization":
|
|
307
403
|
return <AuthorizationPrompt part={part} />;
|
|
308
|
-
case "dynamic-tool":
|
|
404
|
+
case "dynamic-tool": {
|
|
405
|
+
const inputRequest = part.toolMetadata?.eve?.inputRequest;
|
|
406
|
+
if (inputRequest?.kind === "question") {
|
|
407
|
+
return (
|
|
408
|
+
<QuestionRequest
|
|
409
|
+
canRespond={canRespond}
|
|
410
|
+
inputRequest={inputRequest}
|
|
411
|
+
inputResponse={part.toolMetadata?.eve?.inputResponse}
|
|
412
|
+
onInputResponses={onInputResponses}
|
|
413
|
+
/>
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
309
417
|
return (
|
|
310
418
|
<Tool
|
|
311
419
|
defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}
|
|
@@ -327,9 +435,76 @@ function AgentMessagePart({
|
|
|
327
435
|
</ToolContent>
|
|
328
436
|
</Tool>
|
|
329
437
|
);
|
|
438
|
+
}
|
|
330
439
|
}
|
|
331
440
|
}
|
|
332
441
|
|
|
442
|
+
function QuestionRequest({
|
|
443
|
+
canRespond,
|
|
444
|
+
inputRequest,
|
|
445
|
+
inputResponse,
|
|
446
|
+
onInputResponses,
|
|
447
|
+
}: {
|
|
448
|
+
readonly canRespond: boolean;
|
|
449
|
+
readonly inputRequest: EveMessageInputRequest;
|
|
450
|
+
readonly inputResponse?: AgentInputResponse;
|
|
451
|
+
readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;
|
|
452
|
+
}) {
|
|
453
|
+
const selectedOption = inputRequest.options?.find(
|
|
454
|
+
(option) => option.id === inputResponse?.optionId,
|
|
455
|
+
);
|
|
456
|
+
const hasOptions = (inputRequest.options?.length ?? 0) > 0;
|
|
457
|
+
const acceptsFreeform = inputRequest.allowFreeform === true || !hasOptions;
|
|
458
|
+
|
|
459
|
+
const submitResponse = ({ selectedValues, text }: QuestionResponse) =>
|
|
460
|
+
onInputResponses([
|
|
461
|
+
{
|
|
462
|
+
optionId: selectedValues[0],
|
|
463
|
+
requestId: inputRequest.requestId,
|
|
464
|
+
text,
|
|
465
|
+
},
|
|
466
|
+
]);
|
|
467
|
+
|
|
468
|
+
return (
|
|
469
|
+
<Question
|
|
470
|
+
defaultValue={{
|
|
471
|
+
selectedValues: inputResponse?.optionId ? [inputResponse.optionId] : [],
|
|
472
|
+
text: inputResponse?.text ?? "",
|
|
473
|
+
}}
|
|
474
|
+
disabled={!canRespond || inputResponse !== undefined}
|
|
475
|
+
onSubmit={submitResponse}
|
|
476
|
+
>
|
|
477
|
+
<QuestionPrompt>{inputRequest.prompt}</QuestionPrompt>
|
|
478
|
+
{hasOptions ? (
|
|
479
|
+
<QuestionOptions className="flex-col items-stretch" aria-label={inputRequest.prompt}>
|
|
480
|
+
{inputRequest.options?.map((option) => (
|
|
481
|
+
<QuestionOption className="justify-start text-left" key={option.id} value={option.id}>
|
|
482
|
+
<span>
|
|
483
|
+
<span className="block">{option.label}</span>
|
|
484
|
+
{option.description ? (
|
|
485
|
+
<span className="block font-normal text-xs opacity-70">{option.description}</span>
|
|
486
|
+
) : null}
|
|
487
|
+
</span>
|
|
488
|
+
</QuestionOption>
|
|
489
|
+
))}
|
|
490
|
+
</QuestionOptions>
|
|
491
|
+
) : null}
|
|
492
|
+
{acceptsFreeform ? (
|
|
493
|
+
<QuestionInput aria-label="Answer" placeholder="Type your answer…" />
|
|
494
|
+
) : null}
|
|
495
|
+
{inputResponse ? (
|
|
496
|
+
<QuestionDescription>
|
|
497
|
+
Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}
|
|
498
|
+
</QuestionDescription>
|
|
499
|
+
) : (
|
|
500
|
+
<QuestionActions>
|
|
501
|
+
<QuestionSubmit>Answer</QuestionSubmit>
|
|
502
|
+
</QuestionActions>
|
|
503
|
+
)}
|
|
504
|
+
</Question>
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
333
508
|
function AttachmentPart({ part }: { readonly part: EveFilePart }) {
|
|
334
509
|
const label = part.filename ?? "Attachment";
|
|
335
510
|
const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");
|
|
@@ -668,6 +843,21 @@ export default function RootLayout({ children }: { readonly children: ReactNode
|
|
|
668
843
|
export default function Page() {
|
|
669
844
|
return <AgentChat />;
|
|
670
845
|
}
|
|
846
|
+
`,"app/s/[sessionId]/page.tsx":`import { AgentChat } from "@/app/_components/agent-chat";
|
|
847
|
+
|
|
848
|
+
export default async function SessionPage({
|
|
849
|
+
params,
|
|
850
|
+
}: {
|
|
851
|
+
readonly params: Promise<{ readonly sessionId: string }>;
|
|
852
|
+
}) {
|
|
853
|
+
const { sessionId } = await params;
|
|
854
|
+
return <AgentChat sessionId={sessionId} />;
|
|
855
|
+
}
|
|
856
|
+
`,"app/s/page.tsx":`import { AgentChat } from "@/app/_components/agent-chat";
|
|
857
|
+
|
|
858
|
+
export default function NewSessionPage() {
|
|
859
|
+
return <AgentChat sessionless />;
|
|
860
|
+
}
|
|
671
861
|
`,"components/ai-elements/chain-of-thought.tsx":`"use client";
|
|
672
862
|
|
|
673
863
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
@@ -1394,21 +1584,116 @@ import { cn } from "@/lib/utils";
|
|
|
1394
1584
|
import type { UIMessage } from "ai";
|
|
1395
1585
|
import { ArrowDownIcon, DownloadIcon } from "lucide-react";
|
|
1396
1586
|
import type { ComponentProps } from "react";
|
|
1397
|
-
import { useCallback } from "react";
|
|
1587
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
1398
1588
|
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
1399
1589
|
|
|
1400
|
-
export type ConversationProps = ComponentProps<typeof StickToBottom
|
|
1590
|
+
export type ConversationProps = ComponentProps<typeof StickToBottom> & {
|
|
1591
|
+
scrollRestorationKey?: string;
|
|
1592
|
+
};
|
|
1401
1593
|
|
|
1402
|
-
export const Conversation = ({
|
|
1594
|
+
export const Conversation = ({
|
|
1595
|
+
children,
|
|
1596
|
+
className,
|
|
1597
|
+
initial,
|
|
1598
|
+
scrollRestorationKey,
|
|
1599
|
+
...props
|
|
1600
|
+
}: ConversationProps) => (
|
|
1403
1601
|
<StickToBottom
|
|
1404
1602
|
className={cn("relative flex-1 overflow-y-hidden", className)}
|
|
1405
|
-
initial="smooth"
|
|
1603
|
+
initial={initial ?? (scrollRestorationKey === undefined ? "smooth" : false)}
|
|
1406
1604
|
resize="smooth"
|
|
1407
1605
|
role="log"
|
|
1408
1606
|
{...props}
|
|
1409
|
-
|
|
1607
|
+
>
|
|
1608
|
+
{typeof children === "function" ? (
|
|
1609
|
+
(context) => (
|
|
1610
|
+
<>
|
|
1611
|
+
{children(context)}
|
|
1612
|
+
{scrollRestorationKey === undefined ? null : (
|
|
1613
|
+
<ConversationScrollRestoration storageKey={scrollRestorationKey} />
|
|
1614
|
+
)}
|
|
1615
|
+
</>
|
|
1616
|
+
)
|
|
1617
|
+
) : (
|
|
1618
|
+
<>
|
|
1619
|
+
{children}
|
|
1620
|
+
{scrollRestorationKey === undefined ? null : (
|
|
1621
|
+
<ConversationScrollRestoration storageKey={scrollRestorationKey} />
|
|
1622
|
+
)}
|
|
1623
|
+
</>
|
|
1624
|
+
)}
|
|
1625
|
+
</StickToBottom>
|
|
1410
1626
|
);
|
|
1411
1627
|
|
|
1628
|
+
function ConversationScrollRestoration({ storageKey }: { readonly storageKey: string }) {
|
|
1629
|
+
const { scrollRef, scrollToBottom, state } = useStickToBottomContext();
|
|
1630
|
+
const restoredKeyRef = useRef<string | undefined>(undefined);
|
|
1631
|
+
|
|
1632
|
+
useLayoutEffect(() => {
|
|
1633
|
+
const scrollElement = scrollRef.current;
|
|
1634
|
+
if (scrollElement === null) return;
|
|
1635
|
+
|
|
1636
|
+
if (restoredKeyRef.current !== storageKey) {
|
|
1637
|
+
const saved = readScrollPosition(sessionStorage.getItem(storageKey));
|
|
1638
|
+
if (saved?.atBottom === false) {
|
|
1639
|
+
scrollElement.scrollTop = saved.scrollTop;
|
|
1640
|
+
requestAnimationFrame(() => {
|
|
1641
|
+
scrollElement.scrollTop = saved.scrollTop;
|
|
1642
|
+
});
|
|
1643
|
+
} else {
|
|
1644
|
+
scrollElement.scrollTop = scrollElement.scrollHeight;
|
|
1645
|
+
scrollToBottom({ animation: "instant", ignoreEscapes: true });
|
|
1646
|
+
}
|
|
1647
|
+
restoredKeyRef.current = storageKey;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
const saveNow = () => {
|
|
1651
|
+
sessionStorage.setItem(
|
|
1652
|
+
storageKey,
|
|
1653
|
+
JSON.stringify({
|
|
1654
|
+
atBottom: state.isAtBottom || state.isNearBottom,
|
|
1655
|
+
scrollTop: scrollElement.scrollTop,
|
|
1656
|
+
}),
|
|
1657
|
+
);
|
|
1658
|
+
};
|
|
1659
|
+
let frame: number | undefined;
|
|
1660
|
+
const scheduleSave = () => {
|
|
1661
|
+
if (frame !== undefined) return;
|
|
1662
|
+
frame = requestAnimationFrame(() => {
|
|
1663
|
+
frame = undefined;
|
|
1664
|
+
saveNow();
|
|
1665
|
+
});
|
|
1666
|
+
};
|
|
1667
|
+
scrollElement.addEventListener("scroll", scheduleSave, { passive: true });
|
|
1668
|
+
window.addEventListener("pagehide", saveNow);
|
|
1669
|
+
return () => {
|
|
1670
|
+
scrollElement.removeEventListener("scroll", scheduleSave);
|
|
1671
|
+
window.removeEventListener("pagehide", saveNow);
|
|
1672
|
+
if (frame !== undefined) cancelAnimationFrame(frame);
|
|
1673
|
+
saveNow();
|
|
1674
|
+
};
|
|
1675
|
+
}, [scrollRef, scrollToBottom, state, storageKey]);
|
|
1676
|
+
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function readScrollPosition(value: string | null):
|
|
1681
|
+
| {
|
|
1682
|
+
readonly atBottom: boolean;
|
|
1683
|
+
readonly scrollTop: number;
|
|
1684
|
+
}
|
|
1685
|
+
| undefined {
|
|
1686
|
+
if (value === null) return undefined;
|
|
1687
|
+
try {
|
|
1688
|
+
const parsed = JSON.parse(value) as { atBottom?: unknown; scrollTop?: unknown };
|
|
1689
|
+
return typeof parsed.atBottom === "boolean" && typeof parsed.scrollTop === "number"
|
|
1690
|
+
? { atBottom: parsed.atBottom, scrollTop: parsed.scrollTop }
|
|
1691
|
+
: undefined;
|
|
1692
|
+
} catch {
|
|
1693
|
+
return undefined;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1412
1697
|
export type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
|
|
1413
1698
|
|
|
1414
1699
|
export const ConversationContent = ({ className, ...props }: ConversationContentProps) => (
|
|
@@ -1455,16 +1740,21 @@ export const ConversationScrollButton = ({
|
|
|
1455
1740
|
...props
|
|
1456
1741
|
}: ConversationScrollButtonProps) => {
|
|
1457
1742
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
1743
|
+
const [isReady, setIsReady] = useState(false);
|
|
1744
|
+
|
|
1745
|
+
useEffect(() => setIsReady(true), []);
|
|
1458
1746
|
|
|
1459
1747
|
const handleScrollToBottom = useCallback(() => {
|
|
1460
1748
|
scrollToBottom();
|
|
1461
1749
|
}, [scrollToBottom]);
|
|
1462
1750
|
|
|
1463
1751
|
return (
|
|
1752
|
+
isReady &&
|
|
1464
1753
|
!isAtBottom && (
|
|
1465
1754
|
<Button
|
|
1755
|
+
aria-label="Scroll to bottom"
|
|
1466
1756
|
className={cn(
|
|
1467
|
-
"absolute bottom-
|
|
1757
|
+
"absolute bottom-32 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
1468
1758
|
className,
|
|
1469
1759
|
)}
|
|
1470
1760
|
onClick={handleScrollToBottom}
|
|
@@ -3149,6 +3439,287 @@ export const PromptInputCommandSeparator = ({
|
|
|
3149
3439
|
className,
|
|
3150
3440
|
...props
|
|
3151
3441
|
}: PromptInputCommandSeparatorProps) => <CommandSeparator className={cn(className)} {...props} />;
|
|
3442
|
+
`,"components/ai-elements/question.tsx":`"use client";
|
|
3443
|
+
|
|
3444
|
+
import type {
|
|
3445
|
+
ChangeEvent,
|
|
3446
|
+
ComponentProps,
|
|
3447
|
+
FormEvent,
|
|
3448
|
+
HTMLAttributes,
|
|
3449
|
+
MouseEvent,
|
|
3450
|
+
ReactNode,
|
|
3451
|
+
} from "react";
|
|
3452
|
+
|
|
3453
|
+
import { Button } from "@/components/ui/button";
|
|
3454
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
3455
|
+
import { cn } from "@/lib/utils";
|
|
3456
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
3457
|
+
|
|
3458
|
+
export interface QuestionValue {
|
|
3459
|
+
selectedValues: readonly string[];
|
|
3460
|
+
text: string;
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
export interface QuestionResponse {
|
|
3464
|
+
selectedValues: readonly string[];
|
|
3465
|
+
text?: string;
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
type SelectionMode = "multiple" | "single";
|
|
3469
|
+
|
|
3470
|
+
interface QuestionContextValue {
|
|
3471
|
+
disabled: boolean;
|
|
3472
|
+
selectedValues: readonly string[];
|
|
3473
|
+
selectionMode: SelectionMode;
|
|
3474
|
+
setText: (text: string) => void;
|
|
3475
|
+
text: string;
|
|
3476
|
+
toggleValue: (value: string) => void;
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
const QuestionContext = createContext<QuestionContextValue | null>(null);
|
|
3480
|
+
|
|
3481
|
+
const useQuestion = () => {
|
|
3482
|
+
const context = useContext(QuestionContext);
|
|
3483
|
+
|
|
3484
|
+
if (!context) {
|
|
3485
|
+
throw new Error("Question components must be used within Question");
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
return context;
|
|
3489
|
+
};
|
|
3490
|
+
|
|
3491
|
+
export type QuestionProps = Omit<ComponentProps<"form">, "defaultValue" | "onSubmit" | "value"> & {
|
|
3492
|
+
defaultValue?: QuestionValue;
|
|
3493
|
+
disabled?: boolean;
|
|
3494
|
+
onSubmit?: (
|
|
3495
|
+
response: QuestionResponse,
|
|
3496
|
+
event: FormEvent<HTMLFormElement>,
|
|
3497
|
+
) => void | Promise<void>;
|
|
3498
|
+
onValueChange?: (value: QuestionValue) => void;
|
|
3499
|
+
selectionMode?: SelectionMode;
|
|
3500
|
+
value?: QuestionValue;
|
|
3501
|
+
};
|
|
3502
|
+
|
|
3503
|
+
const EMPTY_VALUE: QuestionValue = { selectedValues: [], text: "" };
|
|
3504
|
+
|
|
3505
|
+
const getSelectedValues = (
|
|
3506
|
+
currentValues: readonly string[],
|
|
3507
|
+
optionValue: string,
|
|
3508
|
+
selectionMode: SelectionMode,
|
|
3509
|
+
): readonly string[] => {
|
|
3510
|
+
const isSelected = currentValues.includes(optionValue);
|
|
3511
|
+
|
|
3512
|
+
if (selectionMode === "single") {
|
|
3513
|
+
return isSelected ? [] : [optionValue];
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
if (isSelected) {
|
|
3517
|
+
return currentValues.filter((item) => item !== optionValue);
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
return [...currentValues, optionValue];
|
|
3521
|
+
};
|
|
3522
|
+
|
|
3523
|
+
export const Question = ({
|
|
3524
|
+
children,
|
|
3525
|
+
className,
|
|
3526
|
+
defaultValue = EMPTY_VALUE,
|
|
3527
|
+
disabled = false,
|
|
3528
|
+
onSubmit,
|
|
3529
|
+
onValueChange,
|
|
3530
|
+
selectionMode = "single",
|
|
3531
|
+
value: controlledValue,
|
|
3532
|
+
...props
|
|
3533
|
+
}: QuestionProps) => {
|
|
3534
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
3535
|
+
const value = controlledValue ?? internalValue;
|
|
3536
|
+
|
|
3537
|
+
const setValue = useCallback(
|
|
3538
|
+
(nextValue: QuestionValue) => {
|
|
3539
|
+
if (controlledValue === undefined) {
|
|
3540
|
+
setInternalValue(nextValue);
|
|
3541
|
+
}
|
|
3542
|
+
onValueChange?.(nextValue);
|
|
3543
|
+
},
|
|
3544
|
+
[controlledValue, onValueChange],
|
|
3545
|
+
);
|
|
3546
|
+
|
|
3547
|
+
const setText = useCallback(
|
|
3548
|
+
(text: string) => {
|
|
3549
|
+
setValue({ ...value, text });
|
|
3550
|
+
},
|
|
3551
|
+
[setValue, value],
|
|
3552
|
+
);
|
|
3553
|
+
|
|
3554
|
+
const toggleValue = useCallback(
|
|
3555
|
+
(optionValue: string) => {
|
|
3556
|
+
const selectedValues = getSelectedValues(value.selectedValues, optionValue, selectionMode);
|
|
3557
|
+
setValue({ ...value, selectedValues });
|
|
3558
|
+
},
|
|
3559
|
+
[selectionMode, setValue, value],
|
|
3560
|
+
);
|
|
3561
|
+
|
|
3562
|
+
const contextValue = useMemo(
|
|
3563
|
+
() => ({
|
|
3564
|
+
disabled,
|
|
3565
|
+
selectedValues: value.selectedValues,
|
|
3566
|
+
selectionMode,
|
|
3567
|
+
setText,
|
|
3568
|
+
text: value.text,
|
|
3569
|
+
toggleValue,
|
|
3570
|
+
}),
|
|
3571
|
+
[disabled, selectionMode, setText, toggleValue, value],
|
|
3572
|
+
);
|
|
3573
|
+
|
|
3574
|
+
const handleSubmit = useCallback(
|
|
3575
|
+
async (event: FormEvent<HTMLFormElement>) => {
|
|
3576
|
+
event.preventDefault();
|
|
3577
|
+
if (disabled) {
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
const text = value.text.trim();
|
|
3582
|
+
if (value.selectedValues.length === 0 && text.length === 0) {
|
|
3583
|
+
return;
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
await onSubmit?.(
|
|
3587
|
+
{
|
|
3588
|
+
selectedValues: value.selectedValues,
|
|
3589
|
+
text: text.length > 0 ? text : undefined,
|
|
3590
|
+
},
|
|
3591
|
+
event,
|
|
3592
|
+
);
|
|
3593
|
+
},
|
|
3594
|
+
[disabled, onSubmit, value],
|
|
3595
|
+
);
|
|
3596
|
+
|
|
3597
|
+
return (
|
|
3598
|
+
<QuestionContext.Provider value={contextValue}>
|
|
3599
|
+
<form
|
|
3600
|
+
className={cn("space-y-4 rounded-lg border bg-background p-4", className)}
|
|
3601
|
+
onSubmit={handleSubmit}
|
|
3602
|
+
{...props}
|
|
3603
|
+
>
|
|
3604
|
+
{children}
|
|
3605
|
+
</form>
|
|
3606
|
+
</QuestionContext.Provider>
|
|
3607
|
+
);
|
|
3608
|
+
};
|
|
3609
|
+
|
|
3610
|
+
export type QuestionPromptProps = HTMLAttributes<HTMLParagraphElement>;
|
|
3611
|
+
|
|
3612
|
+
export const QuestionPrompt = ({ className, ...props }: QuestionPromptProps) => (
|
|
3613
|
+
<p className={cn("font-medium text-sm", className)} {...props} />
|
|
3614
|
+
);
|
|
3615
|
+
|
|
3616
|
+
export type QuestionDescriptionProps = HTMLAttributes<HTMLParagraphElement>;
|
|
3617
|
+
|
|
3618
|
+
export const QuestionDescription = ({ className, ...props }: QuestionDescriptionProps) => (
|
|
3619
|
+
<p className={cn("text-muted-foreground text-sm", className)} {...props} />
|
|
3620
|
+
);
|
|
3621
|
+
|
|
3622
|
+
export type QuestionOptionsProps = HTMLAttributes<HTMLDivElement>;
|
|
3623
|
+
|
|
3624
|
+
export const QuestionOptions = ({ className, ...props }: QuestionOptionsProps) => {
|
|
3625
|
+
const { selectionMode } = useQuestion();
|
|
3626
|
+
|
|
3627
|
+
return (
|
|
3628
|
+
<div
|
|
3629
|
+
className={cn("flex flex-wrap gap-2", className)}
|
|
3630
|
+
role={selectionMode === "single" ? "radiogroup" : "group"}
|
|
3631
|
+
{...props}
|
|
3632
|
+
/>
|
|
3633
|
+
);
|
|
3634
|
+
};
|
|
3635
|
+
|
|
3636
|
+
export type QuestionOptionProps = Omit<ComponentProps<typeof Button>, "value"> & {
|
|
3637
|
+
value: string;
|
|
3638
|
+
};
|
|
3639
|
+
|
|
3640
|
+
export const QuestionOption = ({
|
|
3641
|
+
children,
|
|
3642
|
+
className,
|
|
3643
|
+
disabled,
|
|
3644
|
+
onClick,
|
|
3645
|
+
value,
|
|
3646
|
+
variant,
|
|
3647
|
+
...props
|
|
3648
|
+
}: QuestionOptionProps) => {
|
|
3649
|
+
const question = useQuestion();
|
|
3650
|
+
const isSelected = question.selectedValues.includes(value);
|
|
3651
|
+
const role = question.selectionMode === "single" ? "radio" : "checkbox";
|
|
3652
|
+
const handleClick = useCallback(
|
|
3653
|
+
(event: MouseEvent<HTMLButtonElement>) => {
|
|
3654
|
+
question.toggleValue(value);
|
|
3655
|
+
onClick?.(event);
|
|
3656
|
+
},
|
|
3657
|
+
[onClick, question, value],
|
|
3658
|
+
);
|
|
3659
|
+
|
|
3660
|
+
return (
|
|
3661
|
+
<Button
|
|
3662
|
+
aria-checked={isSelected}
|
|
3663
|
+
className={cn("h-auto whitespace-normal", className)}
|
|
3664
|
+
disabled={question.disabled || disabled}
|
|
3665
|
+
onClick={handleClick}
|
|
3666
|
+
role={role}
|
|
3667
|
+
type="button"
|
|
3668
|
+
variant={variant ?? (isSelected ? "default" : "outline")}
|
|
3669
|
+
{...props}
|
|
3670
|
+
>
|
|
3671
|
+
{children ?? value}
|
|
3672
|
+
</Button>
|
|
3673
|
+
);
|
|
3674
|
+
};
|
|
3675
|
+
|
|
3676
|
+
export type QuestionInputProps = Omit<ComponentProps<typeof Textarea>, "defaultValue" | "value">;
|
|
3677
|
+
|
|
3678
|
+
export const QuestionInput = ({ className, disabled, onChange, ...props }: QuestionInputProps) => {
|
|
3679
|
+
const question = useQuestion();
|
|
3680
|
+
const handleChange = useCallback(
|
|
3681
|
+
(event: ChangeEvent<HTMLTextAreaElement>) => {
|
|
3682
|
+
question.setText(event.currentTarget.value);
|
|
3683
|
+
onChange?.(event);
|
|
3684
|
+
},
|
|
3685
|
+
[onChange, question],
|
|
3686
|
+
);
|
|
3687
|
+
|
|
3688
|
+
return (
|
|
3689
|
+
<Textarea
|
|
3690
|
+
className={cn("min-h-20", className)}
|
|
3691
|
+
disabled={question.disabled || disabled}
|
|
3692
|
+
onChange={handleChange}
|
|
3693
|
+
value={question.text}
|
|
3694
|
+
{...props}
|
|
3695
|
+
/>
|
|
3696
|
+
);
|
|
3697
|
+
};
|
|
3698
|
+
|
|
3699
|
+
export type QuestionActionsProps = HTMLAttributes<HTMLDivElement>;
|
|
3700
|
+
|
|
3701
|
+
export const QuestionActions = ({ className, ...props }: QuestionActionsProps) => (
|
|
3702
|
+
<div className={cn("flex items-center justify-end gap-2", className)} {...props} />
|
|
3703
|
+
);
|
|
3704
|
+
|
|
3705
|
+
export type QuestionSubmitProps = ComponentProps<typeof Button> & {
|
|
3706
|
+
children?: ReactNode;
|
|
3707
|
+
};
|
|
3708
|
+
|
|
3709
|
+
export const QuestionSubmit = ({
|
|
3710
|
+
children = "Submit",
|
|
3711
|
+
disabled,
|
|
3712
|
+
...props
|
|
3713
|
+
}: QuestionSubmitProps) => {
|
|
3714
|
+
const question = useQuestion();
|
|
3715
|
+
const hasResponse = question.selectedValues.length > 0 || question.text.trim().length > 0;
|
|
3716
|
+
|
|
3717
|
+
return (
|
|
3718
|
+
<Button disabled={question.disabled || disabled || !hasResponse} type="submit" {...props}>
|
|
3719
|
+
{children}
|
|
3720
|
+
</Button>
|
|
3721
|
+
);
|
|
3722
|
+
};
|
|
3152
3723
|
`,"components/ai-elements/reasoning.tsx":`"use client";
|
|
3153
3724
|
|
|
3154
3725
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
@@ -4942,6 +5513,36 @@ const betterAuthSession: AuthFn<Request> = async (request) => {
|
|
|
4942
5513
|
export default eveChannel({
|
|
4943
5514
|
auth: [betterAuthSession, vercelOidc(), localDev()],
|
|
4944
5515
|
});
|
|
5516
|
+
`,"app/_components/authenticated-agent-chat.tsx":`import { headers } from "next/headers";
|
|
5517
|
+
import { auth } from "@/lib/auth";
|
|
5518
|
+
import { AgentChat } from "./agent-chat";
|
|
5519
|
+
import { AccountControl, SignIn } from "./web-chat-auth";
|
|
5520
|
+
|
|
5521
|
+
export async function AuthenticatedAgentChat({
|
|
5522
|
+
sessionId,
|
|
5523
|
+
sessionless,
|
|
5524
|
+
}: {
|
|
5525
|
+
readonly sessionId?: string;
|
|
5526
|
+
readonly sessionless?: boolean;
|
|
5527
|
+
}) {
|
|
5528
|
+
if (process.env.NODE_ENV === "development") {
|
|
5529
|
+
return <AgentChat sessionId={sessionId} sessionless={sessionless} />;
|
|
5530
|
+
}
|
|
5531
|
+
|
|
5532
|
+
const session = await auth.api.getSession({ headers: await headers() });
|
|
5533
|
+
if (!session) return <SignIn />;
|
|
5534
|
+
|
|
5535
|
+
return (
|
|
5536
|
+
<>
|
|
5537
|
+
<AgentChat sessionId={sessionId} sessionless={sessionless} />
|
|
5538
|
+
<AccountControl
|
|
5539
|
+
email={session.user.email}
|
|
5540
|
+
image={session.user.image}
|
|
5541
|
+
name={session.user.name}
|
|
5542
|
+
/>
|
|
5543
|
+
</>
|
|
5544
|
+
);
|
|
5545
|
+
}
|
|
4945
5546
|
`,"app/_components/web-chat-auth.tsx":`"use client";
|
|
4946
5547
|
|
|
4947
5548
|
import { LogOutIcon } from "lucide-react";
|
|
@@ -5154,31 +5755,25 @@ export default function RootLayout({ children }: { readonly children: ReactNode
|
|
|
5154
5755
|
</html>
|
|
5155
5756
|
);
|
|
5156
5757
|
}
|
|
5157
|
-
`,"app/page.tsx":`import {
|
|
5158
|
-
import { AgentChat } from "./_components/agent-chat";
|
|
5159
|
-
import { AccountControl, SignIn } from "./_components/web-chat-auth";
|
|
5160
|
-
import { auth } from "@/lib/auth";
|
|
5758
|
+
`,"app/page.tsx":`import { AuthenticatedAgentChat } from "./_components/authenticated-agent-chat";
|
|
5161
5759
|
|
|
5162
|
-
export default
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5760
|
+
export default function Page() {
|
|
5761
|
+
return <AuthenticatedAgentChat />;
|
|
5762
|
+
}
|
|
5763
|
+
`,"app/s/[sessionId]/page.tsx":`import { AuthenticatedAgentChat } from "@/app/_components/authenticated-agent-chat";
|
|
5166
5764
|
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
}
|
|
5765
|
+
export default async function SessionPage({
|
|
5766
|
+
params,
|
|
5767
|
+
}: {
|
|
5768
|
+
readonly params: Promise<{ readonly sessionId: string }>;
|
|
5769
|
+
}) {
|
|
5770
|
+
const { sessionId } = await params;
|
|
5771
|
+
return <AuthenticatedAgentChat sessionId={sessionId} />;
|
|
5772
|
+
}
|
|
5773
|
+
`,"app/s/page.tsx":`import { AuthenticatedAgentChat } from "@/app/_components/authenticated-agent-chat";
|
|
5171
5774
|
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
<AgentChat />
|
|
5175
|
-
<AccountControl
|
|
5176
|
-
email={session.user.email}
|
|
5177
|
-
image={session.user.image}
|
|
5178
|
-
name={session.user.name}
|
|
5179
|
-
/>
|
|
5180
|
-
</>
|
|
5181
|
-
);
|
|
5775
|
+
export default function NewSessionPage() {
|
|
5776
|
+
return <AuthenticatedAgentChat sessionless />;
|
|
5182
5777
|
}
|
|
5183
5778
|
`,"lib/auth-client.ts":`"use client";
|
|
5184
5779
|
|