eve 0.44.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/src/chunks/{use-eve-agent-C0SG1Ttk.js → use-eve-agent-BsLJiIbc.js} +224 -62
  3. package/dist/src/chunks/{use-eve-agent-D71oDil7.js → use-eve-agent-DpUEwS23.js} +222 -62
  4. package/dist/src/cli/commands/extension-init.js +1 -1
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  7. package/dist/src/client/eve-agent-store-helpers.d.ts +12 -0
  8. package/dist/src/client/eve-agent-store-helpers.js +2 -0
  9. package/dist/src/client/eve-agent-store.d.ts +8 -6
  10. package/dist/src/client/eve-agent-store.js +1 -2
  11. package/dist/src/client/ndjson.d.ts +3 -1
  12. package/dist/src/client/ndjson.js +3 -3
  13. package/dist/src/client/open-stream.d.ts +3 -0
  14. package/dist/src/client/open-stream.js +1 -1
  15. package/dist/src/context/build-dynamic-tools.js +1 -1
  16. package/dist/src/context/dynamic-tool-lifecycle.d.ts +5 -1
  17. package/dist/src/context/dynamic-tool-lifecycle.js +1 -1
  18. package/dist/src/context/keys.d.ts +4 -2
  19. package/dist/src/context/keys.js +1 -1
  20. package/dist/src/execution/cancel-descendant-turns-step.js +1 -1
  21. package/dist/src/execution/dispatch-turn-step.js +1 -1
  22. package/dist/src/execution/eve-workflow-attributes.d.ts +3 -0
  23. package/dist/src/execution/eve-workflow-attributes.js +1 -1
  24. package/dist/src/execution/pending-turn-state.d.ts +9 -0
  25. package/dist/src/execution/pending-turn-state.js +1 -0
  26. package/dist/src/execution/settle-cancelled-turn-step.js +1 -1
  27. package/dist/src/execution/tasks/parent/tool-execution.js +1 -1
  28. package/dist/src/execution/turn-workflow.js +1 -1
  29. package/dist/src/execution/workflow-steps.js +1 -1
  30. package/dist/src/harness/channel-delivery-instrumentation.js +1 -1
  31. package/dist/src/harness/instrumentation/content-policy.d.ts +6 -0
  32. package/dist/src/harness/instrumentation/content-policy.js +1 -0
  33. package/dist/src/harness/instrumentation/lifecycle.d.ts +2 -0
  34. package/dist/src/harness/instrumentation/runtime-context.js +1 -1
  35. package/dist/src/harness/runtime-actions.js +1 -1
  36. package/dist/src/harness/tool-loop.js +2 -2
  37. package/dist/src/internal/application/package.js +1 -1
  38. package/dist/src/internal/workflow-bundle/dynamic-remote-agent-transform.js +2 -2
  39. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.d.ts +4 -4
  40. package/dist/src/internal/workflow-bundle/dynamic-tool-transform.js +3 -4
  41. package/dist/src/internal/workflow-bundle/stable-module-id.d.ts +6 -0
  42. package/dist/src/internal/workflow-bundle/stable-module-id.js +1 -0
  43. package/dist/src/packages/eve-catalog/src/index.js +1 -1
  44. package/dist/src/public/tools/approval/approval-helpers.js +1 -1
  45. package/dist/src/react/use-eve-agent.d.ts +10 -1
  46. package/dist/src/react/use-eve-agent.js +1 -1
  47. package/dist/src/runtime/attributes/emit.d.ts +1 -1
  48. package/dist/src/runtime/attributes/normalize.d.ts +1 -1
  49. package/dist/src/runtime/attributes/normalize.js +1 -1
  50. package/dist/src/runtime/framework-tools/connection-search-dynamic.js +2 -2
  51. package/dist/src/runtime/framework-tools/subagent/local.js +1 -1
  52. package/dist/src/runtime/sessions/runtime-context-keys.js +1 -1
  53. package/dist/src/setup/integrations/linq/connect.js +1 -1
  54. package/dist/src/setup/primitives/pm/npm.js +1 -1
  55. package/dist/src/setup/primitives/pm/pnpm.js +5 -9
  56. package/dist/src/setup/primitives/pm/types.d.ts +0 -2
  57. package/dist/src/setup/scaffold/create/project.js +1 -1
  58. package/dist/src/setup/scaffold/create/web-template.d.ts +9 -4
  59. package/dist/src/setup/scaffold/create/web-template.js +296 -73
  60. package/dist/src/shared/durable-dynamic-tool-callbacks.d.ts +43 -19
  61. package/dist/src/shared/durable-dynamic-tool-callbacks.js +1 -1
  62. package/dist/src/svelte/index.js +1 -1
  63. package/dist/src/svelte/use-eve-agent.d.ts +4 -0
  64. package/dist/src/svelte/use-eve-agent.js +1 -1
  65. package/dist/src/tasks/delivery-context.d.ts +9 -0
  66. package/dist/src/tasks/delivery-context.js +5 -2
  67. package/dist/src/vue/index.js +1 -1
  68. package/dist/src/vue/use-eve-agent.d.ts +4 -0
  69. package/dist/src/vue/use-eve-agent.js +1 -1
  70. package/docs/concepts/sessions-runs-and-streaming.md +1 -1
  71. package/docs/guides/dynamic-capabilities.md +10 -2
  72. package/docs/guides/frontend/nextjs.mdx +23 -0
  73. package/docs/guides/frontend/overview.mdx +24 -9
  74. package/docs/guides/frontend/use-eve-agent-svelte.mdx +9 -9
  75. package/docs/guides/frontend/use-eve-agent-vue.mdx +9 -9
  76. package/docs/tools/overview.mdx +5 -4
  77. 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) || isBusy) return;
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={isBusy} placeholder="Send a message…" />
92
- <PromptInputSubmit onStop={requestCancellation} status={agent.status} />
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
- {isEmpty ? null : (
99
- <header className="flex h-14 shrink-0 items-center justify-center pl-4 pr-2">
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
- {isEmpty ? null : (
120
- <Conversation className="min-h-0 flex-1">
121
- <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">
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
- isEmpty
150
- ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"
151
- : "max-w-3xl shrink-0 pb-6",
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
- {isEmpty ? (
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
- ) : null}
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
- <AgentMessagePart
277
- canRespond={canRespond}
278
- key={partKey(part, index)}
279
- onInputResponses={onInputResponses}
280
- part={part}
281
- showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}
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 = ({ className, ...props }: ConversationProps) => (
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-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",
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 { headers } from "next/headers";
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 async function Page() {
5535
- if (process.env.NODE_ENV === "development") {
5536
- return <AgentChat />;
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
- const session = await auth.api.getSession({ headers: await headers() });
5540
- if (!session) {
5541
- return <SignIn />;
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
- return (
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
- export declare const DURABLE_DYNAMIC_TOOL_CALLBACKS: unique symbol;
14
- type Callback = (...args: never[]) => unknown;
15
- /** Registers one module-stable replay entry point. Re-registration replaces the same code-owned id. */
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
- readonly callback: (closure: JsonObject, ...args: TArgs) => TResult;
23
- readonly stepId: string;
24
- }): (...args: TArgs) => TResult;
25
- export declare function readDurableDynamicCallback(callback: unknown): DurableDynamicCallbackReference | undefined;
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: Partial<DurableDynamicToolCallbacks>): void;
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: Callback;
31
- readonly toModelOutput?: Callback;
32
- }): Partial<DurableDynamicToolCallbacks>;
33
- export declare function readDurableDynamicToolCallbacks(definition: object): Partial<DurableDynamicToolCallbacks> | undefined;
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 DURABLE_CALLBACK_REFERENCE=Symbol.for(`eve:durable-dynamic-callback`),DURABLE_DYNAMIC_TOOL_CALLBACKS=Symbol.for(`eve:durable-dynamic-tool-callbacks`),STEP_REGISTRY=Symbol.for(`@workflow/core//registeredSteps`);function getStepRegistry(){let e=globalThis,t=e[STEP_REGISTRY];if(t!==void 0)return t;let n=new Map;return e[STEP_REGISTRY]=n,n}function registerDurableDynamicCallback(e,t){if(e.length===0)throw Error(`A durable dynamic callback step id cannot be empty.`);getStepRegistry().set(e,t)}function stampDurableDynamicCallback(e,n){return Object.defineProperty(e,DURABLE_CALLBACK_REFERENCE,{configurable:!0,value:n}),e}function createDurableDynamicCallback(e){return registerDurableDynamicCallback(e.stepId,e.callback),stampDurableDynamicCallback((...t)=>e.callback(e.closure,...t),{closure:e.closure,stepId:e.stepId})}function readDurableDynamicCallback(e){if(typeof e==`function`)return Reflect.get(e,DURABLE_CALLBACK_REFERENCE)}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,collectDurableDynamicToolCallbacks,createDurableDynamicCallback,readDurableDynamicCallback,readDurableDynamicToolCallbacks,registerDurableDynamicCallback,stampDurableDynamicCallback,stampDurableDynamicToolCallbacks};
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};
@@ -1,3 +1,3 @@
1
- import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-C0SG1Ttk.js";
1
+ import { n as defaultMessageReducer, t as useEveAgent } from "../chunks/use-eve-agent-BsLJiIbc.js";
2
2
 
3
3
  export { defaultMessageReducer, useEveAgent };
@@ -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,3 +1,3 @@
1
- import { t as useEveAgent } from "../chunks/use-eve-agent-C0SG1Ttk.js";
1
+ import { t as useEveAgent } from "../chunks/use-eve-agent-BsLJiIbc.js";
2
2
 
3
3
  export { useEveAgent };
@@ -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;