@alexanderolsen/create-deepagent 0.1.0
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/dist/index.d.ts +2 -0
- package/dist/index.js +661 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/registry/frameworks/deno/.env.example +6 -0
- package/registry/frameworks/deno/README.md +137 -0
- package/registry/frameworks/deno/client/index.html +23 -0
- package/registry/frameworks/deno/client/package.json +30 -0
- package/registry/frameworks/deno/client/public/favicon.ico +0 -0
- package/registry/frameworks/deno/client/src/components/Chat.tsx +124 -0
- package/registry/frameworks/deno/client/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/deno/client/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/deno/client/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/deno/client/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/deno/client/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/deno/client/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/deno/client/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/deno/client/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/deno/client/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/deno/client/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/deno/client/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/deno/client/src/lib/chat/threads-client.ts +51 -0
- package/registry/frameworks/deno/client/src/main.tsx +11 -0
- package/registry/frameworks/deno/client/src/styles/globals.css +714 -0
- package/registry/frameworks/deno/client/src/vite-env.d.ts +1 -0
- package/registry/frameworks/deno/client/tsconfig.app.json +7 -0
- package/registry/frameworks/deno/client/tsconfig.json +24 -0
- package/registry/frameworks/deno/client/tsconfig.node.json +19 -0
- package/registry/frameworks/deno/client/vite.config.ts +24 -0
- package/registry/frameworks/deno/deno.json +16 -0
- package/registry/frameworks/deno/main.ts +23 -0
- package/registry/frameworks/deno/package.json +14 -0
- package/registry/frameworks/deno/server/agent/index.ts +60 -0
- package/registry/frameworks/deno/server/agent/middleware.ts +24 -0
- package/registry/frameworks/deno/server/agent/tools.ts +64 -0
- package/registry/frameworks/deno/server/registry.ts +40 -0
- package/registry/frameworks/deno/server/routes.ts +114 -0
- package/registry/frameworks/deno/server/serialize.ts +30 -0
- package/registry/frameworks/deno/server/session.ts +210 -0
- package/registry/frameworks/deno/server/threads.ts +404 -0
- package/registry/frameworks/deno.ts +17 -0
- package/registry/frameworks/hono/.env.example +6 -0
- package/registry/frameworks/hono/README.md +186 -0
- package/registry/frameworks/hono/index.html +22 -0
- package/registry/frameworks/hono/package.json +42 -0
- package/registry/frameworks/hono/src/components/Chat.tsx +124 -0
- package/registry/frameworks/hono/src/components/ChatApp.tsx +129 -0
- package/registry/frameworks/hono/src/components/Conversation.tsx +90 -0
- package/registry/frameworks/hono/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/hono/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/hono/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/hono/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/hono/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/hono/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/hono/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/hono/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/hono/src/lib/agent/types.ts +4 -0
- package/registry/frameworks/hono/src/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/hono/src/main.tsx +11 -0
- package/registry/frameworks/hono/src/styles/globals.css +714 -0
- package/registry/frameworks/hono/src/vite-env.d.ts +1 -0
- package/registry/frameworks/hono/tsconfig.app.json +22 -0
- package/registry/frameworks/hono/tsconfig.json +7 -0
- package/registry/frameworks/hono/tsconfig.worker.json +18 -0
- package/registry/frameworks/hono/vite.config.ts +16 -0
- package/registry/frameworks/hono/worker/agent/index.ts +53 -0
- package/registry/frameworks/hono/worker/agent/middleware.ts +20 -0
- package/registry/frameworks/hono/worker/agent/tools.ts +55 -0
- package/registry/frameworks/hono/worker/durable-objects/thread-session.ts +159 -0
- package/registry/frameworks/hono/worker/env.d.ts +17 -0
- package/registry/frameworks/hono/worker/index.ts +140 -0
- package/registry/frameworks/hono/worker/server/registry.ts +39 -0
- package/registry/frameworks/hono/worker/server/runs.ts +82 -0
- package/registry/frameworks/hono/worker/server/serialize.ts +30 -0
- package/registry/frameworks/hono/worker/server/threads.ts +404 -0
- package/registry/frameworks/hono/worker/tsconfig.json +4 -0
- package/registry/frameworks/hono/wrangler.jsonc +28 -0
- package/registry/frameworks/hono.ts +35 -0
- package/registry/frameworks/next/.env.example +6 -0
- package/registry/frameworks/next/README.md +173 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/commands/route.ts +21 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/history/route.ts +35 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/route.ts +13 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/state/route.ts +51 -0
- package/registry/frameworks/next/app/api/threads/[threadId]/stream/route.ts +30 -0
- package/registry/frameworks/next/app/api/threads/route.ts +11 -0
- package/registry/frameworks/next/app/favicon.ico +0 -0
- package/registry/frameworks/next/app/globals.css +712 -0
- package/registry/frameworks/next/app/layout.tsx +34 -0
- package/registry/frameworks/next/app/page.tsx +5 -0
- package/registry/frameworks/next/components/Chat.tsx +124 -0
- package/registry/frameworks/next/components/ChatApp.tsx +129 -0
- package/registry/frameworks/next/components/Conversation.tsx +90 -0
- package/registry/frameworks/next/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/next/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/next/components/MessageThread.tsx +135 -0
- package/registry/frameworks/next/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/next/components/Subagents.tsx +120 -0
- package/registry/frameworks/next/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/next/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/next/components/ToolCall.tsx +89 -0
- package/registry/frameworks/next/eslint.config.mjs +18 -0
- package/registry/frameworks/next/lib/agent/index.ts +95 -0
- package/registry/frameworks/next/lib/agent/middleware.ts +40 -0
- package/registry/frameworks/next/lib/agent/tools.ts +66 -0
- package/registry/frameworks/next/lib/chat/threads-client.ts +57 -0
- package/registry/frameworks/next/lib/server/registry.ts +57 -0
- package/registry/frameworks/next/lib/server/serialize.ts +32 -0
- package/registry/frameworks/next/lib/server/session.ts +212 -0
- package/registry/frameworks/next/lib/server/threads.ts +406 -0
- package/registry/frameworks/next/next.config.ts +7 -0
- package/registry/frameworks/next/package.json +37 -0
- package/registry/frameworks/next/postcss.config.mjs +7 -0
- package/registry/frameworks/next/public/file.svg +1 -0
- package/registry/frameworks/next/public/globe.svg +1 -0
- package/registry/frameworks/next/public/next.svg +1 -0
- package/registry/frameworks/next/public/vercel.svg +1 -0
- package/registry/frameworks/next/public/window.svg +1 -0
- package/registry/frameworks/next/tsconfig.json +34 -0
- package/registry/frameworks/next.ts +17 -0
- package/registry/frameworks/nuxt/.env.example +3 -0
- package/registry/frameworks/nuxt/README.md +133 -0
- package/registry/frameworks/nuxt/app/app.vue +26 -0
- package/registry/frameworks/nuxt/app/assets/css/main.css +707 -0
- package/registry/frameworks/nuxt/app/components/Chat.vue +105 -0
- package/registry/frameworks/nuxt/app/components/ChatApp.vue +89 -0
- package/registry/frameworks/nuxt/app/components/ChatThread.vue +27 -0
- package/registry/frameworks/nuxt/app/components/MessageBubble.vue +60 -0
- package/registry/frameworks/nuxt/app/components/MessageBubbles.vue +213 -0
- package/registry/frameworks/nuxt/app/components/MessageList.vue +51 -0
- package/registry/frameworks/nuxt/app/components/MessageReasoning.vue +53 -0
- package/registry/frameworks/nuxt/app/components/StreamingIndicator.vue +9 -0
- package/registry/frameworks/nuxt/app/components/SubagentDetail.vue +51 -0
- package/registry/frameworks/nuxt/app/components/SubagentList.vue +49 -0
- package/registry/frameworks/nuxt/app/components/ThemeToggle.vue +43 -0
- package/registry/frameworks/nuxt/app/components/ThreadHistory.vue +65 -0
- package/registry/frameworks/nuxt/app/components/ToolCall.vue +81 -0
- package/registry/frameworks/nuxt/app/components/TypingDots.vue +14 -0
- package/registry/frameworks/nuxt/app/composables/useTheme.ts +14 -0
- package/registry/frameworks/nuxt/app/utils/streaming.ts +44 -0
- package/registry/frameworks/nuxt/app/utils/threads.ts +57 -0
- package/registry/frameworks/nuxt/nuxt.config.ts +6 -0
- package/registry/frameworks/nuxt/package.json +28 -0
- package/registry/frameworks/nuxt/public/favicon.ico +0 -0
- package/registry/frameworks/nuxt/public/robots.txt +2 -0
- package/registry/frameworks/nuxt/server/agent/index.ts +89 -0
- package/registry/frameworks/nuxt/server/agent/middleware.ts +38 -0
- package/registry/frameworks/nuxt/server/agent/tools.ts +66 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/commands.post.ts +16 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/history.post.ts +37 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/index.delete.ts +12 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.get.ts +22 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.post.ts +32 -0
- package/registry/frameworks/nuxt/server/api/threads/[threadId]/stream.post.ts +24 -0
- package/registry/frameworks/nuxt/server/api/threads/index.get.ts +13 -0
- package/registry/frameworks/nuxt/server/utils/runtime.ts +42 -0
- package/registry/frameworks/nuxt/server/utils/serialize.ts +30 -0
- package/registry/frameworks/nuxt/server/utils/session.ts +210 -0
- package/registry/frameworks/nuxt/server/utils/threads.ts +404 -0
- package/registry/frameworks/nuxt/tsconfig.json +18 -0
- package/registry/frameworks/nuxt.ts +17 -0
- package/registry/frameworks/vite/.env.example +20 -0
- package/registry/frameworks/vite/README.md +149 -0
- package/registry/frameworks/vite/agent/index.ts +59 -0
- package/registry/frameworks/vite/agent/middleware.ts +24 -0
- package/registry/frameworks/vite/agent/tools.ts +64 -0
- package/registry/frameworks/vite/index.html +23 -0
- package/registry/frameworks/vite/langgraph.json +16 -0
- package/registry/frameworks/vite/package.json +39 -0
- package/registry/frameworks/vite/public/favicon.ico +0 -0
- package/registry/frameworks/vite/scripts/vite-langgraph-proxy.ts +34 -0
- package/registry/frameworks/vite/src/components/Chat.tsx +124 -0
- package/registry/frameworks/vite/src/components/ChatApp.tsx +122 -0
- package/registry/frameworks/vite/src/components/Conversation.tsx +91 -0
- package/registry/frameworks/vite/src/components/MessageBubbles.tsx +88 -0
- package/registry/frameworks/vite/src/components/MessageReasoning.tsx +71 -0
- package/registry/frameworks/vite/src/components/MessageThread.tsx +135 -0
- package/registry/frameworks/vite/src/components/StreamingIndicator.tsx +36 -0
- package/registry/frameworks/vite/src/components/Subagents.tsx +120 -0
- package/registry/frameworks/vite/src/components/ThemeIcons.tsx +31 -0
- package/registry/frameworks/vite/src/components/ThreadHistory.tsx +73 -0
- package/registry/frameworks/vite/src/components/ToolCall.tsx +89 -0
- package/registry/frameworks/vite/src/lib/agent-type.ts +4 -0
- package/registry/frameworks/vite/src/lib/chat/threads-client.ts +114 -0
- package/registry/frameworks/vite/src/main.tsx +11 -0
- package/registry/frameworks/vite/src/styles/globals.css +714 -0
- package/registry/frameworks/vite/src/vite-env.d.ts +11 -0
- package/registry/frameworks/vite/tsconfig.app.json +24 -0
- package/registry/frameworks/vite/tsconfig.json +7 -0
- package/registry/frameworks/vite/tsconfig.node.json +21 -0
- package/registry/frameworks/vite/vercel.json +3 -0
- package/registry/frameworks/vite/vite.config.ts +24 -0
- package/registry/frameworks/vite.ts +17 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
|
|
5
|
+
import type { BaseMessage } from "@langchain/core/messages";
|
|
6
|
+
import type { SubagentDiscoverySnapshot } from "@langchain/langgraph-sdk/stream";
|
|
7
|
+
import { useMessages, useStreamContext } from "@langchain/react";
|
|
8
|
+
|
|
9
|
+
import type { Agent } from "@/lib/agent/types";
|
|
10
|
+
import { MessageThread } from "./MessageThread";
|
|
11
|
+
import { StreamingIndicator } from "./StreamingIndicator";
|
|
12
|
+
|
|
13
|
+
export type SubagentStatus = SubagentDiscoverySnapshot["status"];
|
|
14
|
+
|
|
15
|
+
/** Lightweight model for a subagent card, derived from a `task` tool call. */
|
|
16
|
+
export type SubagentCard = {
|
|
17
|
+
/** The `task` tool-call id — also the subagent discovery key. */
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
task?: string;
|
|
21
|
+
status: SubagentStatus;
|
|
22
|
+
/** Whether a discovery snapshot exists yet (i.e. the card can be opened). */
|
|
23
|
+
openable: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function statusLabel(status: SubagentStatus) {
|
|
27
|
+
if (status === "running") return "Running";
|
|
28
|
+
if (status === "complete") return "Complete";
|
|
29
|
+
return "Error";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The task prompt is shown separately; skip the matching human message. */
|
|
33
|
+
function omitTaskHumanMessage(
|
|
34
|
+
messages: BaseMessage[],
|
|
35
|
+
taskInput?: string,
|
|
36
|
+
): BaseMessage[] {
|
|
37
|
+
const task = taskInput?.trim();
|
|
38
|
+
if (!task) return messages;
|
|
39
|
+
return messages.filter(
|
|
40
|
+
(message) => message.type !== "human" || message.text?.trim() !== task,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compact, clickable subagent cards showing only the name and task prompt.
|
|
46
|
+
* Rendered inline where the coordinator spawned the subagents. Selecting one
|
|
47
|
+
* drills into its dedicated chat view.
|
|
48
|
+
*/
|
|
49
|
+
export function SubagentList({
|
|
50
|
+
cards,
|
|
51
|
+
onOpen,
|
|
52
|
+
}: {
|
|
53
|
+
cards: SubagentCard[];
|
|
54
|
+
onOpen: (id: string) => void;
|
|
55
|
+
}) {
|
|
56
|
+
if (cards.length === 0) return null;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div aria-label="Subagents" className="subagent-list">
|
|
60
|
+
{cards.map((card) => (
|
|
61
|
+
<button
|
|
62
|
+
className="subagent-chip"
|
|
63
|
+
disabled={!card.openable}
|
|
64
|
+
key={card.id}
|
|
65
|
+
onClick={() => card.openable && onOpen(card.id)}
|
|
66
|
+
type="button"
|
|
67
|
+
>
|
|
68
|
+
<span className="subagent-chip-head">
|
|
69
|
+
<span className="subagent-chip-name">{card.name}</span>
|
|
70
|
+
<span className={`subagent-status status-${card.status}`}>
|
|
71
|
+
{statusLabel(card.status)}
|
|
72
|
+
</span>
|
|
73
|
+
</span>
|
|
74
|
+
{card.task ? (
|
|
75
|
+
<span className="subagent-chip-task">{card.task}</span>
|
|
76
|
+
) : null}
|
|
77
|
+
</button>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The chat interface for a single subagent.
|
|
85
|
+
*
|
|
86
|
+
* `useMessages` is scoped to the subagent's namespace, so its tokens, tool
|
|
87
|
+
* calls, and results stream independently from the root conversation.
|
|
88
|
+
*/
|
|
89
|
+
export function SubagentDetail({
|
|
90
|
+
snapshot,
|
|
91
|
+
}: {
|
|
92
|
+
snapshot: SubagentDiscoverySnapshot;
|
|
93
|
+
}) {
|
|
94
|
+
const stream = useStreamContext<Agent>();
|
|
95
|
+
const messages = useMessages(stream, snapshot);
|
|
96
|
+
const visibleMessages = useMemo(
|
|
97
|
+
() => omitTaskHumanMessage(messages, snapshot.taskInput),
|
|
98
|
+
[messages, snapshot.taskInput],
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<>
|
|
103
|
+
{snapshot.taskInput ? (
|
|
104
|
+
<div className="subagent-prompt">
|
|
105
|
+
<span>Task</span>
|
|
106
|
+
<p>{snapshot.taskInput}</p>
|
|
107
|
+
</div>
|
|
108
|
+
) : null}
|
|
109
|
+
|
|
110
|
+
<MessageThread
|
|
111
|
+
isLoading={snapshot.status === "running"}
|
|
112
|
+
messages={visibleMessages}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
{snapshot.status === "running" && visibleMessages.length === 0 ? (
|
|
116
|
+
<StreamingIndicator />
|
|
117
|
+
) : null}
|
|
118
|
+
</>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function SunIcon() {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
aria-hidden
|
|
5
|
+
fill="none"
|
|
6
|
+
stroke="currentColor"
|
|
7
|
+
strokeLinecap="round"
|
|
8
|
+
strokeWidth="1.5"
|
|
9
|
+
viewBox="0 0 24 24"
|
|
10
|
+
>
|
|
11
|
+
<circle cx="12" cy="12" r="4" />
|
|
12
|
+
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function MoonIcon() {
|
|
18
|
+
return (
|
|
19
|
+
<svg
|
|
20
|
+
aria-hidden
|
|
21
|
+
fill="none"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
strokeLinecap="round"
|
|
24
|
+
strokeLinejoin="round"
|
|
25
|
+
strokeWidth="1.5"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
>
|
|
28
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
29
|
+
</svg>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ThreadSummary } from "@/lib/chat/threads-client";
|
|
4
|
+
|
|
5
|
+
function formatTime(updatedAt: string | null) {
|
|
6
|
+
if (!updatedAt) return "";
|
|
7
|
+
const date = new Date(updatedAt);
|
|
8
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
9
|
+
return date.toLocaleString(undefined, {
|
|
10
|
+
month: "short",
|
|
11
|
+
day: "numeric",
|
|
12
|
+
hour: "numeric",
|
|
13
|
+
minute: "2-digit",
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function ThreadHistory({
|
|
18
|
+
threads,
|
|
19
|
+
activeThreadId,
|
|
20
|
+
onSelect,
|
|
21
|
+
onCreate,
|
|
22
|
+
onDelete,
|
|
23
|
+
}: {
|
|
24
|
+
threads: ThreadSummary[];
|
|
25
|
+
activeThreadId: string;
|
|
26
|
+
onSelect: (threadId: string) => void;
|
|
27
|
+
onCreate: () => void;
|
|
28
|
+
onDelete: (threadId: string) => void;
|
|
29
|
+
}) {
|
|
30
|
+
return (
|
|
31
|
+
<aside aria-label="Thread history" className="sidebar">
|
|
32
|
+
<div className="sidebar-head">
|
|
33
|
+
<span className="eyebrow">History</span>
|
|
34
|
+
<button className="new-thread" onClick={onCreate} type="button">
|
|
35
|
+
+ New
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<ul className="thread-list">
|
|
40
|
+
{threads.length === 0 ? (
|
|
41
|
+
<li className="thread-empty">No conversations yet.</li>
|
|
42
|
+
) : null}
|
|
43
|
+
{threads.map((thread) => (
|
|
44
|
+
<li
|
|
45
|
+
className={`thread-item ${
|
|
46
|
+
thread.id === activeThreadId ? "active" : ""
|
|
47
|
+
}`}
|
|
48
|
+
key={thread.id}
|
|
49
|
+
>
|
|
50
|
+
<button
|
|
51
|
+
className="thread-open"
|
|
52
|
+
onClick={() => onSelect(thread.id)}
|
|
53
|
+
type="button"
|
|
54
|
+
>
|
|
55
|
+
<span className="thread-title">{thread.title}</span>
|
|
56
|
+
<span className="thread-time">
|
|
57
|
+
{formatTime(thread.updatedAt)}
|
|
58
|
+
</span>
|
|
59
|
+
</button>
|
|
60
|
+
<button
|
|
61
|
+
aria-label="Delete conversation"
|
|
62
|
+
className="thread-delete"
|
|
63
|
+
onClick={() => onDelete(thread.id)}
|
|
64
|
+
type="button"
|
|
65
|
+
>
|
|
66
|
+
×
|
|
67
|
+
</button>
|
|
68
|
+
</li>
|
|
69
|
+
))}
|
|
70
|
+
</ul>
|
|
71
|
+
</aside>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
export type ToolCallStatus = "running" | "complete" | "error";
|
|
6
|
+
|
|
7
|
+
export type ToolCallView = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
args: Record<string, unknown>;
|
|
11
|
+
output?: string;
|
|
12
|
+
status: ToolCallStatus;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function stringifyArgs(args: Record<string, unknown>) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.stringify(args, null, 2);
|
|
18
|
+
} catch {
|
|
19
|
+
return String(args);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function statusLabel(status: ToolCallStatus) {
|
|
24
|
+
if (status === "running") return "Running";
|
|
25
|
+
if (status === "error") return "Error";
|
|
26
|
+
return "Done";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ToolIcon() {
|
|
30
|
+
return (
|
|
31
|
+
<svg
|
|
32
|
+
aria-hidden
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
strokeLinecap="round"
|
|
36
|
+
strokeLinejoin="round"
|
|
37
|
+
strokeWidth="1.6"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
>
|
|
40
|
+
<path d="M14.7 6.3a4 4 0 0 1-5.4 5.4L4 17v3h3l5.3-5.3a4 4 0 0 1 5.4-5.4l-2.7 2.7-1.4-1.4 2.7-2.7a4 4 0 0 0-1.6.4z" />
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A subtle, collapsible representation of a single tool call: an icon, the
|
|
47
|
+
* tool name, and its status. Expanding reveals the stringified input (args)
|
|
48
|
+
* and output (the tool result).
|
|
49
|
+
*/
|
|
50
|
+
export function ToolCall({ call }: { call: ToolCallView }) {
|
|
51
|
+
const [open, setOpen] = useState(false);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className={`toolcall status-${call.status}`}>
|
|
55
|
+
<button
|
|
56
|
+
aria-expanded={open}
|
|
57
|
+
className="toolcall-head"
|
|
58
|
+
onClick={() => setOpen((value) => !value)}
|
|
59
|
+
type="button"
|
|
60
|
+
>
|
|
61
|
+
<span className="toolcall-icon">
|
|
62
|
+
<ToolIcon />
|
|
63
|
+
</span>
|
|
64
|
+
<span className="toolcall-name">{call.name}</span>
|
|
65
|
+
<span className={`subagent-status status-${call.status}`}>
|
|
66
|
+
{statusLabel(call.status)}
|
|
67
|
+
</span>
|
|
68
|
+
<span aria-hidden className="toolcall-chevron">
|
|
69
|
+
{open ? "▾" : "▸"}
|
|
70
|
+
</span>
|
|
71
|
+
</button>
|
|
72
|
+
|
|
73
|
+
{open ? (
|
|
74
|
+
<div className="toolcall-body">
|
|
75
|
+
<div className="toolcall-section">
|
|
76
|
+
<span>Input</span>
|
|
77
|
+
<pre>{stringifyArgs(call.args)}</pre>
|
|
78
|
+
</div>
|
|
79
|
+
{call.output != null && call.output !== "" ? (
|
|
80
|
+
<div className="toolcall-section">
|
|
81
|
+
<span>Output</span>
|
|
82
|
+
<pre>{call.output}</pre>
|
|
83
|
+
</div>
|
|
84
|
+
) : null}
|
|
85
|
+
</div>
|
|
86
|
+
) : null}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side thread helpers.
|
|
3
|
+
*
|
|
4
|
+
* The server (the agent's in-memory checkpointer) is the single source of truth
|
|
5
|
+
* for threads. There is no client-side cache: the sidebar is always fetched
|
|
6
|
+
* from the API, and restarting the server clears every thread.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Client } from "@langchain/langgraph-sdk/client";
|
|
10
|
+
|
|
11
|
+
/** LangGraph SDK base URL. Route handlers live under `/api/threads/...`. */
|
|
12
|
+
export function getApiUrl(): string {
|
|
13
|
+
return `${window.location.origin}/api`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Summary of a thread for the history sidebar (mirrors the server type). */
|
|
17
|
+
export type ThreadSummary = {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
updatedAt: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Fetch every thread from the server, newest first. */
|
|
24
|
+
export async function fetchThreads(): Promise<ThreadSummary[]> {
|
|
25
|
+
const response = await fetch(`${getApiUrl()}/threads`, { cache: "no-store" });
|
|
26
|
+
if (!response.ok) return [];
|
|
27
|
+
return (await response.json()) as ThreadSummary[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create the thread row server-side so hydration does not 404.
|
|
32
|
+
*
|
|
33
|
+
* Calls `GET /threads/:id/state` and, on 404, bootstraps with
|
|
34
|
+
* `POST /threads/:id/state` and empty `messages`.
|
|
35
|
+
*/
|
|
36
|
+
async function ensureThreadExists(threadId: string) {
|
|
37
|
+
const client = new Client({ apiUrl: getApiUrl() });
|
|
38
|
+
try {
|
|
39
|
+
await client.threads.getState(threadId);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const status = (error as { status?: number })?.status;
|
|
42
|
+
if (status !== 404) throw error;
|
|
43
|
+
await client.threads.updateState(threadId, { values: { messages: [] } });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Mint a new thread and bootstrap its checkpoint on the server. */
|
|
48
|
+
export async function createThread(): Promise<string> {
|
|
49
|
+
const id = crypto.randomUUID();
|
|
50
|
+
await ensureThreadExists(id);
|
|
51
|
+
return id;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Delete a thread (session + checkpointed state) on the server. */
|
|
55
|
+
export async function deleteThread(threadId: string): Promise<void> {
|
|
56
|
+
await fetch(`${getApiUrl()}/threads/${threadId}`, { method: "DELETE" });
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StrictMode } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
|
|
4
|
+
import { ChatApp } from "@/components/ChatApp";
|
|
5
|
+
import "@/styles/globals.css";
|
|
6
|
+
|
|
7
|
+
createRoot(document.getElementById("root")!).render(
|
|
8
|
+
<StrictMode>
|
|
9
|
+
<ChatApp />
|
|
10
|
+
</StrictMode>,
|
|
11
|
+
);
|