eve 0.44.0 → 0.44.2
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 +18 -0
- 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/dev/tui/setup-commands.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/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/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 +2 -0
- package/dist/src/harness/instrumentation/runtime-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/eve.js +3 -3
- 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 +9 -4
- package/dist/src/setup/scaffold/create/web-template.js +296 -73
- 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/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/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/tools/overview.mdx +5 -4
- 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">
|
|
@@ -265,6 +344,9 @@ export function AgentMessage({
|
|
|
265
344
|
(last, part, index) => (part.type === "text" ? index : last),
|
|
266
345
|
-1,
|
|
267
346
|
);
|
|
347
|
+
const hasAssistantText =
|
|
348
|
+
message.role === "assistant" &&
|
|
349
|
+
message.parts.some((part) => part.type === "text" && part.text.length > 0);
|
|
268
350
|
|
|
269
351
|
return (
|
|
270
352
|
<Message
|
|
@@ -272,15 +354,17 @@ export function AgentMessage({
|
|
|
272
354
|
from={message.role}
|
|
273
355
|
>
|
|
274
356
|
<MessageContent>
|
|
275
|
-
{message.parts.map((part, index) =>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
+
)}
|
|
284
368
|
</MessageContent>
|
|
285
369
|
</Message>
|
|
286
370
|
);
|
|
@@ -759,6 +843,21 @@ export default function RootLayout({ children }: { readonly children: ReactNode
|
|
|
759
843
|
export default function Page() {
|
|
760
844
|
return <AgentChat />;
|
|
761
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
|
+
}
|
|
762
861
|
`,"components/ai-elements/chain-of-thought.tsx":`"use client";
|
|
763
862
|
|
|
764
863
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
@@ -1485,21 +1584,116 @@ import { cn } from "@/lib/utils";
|
|
|
1485
1584
|
import type { UIMessage } from "ai";
|
|
1486
1585
|
import { ArrowDownIcon, DownloadIcon } from "lucide-react";
|
|
1487
1586
|
import type { ComponentProps } from "react";
|
|
1488
|
-
import { useCallback } from "react";
|
|
1587
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
1489
1588
|
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
1490
1589
|
|
|
1491
|
-
export type ConversationProps = ComponentProps<typeof StickToBottom
|
|
1590
|
+
export type ConversationProps = ComponentProps<typeof StickToBottom> & {
|
|
1591
|
+
scrollRestorationKey?: string;
|
|
1592
|
+
};
|
|
1492
1593
|
|
|
1493
|
-
export const Conversation = ({
|
|
1594
|
+
export const Conversation = ({
|
|
1595
|
+
children,
|
|
1596
|
+
className,
|
|
1597
|
+
initial,
|
|
1598
|
+
scrollRestorationKey,
|
|
1599
|
+
...props
|
|
1600
|
+
}: ConversationProps) => (
|
|
1494
1601
|
<StickToBottom
|
|
1495
1602
|
className={cn("relative flex-1 overflow-y-hidden", className)}
|
|
1496
|
-
initial="smooth"
|
|
1603
|
+
initial={initial ?? (scrollRestorationKey === undefined ? "smooth" : false)}
|
|
1497
1604
|
resize="smooth"
|
|
1498
1605
|
role="log"
|
|
1499
1606
|
{...props}
|
|
1500
|
-
|
|
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>
|
|
1501
1626
|
);
|
|
1502
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
|
+
|
|
1503
1697
|
export type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
|
|
1504
1698
|
|
|
1505
1699
|
export const ConversationContent = ({ className, ...props }: ConversationContentProps) => (
|
|
@@ -1546,16 +1740,21 @@ export const ConversationScrollButton = ({
|
|
|
1546
1740
|
...props
|
|
1547
1741
|
}: ConversationScrollButtonProps) => {
|
|
1548
1742
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
1743
|
+
const [isReady, setIsReady] = useState(false);
|
|
1744
|
+
|
|
1745
|
+
useEffect(() => setIsReady(true), []);
|
|
1549
1746
|
|
|
1550
1747
|
const handleScrollToBottom = useCallback(() => {
|
|
1551
1748
|
scrollToBottom();
|
|
1552
1749
|
}, [scrollToBottom]);
|
|
1553
1750
|
|
|
1554
1751
|
return (
|
|
1752
|
+
isReady &&
|
|
1555
1753
|
!isAtBottom && (
|
|
1556
1754
|
<Button
|
|
1755
|
+
aria-label="Scroll to bottom"
|
|
1557
1756
|
className={cn(
|
|
1558
|
-
"absolute bottom-
|
|
1757
|
+
"absolute bottom-32 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
|
|
1559
1758
|
className,
|
|
1560
1759
|
)}
|
|
1561
1760
|
onClick={handleScrollToBottom}
|
|
@@ -5314,6 +5513,36 @@ const betterAuthSession: AuthFn<Request> = async (request) => {
|
|
|
5314
5513
|
export default eveChannel({
|
|
5315
5514
|
auth: [betterAuthSession, vercelOidc(), localDev()],
|
|
5316
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
|
+
}
|
|
5317
5546
|
`,"app/_components/web-chat-auth.tsx":`"use client";
|
|
5318
5547
|
|
|
5319
5548
|
import { LogOutIcon } from "lucide-react";
|
|
@@ -5526,31 +5755,25 @@ export default function RootLayout({ children }: { readonly children: ReactNode
|
|
|
5526
5755
|
</html>
|
|
5527
5756
|
);
|
|
5528
5757
|
}
|
|
5529
|
-
`,"app/page.tsx":`import {
|
|
5530
|
-
import { AgentChat } from "./_components/agent-chat";
|
|
5531
|
-
import { AccountControl, SignIn } from "./_components/web-chat-auth";
|
|
5532
|
-
import { auth } from "@/lib/auth";
|
|
5758
|
+
`,"app/page.tsx":`import { AuthenticatedAgentChat } from "./_components/authenticated-agent-chat";
|
|
5533
5759
|
|
|
5534
|
-
export default
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5760
|
+
export default function Page() {
|
|
5761
|
+
return <AuthenticatedAgentChat />;
|
|
5762
|
+
}
|
|
5763
|
+
`,"app/s/[sessionId]/page.tsx":`import { AuthenticatedAgentChat } from "@/app/_components/authenticated-agent-chat";
|
|
5538
5764
|
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
}
|
|
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";
|
|
5543
5774
|
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
<AgentChat />
|
|
5547
|
-
<AccountControl
|
|
5548
|
-
email={session.user.email}
|
|
5549
|
-
image={session.user.image}
|
|
5550
|
-
name={session.user.name}
|
|
5551
|
-
/>
|
|
5552
|
-
</>
|
|
5553
|
-
);
|
|
5775
|
+
export default function NewSessionPage() {
|
|
5776
|
+
return <AuthenticatedAgentChat sessionless />;
|
|
5554
5777
|
}
|
|
5555
5778
|
`,"lib/auth-client.ts":`"use client";
|
|
5556
5779
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Approval } from "#public/definitions/approval.js";
|
|
2
2
|
import type { JsonObject } from "#shared/json.js";
|
|
3
|
+
export type DurableDynamicCallbackPhase = "approvalRequest" | "approvalResponse" | "execute" | "toModelOutput";
|
|
4
|
+
export type DurableDynamicCallbackFn = (closure: JsonObject, ...args: never[]) => unknown;
|
|
5
|
+
/**
|
|
6
|
+
* Persisted binding for one callback phase. Identity is
|
|
7
|
+
* `(toolName, phase)` — carried by the surrounding metadata — so only the
|
|
8
|
+
* snapshotted closure values persist.
|
|
9
|
+
*/
|
|
3
10
|
export interface DurableDynamicCallbackReference {
|
|
4
|
-
readonly stepId: string;
|
|
5
11
|
readonly closure: JsonObject;
|
|
6
12
|
}
|
|
7
13
|
export interface DurableDynamicToolCallbacks {
|
|
@@ -10,25 +16,43 @@ export interface DurableDynamicToolCallbacks {
|
|
|
10
16
|
readonly approvalResponse?: DurableDynamicCallbackReference;
|
|
11
17
|
readonly toModelOutput?: DurableDynamicCallbackReference;
|
|
12
18
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export declare function registerDurableDynamicCallback<TArgs extends unknown[], TResult>(stepId: string, callback: (closure: JsonObject, ...args: TArgs) => TResult): void;
|
|
17
|
-
/** Marks a live callback with the descriptor needed to reconstruct it after a cold start. */
|
|
18
|
-
export declare function stampDurableDynamicCallback<TCallback extends Callback>(callback: TCallback, reference: DurableDynamicCallbackReference): TCallback;
|
|
19
|
-
/** Creates a live callback backed by the same registered function used during durable replay. */
|
|
20
|
-
export declare function createDurableDynamicCallback<TArgs extends unknown[], TResult>(input: {
|
|
19
|
+
/** Live descriptor stamped on authored callbacks; `callback` never persists. */
|
|
20
|
+
export interface StampedDurableDynamicCallback {
|
|
21
|
+
readonly callback: DurableDynamicCallbackFn;
|
|
21
22
|
readonly closure: JsonObject;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
}
|
|
24
|
+
export type LiveDurableDynamicToolCallbacks = Partial<{
|
|
25
|
+
execute: StampedDurableDynamicCallback;
|
|
26
|
+
approvalRequest: StampedDurableDynamicCallback;
|
|
27
|
+
approvalResponse: StampedDurableDynamicCallback;
|
|
28
|
+
toModelOutput: StampedDurableDynamicCallback;
|
|
29
|
+
}>;
|
|
30
|
+
export declare const DURABLE_DYNAMIC_TOOL_CALLBACKS: unique symbol;
|
|
31
|
+
/**
|
|
32
|
+
* Binds the current implementation of one tool callback phase. Re-resolution
|
|
33
|
+
* replaces the binding, so replay after a redeploy runs the latest code.
|
|
34
|
+
*/
|
|
35
|
+
export declare function registerDurableDynamicCallback(input: {
|
|
36
|
+
readonly callback: DurableDynamicCallbackFn;
|
|
37
|
+
readonly phase: DurableDynamicCallbackPhase;
|
|
38
|
+
readonly toolName: string;
|
|
39
|
+
}): void;
|
|
40
|
+
export declare function lookupDurableDynamicCallback(toolName: string, phase: DurableDynamicCallbackPhase): DurableDynamicCallbackFn | undefined;
|
|
41
|
+
/** Invokes a registered callback with its snapshotted closure and live arguments. */
|
|
42
|
+
export declare function callDurableDynamicCallback(callback: DurableDynamicCallbackFn, closure: JsonObject, ...args: unknown[]): unknown;
|
|
43
|
+
/** True when any persisted callback of these tools has no registered binding. */
|
|
44
|
+
export declare function hasUnregisteredDurableDynamicCallbacks(metadata: readonly {
|
|
45
|
+
callbacks: DurableDynamicToolCallbacks;
|
|
46
|
+
name: string;
|
|
47
|
+
}[]): boolean;
|
|
48
|
+
/** Marks a live callback with the descriptor needed to register it at resolve time. */
|
|
49
|
+
export declare function stampDurableDynamicCallback<TCallback extends (...args: never[]) => unknown>(callback: TCallback, descriptor: StampedDurableDynamicCallback): TCallback;
|
|
50
|
+
export declare function readDurableDynamicCallback(callback: unknown): StampedDurableDynamicCallback | undefined;
|
|
26
51
|
/** Copies phase-specific callback descriptors onto a branded tool definition. */
|
|
27
|
-
export declare function stampDurableDynamicToolCallbacks(definition: object, callbacks:
|
|
52
|
+
export declare function stampDurableDynamicToolCallbacks(definition: object, callbacks: LiveDurableDynamicToolCallbacks): void;
|
|
28
53
|
export declare function collectDurableDynamicToolCallbacks(input: {
|
|
29
54
|
readonly approval?: Approval<never>;
|
|
30
|
-
readonly execute:
|
|
31
|
-
readonly toModelOutput?:
|
|
32
|
-
}):
|
|
33
|
-
export declare function readDurableDynamicToolCallbacks(definition: object):
|
|
34
|
-
export {};
|
|
55
|
+
readonly execute: (...args: never[]) => unknown;
|
|
56
|
+
readonly toModelOutput?: (...args: never[]) => unknown;
|
|
57
|
+
}): LiveDurableDynamicToolCallbacks;
|
|
58
|
+
export declare function readDurableDynamicToolCallbacks(definition: object): LiveDurableDynamicToolCallbacks | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveApprovalPolicy}from"#public/definitions/approval.js";const
|
|
1
|
+
import{resolveApprovalPolicy}from"#public/definitions/approval.js";const STAMPED_CALLBACK=Symbol.for(`eve:durable-dynamic-callback`),DURABLE_DYNAMIC_TOOL_CALLBACKS=Symbol.for(`eve:durable-dynamic-tool-callbacks`),REGISTRY=Symbol.for(`eve:dynamic-tool-callbacks`);function getRegistry(){let e=globalThis,t=e[REGISTRY];if(t!==void 0)return t;let n=new Map;return e[REGISTRY]=n,n}function registerDurableDynamicCallback(e){let t=getRegistry(),n=t.get(e.toolName);n===void 0&&(n=new Map,t.set(e.toolName,n)),n.set(e.phase,e.callback)}function lookupDurableDynamicCallback(e,t){return getRegistry().get(e)?.get(t)}function callDurableDynamicCallback(e,t,...n){return e(t,...n)}function hasUnregisteredDurableDynamicCallbacks(e){return e.some(e=>Object.keys(e.callbacks).some(t=>lookupDurableDynamicCallback(e.name,t)===void 0))}function stampDurableDynamicCallback(e,n){return Object.defineProperty(e,STAMPED_CALLBACK,{configurable:!0,value:n}),e}function readDurableDynamicCallback(e){if(typeof e==`function`)return Reflect.get(e,STAMPED_CALLBACK)}function stampDurableDynamicToolCallbacks(e,t){Object.defineProperty(e,DURABLE_DYNAMIC_TOOL_CALLBACKS,{configurable:!0,value:t})}function collectDurableDynamicToolCallbacks(t){let n=t.approval===void 0?void 0:readDurableDynamicCallback(resolveApprovalPolicy(t.approval)),r=t.approval===void 0||typeof t.approval==`function`?void 0:readDurableDynamicCallback(t.approval.response),i=readDurableDynamicCallback(t.execute),a=readDurableDynamicCallback(t.toModelOutput),o={};return i!==void 0&&(o.execute=i),n!==void 0&&(o.approvalRequest=n),r!==void 0&&(o.approvalResponse=r),a!==void 0&&(o.toModelOutput=a),o}function readDurableDynamicToolCallbacks(e){return e[DURABLE_DYNAMIC_TOOL_CALLBACKS]}export{DURABLE_DYNAMIC_TOOL_CALLBACKS,callDurableDynamicCallback,collectDurableDynamicToolCallbacks,hasUnregisteredDurableDynamicCallbacks,lookupDurableDynamicCallback,readDurableDynamicCallback,readDurableDynamicToolCallbacks,registerDurableDynamicCallback,stampDurableDynamicCallback,stampDurableDynamicToolCallbacks};
|
package/dist/src/svelte/index.js
CHANGED
|
@@ -34,6 +34,8 @@ export interface UseEveAgentReturn<TData> {
|
|
|
34
34
|
readonly error: Error | undefined;
|
|
35
35
|
/** Raw server events received during this session (authoritative stream). */
|
|
36
36
|
readonly events: readonly MessageStreamEvent[];
|
|
37
|
+
/** Replay the attached durable session and follow its in-flight turn, if any. */
|
|
38
|
+
readonly resume: () => Promise<void>;
|
|
37
39
|
/** Clear all state and start a new session. */
|
|
38
40
|
readonly reset: () => void;
|
|
39
41
|
/** Send a message with optional turn settings. */
|
|
@@ -97,6 +99,8 @@ export interface UseEveAgentOptions<TData> extends EveAgentStoreCallbacks<TData>
|
|
|
97
99
|
* which fixes `TData` to {@link EveMessageData}.
|
|
98
100
|
*/
|
|
99
101
|
readonly reducer?: EveAgentReducer<TData>;
|
|
102
|
+
/** Replay the attached durable session after mount. Requires `initialSession` or `session`. */
|
|
103
|
+
readonly resume?: boolean;
|
|
100
104
|
/**
|
|
101
105
|
* Pre-built client session to bind to. When omitted, the binding creates its
|
|
102
106
|
* own session from `auth`, `headers`, and `host`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SessionStateMap } from "#harness/types.js";
|
|
2
2
|
export declare const TASK_DELIVERY_CONTEXT_LABEL = "[Task state]";
|
|
3
|
+
export declare const TASK_DELIVERY_INITIATING_INSTRUCTION = "Background task reporting: launch acknowledgement\nThe accompanying [Task state] system message is runtime-authored and lists background tasks accepted so far from the current turn. They continue independently after this turn.\n\nContinue carrying out the user's request, including starting any remaining background work. When no further tool calls are needed in this turn, send one brief user-facing acknowledgement that the background work has started. Do not wait for results or report results that are not available yet. End the turn after the acknowledgement.";
|
|
3
4
|
export declare const TASK_DELIVERY_PENDING_INSTRUCTION = "Background task control: incomplete cohort\nThis framework-authored instruction overrides any earlier instruction to report, summarize, acknowledge, or otherwise handle background results.\n\nThe accompanying [Task state] message is runtime-authored and lists tasks started by the same parent turn. At least one of those tasks is still pending, so the combined report is not ready.\n\nAction:\n- You may call tools only if the newly delivered task result requires immediate action.\n- Otherwise, take no action.\n\nDelivery:\n- Do not report completed tasks or partial results.\n- Do not provide progress, status, an acknowledgement, or a waiting message.\n- After any necessary tool calls, your entire final text response must be exactly <eve-empty-delivery/> and no other text.\n\nIncorrect: \"Two of three tasks have completed.\"\nIncorrect: \"Still waiting for the remaining task.\"\nCorrect: <eve-empty-delivery/>";
|
|
4
5
|
export declare const TASK_DELIVERY_SETTLED_INSTRUCTION = "Background task reporting\nThis turn was triggered by background task activity. The accompanying [Task state] message is runtime-authored and lists tasks started by the same parent turn, all settled, with every available terminal output. Do not reply with <eve-empty-delivery/>. Send one user-facing response that combines their useful results.";
|
|
5
6
|
/** Returns model context and cohort phase for tasks started by the same parent turn as this delivery. */
|
|
@@ -10,3 +11,11 @@ export declare function resolveTaskDeliveryContext(input: {
|
|
|
10
11
|
readonly context: string;
|
|
11
12
|
readonly phase: "pending" | "settled";
|
|
12
13
|
} | undefined;
|
|
14
|
+
/** Returns model context for durable tasks launched by the active parent turn. */
|
|
15
|
+
export declare function resolveInitiatingTaskContext(input: {
|
|
16
|
+
readonly state: SessionStateMap | undefined;
|
|
17
|
+
readonly turnId: string;
|
|
18
|
+
}): {
|
|
19
|
+
readonly context: string;
|
|
20
|
+
readonly phase: "initiating";
|
|
21
|
+
} | undefined;
|