@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,49 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SubagentDiscoverySnapshot } from "@langchain/vue";
|
|
3
|
+
|
|
4
|
+
export type SubagentStatus = SubagentDiscoverySnapshot["status"];
|
|
5
|
+
|
|
6
|
+
/** Lightweight model for a subagent card, derived from a `task` tool call. */
|
|
7
|
+
export type SubagentCard = {
|
|
8
|
+
/** The `task` tool-call id — also the subagent discovery key. */
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
task?: string;
|
|
12
|
+
status: SubagentStatus;
|
|
13
|
+
/** Whether a discovery snapshot exists yet (i.e. the card can be opened). */
|
|
14
|
+
openable: boolean;
|
|
15
|
+
};
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
defineProps<{ cards: SubagentCard[] }>();
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits<{ open: [id: string] }>();
|
|
22
|
+
|
|
23
|
+
function statusLabel(status: SubagentStatus) {
|
|
24
|
+
if (status === "running") return "Running";
|
|
25
|
+
if (status === "complete") return "Complete";
|
|
26
|
+
return "Error";
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<div v-if="cards.length > 0" aria-label="Subagents" class="subagent-list">
|
|
32
|
+
<button
|
|
33
|
+
v-for="card in cards"
|
|
34
|
+
:key="card.id"
|
|
35
|
+
:disabled="!card.openable"
|
|
36
|
+
class="subagent-chip"
|
|
37
|
+
type="button"
|
|
38
|
+
@click="card.openable && emit('open', card.id)"
|
|
39
|
+
>
|
|
40
|
+
<span class="subagent-chip-head">
|
|
41
|
+
<span class="subagent-chip-name">{{ card.name }}</span>
|
|
42
|
+
<span :class="['subagent-status', `status-${card.status}`]">
|
|
43
|
+
{{ statusLabel(card.status) }}
|
|
44
|
+
</span>
|
|
45
|
+
</span>
|
|
46
|
+
<span v-if="card.task" class="subagent-chip-task">{{ card.task }}</span>
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useTheme } from "~/composables/useTheme";
|
|
3
|
+
|
|
4
|
+
const { theme, toggle } = useTheme();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<button
|
|
9
|
+
:aria-label="
|
|
10
|
+
theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'
|
|
11
|
+
"
|
|
12
|
+
class="theme-toggle"
|
|
13
|
+
type="button"
|
|
14
|
+
@click="toggle"
|
|
15
|
+
>
|
|
16
|
+
<svg
|
|
17
|
+
v-if="theme === 'dark'"
|
|
18
|
+
aria-hidden
|
|
19
|
+
fill="none"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
stroke-width="1.5"
|
|
23
|
+
viewBox="0 0 24 24"
|
|
24
|
+
>
|
|
25
|
+
<circle cx="12" cy="12" r="4" />
|
|
26
|
+
<path
|
|
27
|
+
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"
|
|
28
|
+
/>
|
|
29
|
+
</svg>
|
|
30
|
+
<svg
|
|
31
|
+
v-else
|
|
32
|
+
aria-hidden
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
stroke-linecap="round"
|
|
36
|
+
stroke-linejoin="round"
|
|
37
|
+
stroke-width="1.5"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
>
|
|
40
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
41
|
+
</svg>
|
|
42
|
+
</button>
|
|
43
|
+
</template>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ThreadSummary } from "~/utils/threads";
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
threads: ThreadSummary[];
|
|
6
|
+
activeThreadId: string;
|
|
7
|
+
}>();
|
|
8
|
+
|
|
9
|
+
const emit = defineEmits<{
|
|
10
|
+
select: [threadId: string];
|
|
11
|
+
create: [];
|
|
12
|
+
delete: [threadId: string];
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
function formatTime(updatedAt: string | null) {
|
|
16
|
+
if (!updatedAt) return "";
|
|
17
|
+
const date = new Date(updatedAt);
|
|
18
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
19
|
+
return date.toLocaleString(undefined, {
|
|
20
|
+
month: "short",
|
|
21
|
+
day: "numeric",
|
|
22
|
+
hour: "numeric",
|
|
23
|
+
minute: "2-digit",
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<aside aria-label="Thread history" class="sidebar">
|
|
30
|
+
<div class="sidebar-head">
|
|
31
|
+
<span class="eyebrow">History</span>
|
|
32
|
+
<button class="new-thread" type="button" @click="emit('create')">
|
|
33
|
+
+ New
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<ul class="thread-list">
|
|
38
|
+
<li v-if="threads.length === 0" class="thread-empty">
|
|
39
|
+
No conversations yet.
|
|
40
|
+
</li>
|
|
41
|
+
<li
|
|
42
|
+
v-for="thread in threads"
|
|
43
|
+
:key="thread.id"
|
|
44
|
+
:class="['thread-item', { active: thread.id === activeThreadId }]"
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
class="thread-open"
|
|
48
|
+
type="button"
|
|
49
|
+
@click="emit('select', thread.id)"
|
|
50
|
+
>
|
|
51
|
+
<span class="thread-title">{{ thread.title }}</span>
|
|
52
|
+
<span class="thread-time">{{ formatTime(thread.updatedAt) }}</span>
|
|
53
|
+
</button>
|
|
54
|
+
<button
|
|
55
|
+
aria-label="Delete conversation"
|
|
56
|
+
class="thread-delete"
|
|
57
|
+
type="button"
|
|
58
|
+
@click="emit('delete', thread.id)"
|
|
59
|
+
>
|
|
60
|
+
×
|
|
61
|
+
</button>
|
|
62
|
+
</li>
|
|
63
|
+
</ul>
|
|
64
|
+
</aside>
|
|
65
|
+
</template>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export type ToolCallStatus = "running" | "complete" | "error";
|
|
3
|
+
|
|
4
|
+
export type ToolCallView = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
args: Record<string, unknown>;
|
|
8
|
+
output?: string;
|
|
9
|
+
status: ToolCallStatus;
|
|
10
|
+
};
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { ref } from "vue";
|
|
15
|
+
|
|
16
|
+
defineProps<{ call: ToolCallView }>();
|
|
17
|
+
|
|
18
|
+
const open = ref(false);
|
|
19
|
+
|
|
20
|
+
function stringifyArgs(args: Record<string, unknown>) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.stringify(args, null, 2);
|
|
23
|
+
} catch {
|
|
24
|
+
return String(args);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function statusLabel(status: ToolCallStatus) {
|
|
29
|
+
if (status === "running") return "Running";
|
|
30
|
+
if (status === "error") return "Error";
|
|
31
|
+
return "Done";
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<div :class="['toolcall', `status-${call.status}`]">
|
|
37
|
+
<button
|
|
38
|
+
:aria-expanded="open"
|
|
39
|
+
class="toolcall-head"
|
|
40
|
+
type="button"
|
|
41
|
+
@click="open = !open"
|
|
42
|
+
>
|
|
43
|
+
<span class="toolcall-icon">
|
|
44
|
+
<svg
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
stroke-linecap="round"
|
|
49
|
+
stroke-linejoin="round"
|
|
50
|
+
stroke-width="1.6"
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
>
|
|
53
|
+
<path
|
|
54
|
+
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"
|
|
55
|
+
/>
|
|
56
|
+
</svg>
|
|
57
|
+
</span>
|
|
58
|
+
<span class="toolcall-name">{{ call.name }}</span>
|
|
59
|
+
<span :class="['subagent-status', `status-${call.status}`]">
|
|
60
|
+
{{ statusLabel(call.status) }}
|
|
61
|
+
</span>
|
|
62
|
+
<span aria-hidden="true" class="toolcall-chevron">{{
|
|
63
|
+
open ? "▾" : "▸"
|
|
64
|
+
}}</span>
|
|
65
|
+
</button>
|
|
66
|
+
|
|
67
|
+
<div v-if="open" class="toolcall-body">
|
|
68
|
+
<div class="toolcall-section">
|
|
69
|
+
<span>Input</span>
|
|
70
|
+
<pre>{{ stringifyArgs(call.args) }}</pre>
|
|
71
|
+
</div>
|
|
72
|
+
<div
|
|
73
|
+
v-if="call.output != null && call.output !== ''"
|
|
74
|
+
class="toolcall-section"
|
|
75
|
+
>
|
|
76
|
+
<span>Output</span>
|
|
77
|
+
<pre>{{ call.output }}</pre>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{ variant?: "block" | "inline" }>();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<span
|
|
7
|
+
aria-hidden
|
|
8
|
+
:class="variant === 'inline' ? 'inline-dots' : 'typing-dots'"
|
|
9
|
+
>
|
|
10
|
+
<span />
|
|
11
|
+
<span />
|
|
12
|
+
<span />
|
|
13
|
+
</span>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
|
|
3
|
+
export type Theme = "dark" | "light";
|
|
4
|
+
|
|
5
|
+
// Module-scoped so the toggle and the chat shell share one source of truth
|
|
6
|
+
// across remounts (e.g. when starting a new thread).
|
|
7
|
+
const theme = ref<Theme>("dark");
|
|
8
|
+
|
|
9
|
+
export function useTheme() {
|
|
10
|
+
function toggle() {
|
|
11
|
+
theme.value = theme.value === "dark" ? "light" : "dark";
|
|
12
|
+
}
|
|
13
|
+
return { theme, toggle };
|
|
14
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AIMessage, type BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract reasoning-summary text from a message.
|
|
5
|
+
*
|
|
6
|
+
* Reasoning models surface their summaries as `{ type: "reasoning" }` standard
|
|
7
|
+
* content blocks (see `@langchain/openai`'s Responses API converter). Only AI
|
|
8
|
+
* messages carry reasoning; everything else returns an empty string.
|
|
9
|
+
*/
|
|
10
|
+
export function getReasoningText(message: BaseMessage): string {
|
|
11
|
+
if (!AIMessage.isInstance(message)) return "";
|
|
12
|
+
try {
|
|
13
|
+
return message.contentBlocks
|
|
14
|
+
.filter(
|
|
15
|
+
(block): block is { type: "reasoning"; reasoning: string } =>
|
|
16
|
+
block?.type === "reasoning",
|
|
17
|
+
)
|
|
18
|
+
.map((block) => block.reasoning)
|
|
19
|
+
.join("")
|
|
20
|
+
.trim();
|
|
21
|
+
} catch {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether to show the "assistant is thinking" indicator.
|
|
28
|
+
*
|
|
29
|
+
* Mirrors the reference example: show it while a run is loading and the latest
|
|
30
|
+
* message is a human turn, a tool result, or an assistant turn that has not
|
|
31
|
+
* produced any text yet.
|
|
32
|
+
*/
|
|
33
|
+
export function shouldShowTypingIndicator(
|
|
34
|
+
messages: BaseMessage[],
|
|
35
|
+
isLoading: boolean,
|
|
36
|
+
): boolean {
|
|
37
|
+
if (!isLoading) return false;
|
|
38
|
+
|
|
39
|
+
const last = messages.at(-1);
|
|
40
|
+
if (!last) return true;
|
|
41
|
+
if (last.type === "human" || last.type === "tool") return true;
|
|
42
|
+
if (last.type === "ai" && !last.text?.trim()) return true;
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side thread helpers.
|
|
3
|
+
*
|
|
4
|
+
* The server (the agent's in-memory `MemorySaver` checkpointer) is the single
|
|
5
|
+
* source of truth for threads. There is no client-side cache: the sidebar is
|
|
6
|
+
* always fetched 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. Nitro serves the protocol routes under `/api`. */
|
|
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,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "js-nuxt",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "nuxt build",
|
|
7
|
+
"dev": "nuxt dev",
|
|
8
|
+
"generate": "nuxt generate",
|
|
9
|
+
"preview": "nuxt preview",
|
|
10
|
+
"typecheck": "nuxt typecheck",
|
|
11
|
+
"postinstall": "nuxt prepare"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@langchain/core": "^1.2.1",
|
|
15
|
+
"@langchain/langgraph": "^1.3.7",
|
|
16
|
+
"@langchain/langgraph-sdk": "^1.9.20",
|
|
17
|
+
"@langchain/openai": "^1.4.7",
|
|
18
|
+
"@langchain/protocol": "^0.0.16",
|
|
19
|
+
"@langchain/vue": "^1.0.23",
|
|
20
|
+
"deepagents": "^1.10.2",
|
|
21
|
+
"langchain": "^1.4.4",
|
|
22
|
+
"nuxt": "^4.4.8",
|
|
23
|
+
"vue": "^3.5.35",
|
|
24
|
+
"vue-router": "^5.1.0",
|
|
25
|
+
"zod": "^4.4.3"
|
|
26
|
+
},
|
|
27
|
+
"packageManager": "pnpm@10.29.2"
|
|
28
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep agent with delegating subagents.
|
|
3
|
+
*
|
|
4
|
+
* The Vue layer (`@langchain/vue`) discovers subagents from the `task` tool
|
|
5
|
+
* lifecycle events that `deepagents` emits. By giving the coordinator no direct
|
|
6
|
+
* tools and pushing the real capabilities into named subagents (`researcher`,
|
|
7
|
+
* `math-whiz`), every lookup or calculation is delegated through the `task`
|
|
8
|
+
* tool — which is exactly what surfaces as a subagent card in the UI.
|
|
9
|
+
*
|
|
10
|
+
* Reasoning summaries are enabled on the coordinator: over the Responses API,
|
|
11
|
+
* OpenAI returns reasoning *summaries* (not raw chain-of-thought) as
|
|
12
|
+
* `{ type: "reasoning" }` standard content blocks. These stream through the
|
|
13
|
+
* `messages` channel and the UI renders them in a collapsible "Thinking"
|
|
14
|
+
* section.
|
|
15
|
+
*
|
|
16
|
+
* The tool-using subagents deliberately use a plain (non-Responses) model. The
|
|
17
|
+
* Responses API replays prior reasoning items by id on each tool-loop step, and
|
|
18
|
+
* deep-agent subagent history can surface those items with empty ids
|
|
19
|
+
* (`400 Invalid 'input[..].id': ''`). Keeping subagents on standard
|
|
20
|
+
* chat-completions tool calling avoids that, while the coordinator — which only
|
|
21
|
+
* delegates via the `task` tool — keeps its reasoning summaries.
|
|
22
|
+
*
|
|
23
|
+
* The agent is compiled with an in-memory `MemorySaver` checkpointer so the
|
|
24
|
+
* Nitro backend can persist and rehydrate per-thread conversation state. The
|
|
25
|
+
* checkpointer is the single source of truth for the thread list — see the
|
|
26
|
+
* note in `server/utils/runtime.ts`.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
30
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
31
|
+
import { createDeepAgent } from "deepagents";
|
|
32
|
+
|
|
33
|
+
import { stripReasoningReplay } from "./middleware";
|
|
34
|
+
import { calculator, searchWeb } from "./tools";
|
|
35
|
+
|
|
36
|
+
const coordinatorModel = new ChatOpenAI({
|
|
37
|
+
model: "gpt-5.4-mini",
|
|
38
|
+
useResponsesApi: true,
|
|
39
|
+
reasoning: { effort: "low", summary: "auto" },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const subagentModel = new ChatOpenAI({ model: "gpt-5.4-mini" });
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* In-memory checkpointer — the single source of truth for threads.
|
|
46
|
+
*
|
|
47
|
+
* Exported so the server can enumerate threads (via `checkpointer.storage`) and
|
|
48
|
+
* delete them (`checkpointer.deleteThread`). It is process-local and volatile:
|
|
49
|
+
* restarting the server clears every thread.
|
|
50
|
+
*/
|
|
51
|
+
export const checkpointer = new MemorySaver();
|
|
52
|
+
|
|
53
|
+
export const agent = createDeepAgent({
|
|
54
|
+
model: coordinatorModel,
|
|
55
|
+
middleware: [stripReasoningReplay],
|
|
56
|
+
checkpointer,
|
|
57
|
+
subagents: [
|
|
58
|
+
{
|
|
59
|
+
name: "researcher",
|
|
60
|
+
description:
|
|
61
|
+
"Researches a topic using the search_web tool and reports concise findings.",
|
|
62
|
+
tools: [searchWeb],
|
|
63
|
+
model: subagentModel,
|
|
64
|
+
systemPrompt:
|
|
65
|
+
"You are the researcher subagent. Use the search_web tool to look up " +
|
|
66
|
+
"the requested topic, then summarize the findings in two or three " +
|
|
67
|
+
"sentences. Always call search_web at least once before answering.",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "math-whiz",
|
|
71
|
+
description:
|
|
72
|
+
"Performs calculations using the calculator tool and explains the result.",
|
|
73
|
+
tools: [calculator],
|
|
74
|
+
model: subagentModel,
|
|
75
|
+
systemPrompt:
|
|
76
|
+
"You are the math-whiz subagent. Use the calculator tool to evaluate " +
|
|
77
|
+
"the requested expression, then state the result clearly. Always call " +
|
|
78
|
+
"the calculator tool before answering.",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
systemPrompt:
|
|
82
|
+
"You are a helpful coordinator. When a request involves looking something " +
|
|
83
|
+
"up, delegate it to the `researcher` subagent. When it involves math, " +
|
|
84
|
+
"delegate it to the `math-whiz` subagent. You may run both subagents for a " +
|
|
85
|
+
"single request. After the subagents respond, combine their results into a " +
|
|
86
|
+
"short, clearly labeled final answer.",
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export type Agent = typeof agent;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AIMessage, type BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
import { createMiddleware } from "langchain";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rebuild prior assistant messages so the Responses API doesn't replay stale
|
|
6
|
+
* item ids.
|
|
7
|
+
*
|
|
8
|
+
* Over the Responses API, `@langchain/openai` replays an assistant turn either
|
|
9
|
+
* from `response_metadata.output` (the raw response items) or by reconstructing
|
|
10
|
+
* items from `additional_kwargs` — both of which carry ids (reasoning items,
|
|
11
|
+
* function-call items). After a round-trip through the checkpointer those ids
|
|
12
|
+
* can come back empty, and OpenAI rejects the next call with
|
|
13
|
+
* `400 Invalid 'input[..].id': ''`.
|
|
14
|
+
*
|
|
15
|
+
* We rebuild each prior assistant message from just its `content` and
|
|
16
|
+
* `tool_calls`. Tool calls keep their `call_id` (the valid pairing key), the
|
|
17
|
+
* converter emits clean items with no stale ids, and reasoning items are
|
|
18
|
+
* dropped from the model *input*. State is untouched, so the UI still renders
|
|
19
|
+
* each turn's reasoning; the model simply produces fresh reasoning per turn and
|
|
20
|
+
* never receives the old items back.
|
|
21
|
+
*/
|
|
22
|
+
function sanitizeForReplay(message: BaseMessage): BaseMessage {
|
|
23
|
+
if (!AIMessage.isInstance(message)) return message;
|
|
24
|
+
|
|
25
|
+
return new AIMessage({
|
|
26
|
+
id: message.id,
|
|
27
|
+
content: message.content,
|
|
28
|
+
tool_calls: message.tool_calls,
|
|
29
|
+
invalid_tool_calls: message.invalid_tool_calls,
|
|
30
|
+
usage_metadata: message.usage_metadata,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const stripReasoningReplay = createMiddleware({
|
|
35
|
+
name: "StripReasoningReplay",
|
|
36
|
+
wrapModelCall: async (request, handler) =>
|
|
37
|
+
handler({ ...request, messages: request.messages.map(sanitizeForReplay) }),
|
|
38
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock tools for the deep agent's subagents.
|
|
3
|
+
*
|
|
4
|
+
* Ported from the streaming-cookbook `react-custom-backend` example. Both tools
|
|
5
|
+
* are intentionally fake so the example runs offline; what matters is that the
|
|
6
|
+
* subagents emit real tool-call deltas on the `messages` channel and tool
|
|
7
|
+
* results as `ToolMessage`s, all namespaced under their subagent run.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { tool } from "langchain";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
export const searchWeb = tool(
|
|
14
|
+
async ({ query }) => {
|
|
15
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
16
|
+
return JSON.stringify({
|
|
17
|
+
results: [
|
|
18
|
+
{
|
|
19
|
+
title: `Result for: ${query}`,
|
|
20
|
+
snippet:
|
|
21
|
+
"LangGraph streaming sends token deltas on the messages channel " +
|
|
22
|
+
"and tool lifecycle events on tools. Subagent runs carry their own " +
|
|
23
|
+
"namespace so the UI can render them in dedicated cards.",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "search_web",
|
|
30
|
+
description: "Search the web for information.",
|
|
31
|
+
schema: z.object({ query: z.string().describe("Search query.") }),
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/** Demo-only arithmetic evaluator restricted to numbers and basic operators. */
|
|
36
|
+
function evaluateExpression(expression: string): number {
|
|
37
|
+
if (!/^[\d+\-*/().\s]+$/.test(expression)) {
|
|
38
|
+
throw new Error("Only basic arithmetic is supported.");
|
|
39
|
+
}
|
|
40
|
+
const compute = new Function(
|
|
41
|
+
`"use strict"; return (${expression});`,
|
|
42
|
+
) as () => unknown;
|
|
43
|
+
const result = compute();
|
|
44
|
+
if (typeof result !== "number" || !Number.isFinite(result)) {
|
|
45
|
+
throw new Error("Expression did not evaluate to a finite number.");
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const calculator = tool(
|
|
51
|
+
async ({ expression }) => {
|
|
52
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
53
|
+
try {
|
|
54
|
+
return String(evaluateExpression(expression));
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return `Error evaluating: ${expression} (${String(error)})`;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "calculator",
|
|
61
|
+
description: "Evaluate a math expression.",
|
|
62
|
+
schema: z.object({
|
|
63
|
+
expression: z.string().describe("Math expression to evaluate."),
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /api/threads/:threadId/commands`.
|
|
3
|
+
*
|
|
4
|
+
* The request body is an Agent Protocol `Command`. The response is the command
|
|
5
|
+
* result emitted by the owning {@link LocalThreadSession}.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Command } from "@langchain/protocol";
|
|
9
|
+
|
|
10
|
+
import { getSession } from "../../../utils/runtime";
|
|
11
|
+
|
|
12
|
+
export default defineEventHandler(async (event) => {
|
|
13
|
+
const threadId = getRouterParam(event, "threadId") ?? "local";
|
|
14
|
+
const command = await readBody<Command>(event);
|
|
15
|
+
return getSession(threadId).handleCommand(command);
|
|
16
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /api/threads/:threadId/history`.
|
|
3
|
+
*
|
|
4
|
+
* Lists past thread states (newest-first) from the graph checkpointer, powering
|
|
5
|
+
* the SDK's history/replay reads.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getAgent } from "../../../utils/runtime";
|
|
9
|
+
import { ThreadNotFoundError, getThreadHistory } from "../../../utils/threads";
|
|
10
|
+
|
|
11
|
+
type HistoryBody = {
|
|
12
|
+
limit?: number;
|
|
13
|
+
before?: unknown;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
checkpoint?: Record<string, unknown>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default defineEventHandler(async (event) => {
|
|
19
|
+
const threadId = getRouterParam(event, "threadId") ?? "local";
|
|
20
|
+
const body = await readBody<HistoryBody>(event).catch(
|
|
21
|
+
() => ({}) as HistoryBody,
|
|
22
|
+
);
|
|
23
|
+
try {
|
|
24
|
+
return await getThreadHistory(getAgent().graph, threadId, {
|
|
25
|
+
limit: typeof body.limit === "number" ? body.limit : 10,
|
|
26
|
+
before: body.before,
|
|
27
|
+
metadata: body.metadata,
|
|
28
|
+
checkpoint: body.checkpoint,
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof ThreadNotFoundError) {
|
|
32
|
+
setResponseStatus(event, 404);
|
|
33
|
+
return { error: "not_found", message: error.message };
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DELETE /api/threads/:threadId` — drop a thread's session and checkpoints.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { deleteThread } from "../../../utils/runtime";
|
|
6
|
+
|
|
7
|
+
export default defineEventHandler(async (event) => {
|
|
8
|
+
const threadId = getRouterParam(event, "threadId") ?? "local";
|
|
9
|
+
await deleteThread(threadId);
|
|
10
|
+
setResponseStatus(event, 204);
|
|
11
|
+
return null;
|
|
12
|
+
});
|