@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.
Files changed (193) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +661 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +59 -0
  5. package/registry/frameworks/deno/.env.example +6 -0
  6. package/registry/frameworks/deno/README.md +137 -0
  7. package/registry/frameworks/deno/client/index.html +23 -0
  8. package/registry/frameworks/deno/client/package.json +30 -0
  9. package/registry/frameworks/deno/client/public/favicon.ico +0 -0
  10. package/registry/frameworks/deno/client/src/components/Chat.tsx +124 -0
  11. package/registry/frameworks/deno/client/src/components/ChatApp.tsx +129 -0
  12. package/registry/frameworks/deno/client/src/components/Conversation.tsx +91 -0
  13. package/registry/frameworks/deno/client/src/components/MessageBubbles.tsx +88 -0
  14. package/registry/frameworks/deno/client/src/components/MessageReasoning.tsx +71 -0
  15. package/registry/frameworks/deno/client/src/components/MessageThread.tsx +135 -0
  16. package/registry/frameworks/deno/client/src/components/StreamingIndicator.tsx +36 -0
  17. package/registry/frameworks/deno/client/src/components/Subagents.tsx +120 -0
  18. package/registry/frameworks/deno/client/src/components/ThemeIcons.tsx +31 -0
  19. package/registry/frameworks/deno/client/src/components/ThreadHistory.tsx +73 -0
  20. package/registry/frameworks/deno/client/src/components/ToolCall.tsx +89 -0
  21. package/registry/frameworks/deno/client/src/lib/agent-type.ts +4 -0
  22. package/registry/frameworks/deno/client/src/lib/chat/threads-client.ts +51 -0
  23. package/registry/frameworks/deno/client/src/main.tsx +11 -0
  24. package/registry/frameworks/deno/client/src/styles/globals.css +714 -0
  25. package/registry/frameworks/deno/client/src/vite-env.d.ts +1 -0
  26. package/registry/frameworks/deno/client/tsconfig.app.json +7 -0
  27. package/registry/frameworks/deno/client/tsconfig.json +24 -0
  28. package/registry/frameworks/deno/client/tsconfig.node.json +19 -0
  29. package/registry/frameworks/deno/client/vite.config.ts +24 -0
  30. package/registry/frameworks/deno/deno.json +16 -0
  31. package/registry/frameworks/deno/main.ts +23 -0
  32. package/registry/frameworks/deno/package.json +14 -0
  33. package/registry/frameworks/deno/server/agent/index.ts +60 -0
  34. package/registry/frameworks/deno/server/agent/middleware.ts +24 -0
  35. package/registry/frameworks/deno/server/agent/tools.ts +64 -0
  36. package/registry/frameworks/deno/server/registry.ts +40 -0
  37. package/registry/frameworks/deno/server/routes.ts +114 -0
  38. package/registry/frameworks/deno/server/serialize.ts +30 -0
  39. package/registry/frameworks/deno/server/session.ts +210 -0
  40. package/registry/frameworks/deno/server/threads.ts +404 -0
  41. package/registry/frameworks/deno.ts +17 -0
  42. package/registry/frameworks/hono/.env.example +6 -0
  43. package/registry/frameworks/hono/README.md +186 -0
  44. package/registry/frameworks/hono/index.html +22 -0
  45. package/registry/frameworks/hono/package.json +42 -0
  46. package/registry/frameworks/hono/src/components/Chat.tsx +124 -0
  47. package/registry/frameworks/hono/src/components/ChatApp.tsx +129 -0
  48. package/registry/frameworks/hono/src/components/Conversation.tsx +90 -0
  49. package/registry/frameworks/hono/src/components/MessageBubbles.tsx +88 -0
  50. package/registry/frameworks/hono/src/components/MessageReasoning.tsx +71 -0
  51. package/registry/frameworks/hono/src/components/MessageThread.tsx +135 -0
  52. package/registry/frameworks/hono/src/components/StreamingIndicator.tsx +36 -0
  53. package/registry/frameworks/hono/src/components/Subagents.tsx +120 -0
  54. package/registry/frameworks/hono/src/components/ThemeIcons.tsx +31 -0
  55. package/registry/frameworks/hono/src/components/ThreadHistory.tsx +73 -0
  56. package/registry/frameworks/hono/src/components/ToolCall.tsx +89 -0
  57. package/registry/frameworks/hono/src/lib/agent/types.ts +4 -0
  58. package/registry/frameworks/hono/src/lib/chat/threads-client.ts +57 -0
  59. package/registry/frameworks/hono/src/main.tsx +11 -0
  60. package/registry/frameworks/hono/src/styles/globals.css +714 -0
  61. package/registry/frameworks/hono/src/vite-env.d.ts +1 -0
  62. package/registry/frameworks/hono/tsconfig.app.json +22 -0
  63. package/registry/frameworks/hono/tsconfig.json +7 -0
  64. package/registry/frameworks/hono/tsconfig.worker.json +18 -0
  65. package/registry/frameworks/hono/vite.config.ts +16 -0
  66. package/registry/frameworks/hono/worker/agent/index.ts +53 -0
  67. package/registry/frameworks/hono/worker/agent/middleware.ts +20 -0
  68. package/registry/frameworks/hono/worker/agent/tools.ts +55 -0
  69. package/registry/frameworks/hono/worker/durable-objects/thread-session.ts +159 -0
  70. package/registry/frameworks/hono/worker/env.d.ts +17 -0
  71. package/registry/frameworks/hono/worker/index.ts +140 -0
  72. package/registry/frameworks/hono/worker/server/registry.ts +39 -0
  73. package/registry/frameworks/hono/worker/server/runs.ts +82 -0
  74. package/registry/frameworks/hono/worker/server/serialize.ts +30 -0
  75. package/registry/frameworks/hono/worker/server/threads.ts +404 -0
  76. package/registry/frameworks/hono/worker/tsconfig.json +4 -0
  77. package/registry/frameworks/hono/wrangler.jsonc +28 -0
  78. package/registry/frameworks/hono.ts +35 -0
  79. package/registry/frameworks/next/.env.example +6 -0
  80. package/registry/frameworks/next/README.md +173 -0
  81. package/registry/frameworks/next/app/api/threads/[threadId]/commands/route.ts +21 -0
  82. package/registry/frameworks/next/app/api/threads/[threadId]/history/route.ts +35 -0
  83. package/registry/frameworks/next/app/api/threads/[threadId]/route.ts +13 -0
  84. package/registry/frameworks/next/app/api/threads/[threadId]/state/route.ts +51 -0
  85. package/registry/frameworks/next/app/api/threads/[threadId]/stream/route.ts +30 -0
  86. package/registry/frameworks/next/app/api/threads/route.ts +11 -0
  87. package/registry/frameworks/next/app/favicon.ico +0 -0
  88. package/registry/frameworks/next/app/globals.css +712 -0
  89. package/registry/frameworks/next/app/layout.tsx +34 -0
  90. package/registry/frameworks/next/app/page.tsx +5 -0
  91. package/registry/frameworks/next/components/Chat.tsx +124 -0
  92. package/registry/frameworks/next/components/ChatApp.tsx +129 -0
  93. package/registry/frameworks/next/components/Conversation.tsx +90 -0
  94. package/registry/frameworks/next/components/MessageBubbles.tsx +88 -0
  95. package/registry/frameworks/next/components/MessageReasoning.tsx +71 -0
  96. package/registry/frameworks/next/components/MessageThread.tsx +135 -0
  97. package/registry/frameworks/next/components/StreamingIndicator.tsx +36 -0
  98. package/registry/frameworks/next/components/Subagents.tsx +120 -0
  99. package/registry/frameworks/next/components/ThemeIcons.tsx +31 -0
  100. package/registry/frameworks/next/components/ThreadHistory.tsx +73 -0
  101. package/registry/frameworks/next/components/ToolCall.tsx +89 -0
  102. package/registry/frameworks/next/eslint.config.mjs +18 -0
  103. package/registry/frameworks/next/lib/agent/index.ts +95 -0
  104. package/registry/frameworks/next/lib/agent/middleware.ts +40 -0
  105. package/registry/frameworks/next/lib/agent/tools.ts +66 -0
  106. package/registry/frameworks/next/lib/chat/threads-client.ts +57 -0
  107. package/registry/frameworks/next/lib/server/registry.ts +57 -0
  108. package/registry/frameworks/next/lib/server/serialize.ts +32 -0
  109. package/registry/frameworks/next/lib/server/session.ts +212 -0
  110. package/registry/frameworks/next/lib/server/threads.ts +406 -0
  111. package/registry/frameworks/next/next.config.ts +7 -0
  112. package/registry/frameworks/next/package.json +37 -0
  113. package/registry/frameworks/next/postcss.config.mjs +7 -0
  114. package/registry/frameworks/next/public/file.svg +1 -0
  115. package/registry/frameworks/next/public/globe.svg +1 -0
  116. package/registry/frameworks/next/public/next.svg +1 -0
  117. package/registry/frameworks/next/public/vercel.svg +1 -0
  118. package/registry/frameworks/next/public/window.svg +1 -0
  119. package/registry/frameworks/next/tsconfig.json +34 -0
  120. package/registry/frameworks/next.ts +17 -0
  121. package/registry/frameworks/nuxt/.env.example +3 -0
  122. package/registry/frameworks/nuxt/README.md +133 -0
  123. package/registry/frameworks/nuxt/app/app.vue +26 -0
  124. package/registry/frameworks/nuxt/app/assets/css/main.css +707 -0
  125. package/registry/frameworks/nuxt/app/components/Chat.vue +105 -0
  126. package/registry/frameworks/nuxt/app/components/ChatApp.vue +89 -0
  127. package/registry/frameworks/nuxt/app/components/ChatThread.vue +27 -0
  128. package/registry/frameworks/nuxt/app/components/MessageBubble.vue +60 -0
  129. package/registry/frameworks/nuxt/app/components/MessageBubbles.vue +213 -0
  130. package/registry/frameworks/nuxt/app/components/MessageList.vue +51 -0
  131. package/registry/frameworks/nuxt/app/components/MessageReasoning.vue +53 -0
  132. package/registry/frameworks/nuxt/app/components/StreamingIndicator.vue +9 -0
  133. package/registry/frameworks/nuxt/app/components/SubagentDetail.vue +51 -0
  134. package/registry/frameworks/nuxt/app/components/SubagentList.vue +49 -0
  135. package/registry/frameworks/nuxt/app/components/ThemeToggle.vue +43 -0
  136. package/registry/frameworks/nuxt/app/components/ThreadHistory.vue +65 -0
  137. package/registry/frameworks/nuxt/app/components/ToolCall.vue +81 -0
  138. package/registry/frameworks/nuxt/app/components/TypingDots.vue +14 -0
  139. package/registry/frameworks/nuxt/app/composables/useTheme.ts +14 -0
  140. package/registry/frameworks/nuxt/app/utils/streaming.ts +44 -0
  141. package/registry/frameworks/nuxt/app/utils/threads.ts +57 -0
  142. package/registry/frameworks/nuxt/nuxt.config.ts +6 -0
  143. package/registry/frameworks/nuxt/package.json +28 -0
  144. package/registry/frameworks/nuxt/public/favicon.ico +0 -0
  145. package/registry/frameworks/nuxt/public/robots.txt +2 -0
  146. package/registry/frameworks/nuxt/server/agent/index.ts +89 -0
  147. package/registry/frameworks/nuxt/server/agent/middleware.ts +38 -0
  148. package/registry/frameworks/nuxt/server/agent/tools.ts +66 -0
  149. package/registry/frameworks/nuxt/server/api/threads/[threadId]/commands.post.ts +16 -0
  150. package/registry/frameworks/nuxt/server/api/threads/[threadId]/history.post.ts +37 -0
  151. package/registry/frameworks/nuxt/server/api/threads/[threadId]/index.delete.ts +12 -0
  152. package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.get.ts +22 -0
  153. package/registry/frameworks/nuxt/server/api/threads/[threadId]/state.post.ts +32 -0
  154. package/registry/frameworks/nuxt/server/api/threads/[threadId]/stream.post.ts +24 -0
  155. package/registry/frameworks/nuxt/server/api/threads/index.get.ts +13 -0
  156. package/registry/frameworks/nuxt/server/utils/runtime.ts +42 -0
  157. package/registry/frameworks/nuxt/server/utils/serialize.ts +30 -0
  158. package/registry/frameworks/nuxt/server/utils/session.ts +210 -0
  159. package/registry/frameworks/nuxt/server/utils/threads.ts +404 -0
  160. package/registry/frameworks/nuxt/tsconfig.json +18 -0
  161. package/registry/frameworks/nuxt.ts +17 -0
  162. package/registry/frameworks/vite/.env.example +20 -0
  163. package/registry/frameworks/vite/README.md +149 -0
  164. package/registry/frameworks/vite/agent/index.ts +59 -0
  165. package/registry/frameworks/vite/agent/middleware.ts +24 -0
  166. package/registry/frameworks/vite/agent/tools.ts +64 -0
  167. package/registry/frameworks/vite/index.html +23 -0
  168. package/registry/frameworks/vite/langgraph.json +16 -0
  169. package/registry/frameworks/vite/package.json +39 -0
  170. package/registry/frameworks/vite/public/favicon.ico +0 -0
  171. package/registry/frameworks/vite/scripts/vite-langgraph-proxy.ts +34 -0
  172. package/registry/frameworks/vite/src/components/Chat.tsx +124 -0
  173. package/registry/frameworks/vite/src/components/ChatApp.tsx +122 -0
  174. package/registry/frameworks/vite/src/components/Conversation.tsx +91 -0
  175. package/registry/frameworks/vite/src/components/MessageBubbles.tsx +88 -0
  176. package/registry/frameworks/vite/src/components/MessageReasoning.tsx +71 -0
  177. package/registry/frameworks/vite/src/components/MessageThread.tsx +135 -0
  178. package/registry/frameworks/vite/src/components/StreamingIndicator.tsx +36 -0
  179. package/registry/frameworks/vite/src/components/Subagents.tsx +120 -0
  180. package/registry/frameworks/vite/src/components/ThemeIcons.tsx +31 -0
  181. package/registry/frameworks/vite/src/components/ThreadHistory.tsx +73 -0
  182. package/registry/frameworks/vite/src/components/ToolCall.tsx +89 -0
  183. package/registry/frameworks/vite/src/lib/agent-type.ts +4 -0
  184. package/registry/frameworks/vite/src/lib/chat/threads-client.ts +114 -0
  185. package/registry/frameworks/vite/src/main.tsx +11 -0
  186. package/registry/frameworks/vite/src/styles/globals.css +714 -0
  187. package/registry/frameworks/vite/src/vite-env.d.ts +11 -0
  188. package/registry/frameworks/vite/tsconfig.app.json +24 -0
  189. package/registry/frameworks/vite/tsconfig.json +7 -0
  190. package/registry/frameworks/vite/tsconfig.node.json +21 -0
  191. package/registry/frameworks/vite/vercel.json +3 -0
  192. package/registry/frameworks/vite/vite.config.ts +24 -0
  193. package/registry/frameworks/vite.ts +17 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Thread state helpers backed by the graph checkpointer.
3
+ *
4
+ * Implements the LangGraph SDK thread state wire-shape consumed by
5
+ * `client.threads.getState` / `updateState` (`GET|POST /threads/:id/state`) and
6
+ * `getHistory` (`POST /threads/:id/history`), aligned with the Agent Protocol
7
+ * thread model.
8
+ */
9
+
10
+ import type { MemorySaver } from "@langchain/langgraph";
11
+ import type { CompiledGraphType } from "@langchain/langgraph";
12
+ import type { RunnableConfig } from "@langchain/core/runnables";
13
+
14
+ import { isRecord, sanitizeForJson } from "./serialize";
15
+
16
+ /**
17
+ * Compiled LangGraph instance exposed through the custom protocol server.
18
+ *
19
+ * Thread routes read and write checkpointed state through this graph's
20
+ * checkpointer rather than maintaining a separate thread store.
21
+ */
22
+ export type LocalProtocolGraph = CompiledGraphType;
23
+
24
+ type StateSnapshot = Awaited<ReturnType<LocalProtocolGraph["getState"]>>;
25
+
26
+ /**
27
+ * Raised when a thread has no checkpoint yet.
28
+ *
29
+ * The route handlers map this to HTTP 404 so the LangGraph SDK can bootstrap
30
+ * the thread via `POST /threads/:id/state` before the first run.
31
+ */
32
+ export class ThreadNotFoundError extends Error {
33
+ readonly threadId: string;
34
+
35
+ constructor(threadId: string) {
36
+ super(`Thread ${threadId} not found`);
37
+ this.name = "ThreadNotFoundError";
38
+ this.threadId = threadId;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Graph node used when bootstrapping an empty thread.
44
+ *
45
+ * Empty `messages` updates must land on `__start__` so conditional edges are
46
+ * not evaluated before the first human turn exists.
47
+ */
48
+ const INITIAL_UPDATE_NODE = "__start__";
49
+
50
+ /**
51
+ * Default graph node for non-empty state updates on an existing checkpoint.
52
+ *
53
+ * Matches the agent's model node when the client omits `as_node`.
54
+ */
55
+ const DEFAULT_UPDATE_NODE = "model_request";
56
+
57
+ /** Build the {@link RunnableConfig} that scopes graph calls to a thread id. */
58
+ function threadConfig(threadId: string): RunnableConfig {
59
+ return { configurable: { thread_id: threadId } };
60
+ }
61
+
62
+ /**
63
+ * Build the config passed to `getStateHistory` for root or subgraph-scoped
64
+ * history, matching langgraph-api's `{ thread_id, checkpoint_ns: "", ...checkpoint }`.
65
+ */
66
+ function historyConfig(
67
+ threadId: string,
68
+ checkpoint?: Record<string, unknown> | null,
69
+ ): RunnableConfig {
70
+ const configurable: Record<string, unknown> = {
71
+ thread_id: threadId,
72
+ checkpoint_ns: "",
73
+ };
74
+ if (checkpoint && isRecord(checkpoint)) {
75
+ Object.assign(configurable, checkpoint);
76
+ }
77
+ return { configurable };
78
+ }
79
+
80
+ /** Read the `configurable` bag from a LangGraph run config. */
81
+ function configurableOf(config: RunnableConfig): Record<string, unknown> {
82
+ return isRecord(config.configurable) ? config.configurable : {};
83
+ }
84
+
85
+ /**
86
+ * Return whether a {@link StateSnapshot} represents a persisted checkpoint.
87
+ *
88
+ * LangGraph returns an empty configurable bag before the first write; the SDK
89
+ * treats that as "thread not found" rather than an empty thread state.
90
+ */
91
+ function threadHasCheckpoint(snapshot: StateSnapshot): boolean {
92
+ const checkpointId = configurableOf(snapshot.config).checkpoint_id;
93
+ return typeof checkpointId === "string" && checkpointId.length > 0;
94
+ }
95
+
96
+ function isStateSnapshot(state: unknown): state is StateSnapshot {
97
+ return isRecord(state) && "values" in state && "next" in state;
98
+ }
99
+
100
+ function serializeTaskError(error: unknown): string | null {
101
+ if (error == null) return null;
102
+ if (error instanceof Error) return error.message;
103
+ if (typeof error === "string") return error;
104
+ return String(error);
105
+ }
106
+
107
+ /** Map a LangGraph run config to the SDK checkpoint wire shape. */
108
+ function runnableConfigToCheckpoint(
109
+ config: RunnableConfig | null | undefined,
110
+ fallbackThreadId?: string,
111
+ ): Record<string, unknown> | null {
112
+ if (!config || !isRecord(config.configurable)) return null;
113
+ const c = config.configurable;
114
+ const thread_id =
115
+ typeof c.thread_id === "string" ? c.thread_id : fallbackThreadId;
116
+ if (!thread_id || typeof c.checkpoint_id !== "string") return null;
117
+
118
+ return {
119
+ thread_id,
120
+ checkpoint_id: c.checkpoint_id,
121
+ checkpoint_ns: typeof c.checkpoint_ns === "string" ? c.checkpoint_ns : "",
122
+ checkpoint_map: isRecord(c.checkpoint_map) ? c.checkpoint_map : null,
123
+ };
124
+ }
125
+
126
+ function taskCheckpointFromState(
127
+ state: unknown,
128
+ ): Record<string, unknown> | null {
129
+ if (state == null || !isRecord(state) || !isRecord(state.configurable)) {
130
+ return null;
131
+ }
132
+ const c = state.configurable;
133
+ if (typeof c.thread_id !== "string") return null;
134
+ return {
135
+ thread_id: c.thread_id,
136
+ checkpoint_id: typeof c.checkpoint_id === "string" ? c.checkpoint_id : null,
137
+ checkpoint_ns: typeof c.checkpoint_ns === "string" ? c.checkpoint_ns : "",
138
+ checkpoint_map: isRecord(c.checkpoint_map) ? c.checkpoint_map : null,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Serialize a LangGraph {@link StateSnapshot} to the SDK `ThreadState` shape.
144
+ *
145
+ * Aligned with langgraph-api's `stateSnapshotToThreadState`, plus
146
+ * {@link sanitizeForJson} on `values` and nested task `result` payloads.
147
+ */
148
+ export function serializeThreadState(
149
+ snapshot: StateSnapshot,
150
+ threadId: string,
151
+ ): Record<string, unknown> {
152
+ const configurable = configurableOf(snapshot.config);
153
+ const checkpoint = runnableConfigToCheckpoint(snapshot.config, threadId) ?? {
154
+ thread_id: threadId,
155
+ checkpoint_id:
156
+ typeof configurable.checkpoint_id === "string"
157
+ ? configurable.checkpoint_id
158
+ : null,
159
+ checkpoint_ns:
160
+ typeof configurable.checkpoint_ns === "string"
161
+ ? configurable.checkpoint_ns
162
+ : "",
163
+ checkpoint_map: null,
164
+ };
165
+
166
+ const tasks = (snapshot.tasks ?? []).map((task) => {
167
+ const record = task as {
168
+ id?: unknown;
169
+ name?: unknown;
170
+ error?: unknown;
171
+ interrupts?: unknown;
172
+ path?: unknown;
173
+ result?: unknown;
174
+ state?: unknown;
175
+ };
176
+ return {
177
+ id: record.id,
178
+ name: record.name,
179
+ error: serializeTaskError(record.error),
180
+ interrupts: Array.isArray(record.interrupts) ? record.interrupts : [],
181
+ path: record.path ?? null,
182
+ checkpoint: taskCheckpointFromState(record.state),
183
+ state:
184
+ record.state != null && isStateSnapshot(record.state)
185
+ ? serializeThreadState(record.state, threadId)
186
+ : null,
187
+ result: record.result != null ? sanitizeForJson(record.result) : null,
188
+ };
189
+ });
190
+
191
+ const parentConfig = (snapshot as { parentConfig?: RunnableConfig })
192
+ .parentConfig;
193
+
194
+ return {
195
+ values: sanitizeForJson(snapshot.values ?? {}),
196
+ next: [...(snapshot.next ?? [])],
197
+ tasks,
198
+ checkpoint,
199
+ metadata: { ...snapshot.metadata },
200
+ created_at: snapshot.createdAt ?? null,
201
+ parent_checkpoint: runnableConfigToCheckpoint(parentConfig, threadId),
202
+ };
203
+ }
204
+
205
+ /** Summary of a thread for the history sidebar. */
206
+ export type ThreadSummary = {
207
+ id: string;
208
+ title: string;
209
+ updatedAt: string | null;
210
+ };
211
+
212
+ const UNTITLED = "New conversation";
213
+
214
+ /** Derive a sidebar title from the first human message in a thread. */
215
+ function deriveTitle(values: unknown): string {
216
+ if (!isRecord(values) || !Array.isArray(values.messages)) return UNTITLED;
217
+ for (const message of values.messages) {
218
+ if (!isRecord(message) || message.type !== "human") continue;
219
+ const { content } = message;
220
+ const text =
221
+ typeof content === "string"
222
+ ? content
223
+ : Array.isArray(content)
224
+ ? content
225
+ .map((block) =>
226
+ isRecord(block) && typeof block.text === "string"
227
+ ? block.text
228
+ : "",
229
+ )
230
+ .join("")
231
+ : "";
232
+ const trimmed = text.trim();
233
+ if (trimmed) return trimmed.slice(0, 80);
234
+ }
235
+ return UNTITLED;
236
+ }
237
+
238
+ /**
239
+ * List every thread known to the checkpointer, newest first.
240
+ *
241
+ * The checkpointer is the single source of truth: thread ids are the top-level
242
+ * keys of {@link MemorySaver.storage}, and each thread's title/timestamp is
243
+ * derived from its latest checkpoint. Restarting the server clears all of this.
244
+ */
245
+ export async function listThreads(
246
+ graph: LocalProtocolGraph,
247
+ checkpointer: MemorySaver,
248
+ ): Promise<ThreadSummary[]> {
249
+ const ids = Object.keys(checkpointer.storage);
250
+ const summaries: ThreadSummary[] = [];
251
+ for (const id of ids) {
252
+ try {
253
+ const state = await getThreadState(graph, id);
254
+ summaries.push({
255
+ id,
256
+ title: deriveTitle(state.values),
257
+ updatedAt:
258
+ typeof state.created_at === "string" ? state.created_at : null,
259
+ });
260
+ } catch {
261
+ // Skip threads without a readable checkpoint.
262
+ }
263
+ }
264
+ summaries.sort((a, b) =>
265
+ (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""),
266
+ );
267
+ return summaries;
268
+ }
269
+
270
+ /**
271
+ * Read checkpointed thread state for `GET /threads/:threadId/state`.
272
+ *
273
+ * @throws {@link ThreadNotFoundError} When the thread has no checkpoint yet.
274
+ */
275
+ export async function getThreadState(
276
+ graph: LocalProtocolGraph,
277
+ threadId: string,
278
+ ): Promise<Record<string, unknown>> {
279
+ const snapshot = await graph.getState(threadConfig(threadId));
280
+ if (!threadHasCheckpoint(snapshot)) throw new ThreadNotFoundError(threadId);
281
+ return serializeThreadState(snapshot, threadId);
282
+ }
283
+
284
+ /**
285
+ * Parse the `before` pagination cursor accepted by `POST /threads/:id/history`.
286
+ */
287
+ function parseBeforeCursor(
288
+ threadId: string,
289
+ before: unknown,
290
+ ): RunnableConfig | undefined {
291
+ if (before == null) return undefined;
292
+ if (typeof before === "string") {
293
+ return { configurable: { thread_id: threadId, checkpoint_id: before } };
294
+ }
295
+ if (!isRecord(before)) return undefined;
296
+
297
+ if (isRecord(before.configurable)) {
298
+ return {
299
+ configurable: { thread_id: threadId, ...before.configurable },
300
+ };
301
+ }
302
+
303
+ const checkpointId = before.checkpoint_id;
304
+ if (typeof checkpointId !== "string") return undefined;
305
+
306
+ const cursor: RunnableConfig = {
307
+ configurable: { thread_id: threadId, checkpoint_id: checkpointId },
308
+ };
309
+ if (typeof before.checkpoint_ns === "string") {
310
+ (cursor.configurable as Record<string, unknown>).checkpoint_ns =
311
+ before.checkpoint_ns;
312
+ }
313
+ return cursor;
314
+ }
315
+
316
+ /**
317
+ * List past thread states for `POST /threads/:threadId/history`.
318
+ *
319
+ * @throws {@link ThreadNotFoundError} When the thread has no checkpoint yet.
320
+ */
321
+ export async function getThreadHistory(
322
+ graph: LocalProtocolGraph,
323
+ threadId: string,
324
+ options: {
325
+ limit?: number;
326
+ before?: unknown;
327
+ metadata?: Record<string, unknown>;
328
+ checkpoint?: Record<string, unknown> | null;
329
+ } = {},
330
+ ): Promise<Record<string, unknown>[]> {
331
+ await getThreadState(graph, threadId);
332
+
333
+ const history: Record<string, unknown>[] = [];
334
+ const iterator = graph.getStateHistory(
335
+ historyConfig(threadId, options.checkpoint),
336
+ {
337
+ before: parseBeforeCursor(threadId, options.before),
338
+ limit: options.limit ?? 10,
339
+ ...(options.metadata ? { filter: options.metadata } : {}),
340
+ },
341
+ );
342
+ for await (const snapshot of iterator) {
343
+ history.push(serializeThreadState(snapshot, threadId));
344
+ }
345
+ return history;
346
+ }
347
+
348
+ /** Choose which graph node should receive an `updateState` write. */
349
+ function resolveUpdateNode(options: {
350
+ asNode?: string;
351
+ values: Record<string, unknown> | null;
352
+ hasCheckpoint: boolean;
353
+ }): string {
354
+ if (options.asNode) return options.asNode;
355
+ const messages = options.values?.messages;
356
+ if (!Array.isArray(messages) || messages.length === 0) {
357
+ return INITIAL_UPDATE_NODE;
358
+ }
359
+ if (!options.hasCheckpoint) return INITIAL_UPDATE_NODE;
360
+ return DEFAULT_UPDATE_NODE;
361
+ }
362
+
363
+ /**
364
+ * Create or update thread state for `POST /threads/:threadId/state`.
365
+ *
366
+ * Used by the browser bootstrap and by the SDK when hydrating or editing
367
+ * conversation history. Applies the update at {@link resolveUpdateNode}, then
368
+ * returns the latest serialized snapshot.
369
+ */
370
+ export async function updateThreadState(
371
+ graph: LocalProtocolGraph,
372
+ threadId: string,
373
+ options: {
374
+ values?: Record<string, unknown> | null;
375
+ checkpoint?: Record<string, unknown> | null;
376
+ asNode?: string;
377
+ } = {},
378
+ ): Promise<Record<string, unknown>> {
379
+ let config = threadConfig(threadId);
380
+ const checkpoint = options.checkpoint;
381
+ if (checkpoint && typeof checkpoint.checkpoint_id === "string") {
382
+ config = {
383
+ configurable: {
384
+ ...configurableOf(config),
385
+ checkpoint_id: checkpoint.checkpoint_id,
386
+ ...(typeof checkpoint.checkpoint_ns === "string"
387
+ ? { checkpoint_ns: checkpoint.checkpoint_ns }
388
+ : {}),
389
+ },
390
+ };
391
+ }
392
+
393
+ const snapshot = await graph.getState(config);
394
+ const resolvedValues = options.values ?? { messages: [] };
395
+ const resolvedAsNode = resolveUpdateNode({
396
+ asNode: options.asNode,
397
+ values: resolvedValues,
398
+ hasCheckpoint: threadHasCheckpoint(snapshot),
399
+ });
400
+
401
+ await graph.updateState(config, resolvedValues, resolvedAsNode);
402
+ const updated = await graph.getState(threadConfig(threadId));
403
+ return serializeThreadState(updated, threadId);
404
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ // https://nuxt.com/docs/guide/concepts/typescript
3
+ "files": [],
4
+ "references": [
5
+ {
6
+ "path": "./.nuxt/tsconfig.app.json"
7
+ },
8
+ {
9
+ "path": "./.nuxt/tsconfig.server.json"
10
+ },
11
+ {
12
+ "path": "./.nuxt/tsconfig.shared.json"
13
+ },
14
+ {
15
+ "path": "./.nuxt/tsconfig.node.json"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,17 @@
1
+ import { createFramework } from "../../src/registry/framework.js";
2
+
3
+ export const nuxt = createFramework({
4
+ id: "nuxt",
5
+ title: "Nuxt",
6
+ defaultProjectName: "nuxt-deepagents",
7
+ address: {
8
+ scheme: "github",
9
+ owner: "aolsenjazz",
10
+ repo: "deployment-cookbook",
11
+ subPath: "js-nuxt",
12
+ },
13
+ envFilePath: ".env",
14
+ packageJsonPath: "package.json",
15
+ agentPath: "server/agent",
16
+ files: [],
17
+ });
@@ -0,0 +1,20 @@
1
+ # Required: OpenAI API key used by the agent and its subagents (server-side only).
2
+ OPENAI_API_KEY=sk-...
3
+
4
+ # LangSmith API key — used for deploy (langgraph deploy / langgraph dev) AND, when
5
+ # talking to a remote deployment, by the browser client. Vite exposes it to the
6
+ # client via `envPrefix` (see vite.config.ts), so there's only one key to manage.
7
+ LANGSMITH_API_KEY=lsv2-...
8
+
9
+ # Optional: name for the deployment (defaults to the directory name).
10
+ # LANGSMITH_DEPLOYMENT_NAME=deployment-cookbook-agent
11
+
12
+ # Optional: enable LangSmith tracing.
13
+ # LANGSMITH_TRACING=true
14
+
15
+ # --- Client (Vite) ---
16
+
17
+ # LangSmith deployment root URL (no path suffix).
18
+ # Local dev: leave unset — Vite proxies /api/langgraph → http://localhost:2025.
19
+ # Production: https://your-app.us.langgraph.app
20
+ VITE_AGENT_API_URL=
@@ -0,0 +1,149 @@
1
+ # Deploying a LangChain Agent with LangSmith + Vite
2
+
3
+ A self-contained Vite + React chat demo with a LangChain **deep agent** backend deployed to [LangSmith Deployment](https://docs.langchain.com/langsmith/deployment). The agent graph lives in `agent/` and is served by the LangGraph Agent Server; the UI in `src/` provides streaming chat, thread history, and subagent/tool-call rendering.
4
+
5
+ One package — `pnpm install`, `pnpm dev` starts LangGraph and Vite together.
6
+
7
+ The frontend talks to LangSmith's built-in [Agent Server](https://docs.langchain.com/langsmith/agent-server) API (`/threads`, `/runs`, …) through `@langchain/react`'s `StreamProvider` and the LangGraph SDK.
8
+
9
+ > [!NOTE]
10
+ > **Two ways to ship this agent on LangSmith.** This example uses a **LangSmith Deployment**: you define the agent graph in code (`agent/`), deploy it to the LangGraph Agent Server, and the server exposes the `/threads` + `/runs` API your frontend streams from. For a **fully-managed** alternative — where LangChain hosts the agent and you deploy a declarative project (`agent.json` + `AGENTS.md` + subagents) instead of a graph — see [`../js-langsmith-managed`](../js-langsmith-managed).
11
+ >
12
+ > | Use this example (LangSmith Deployment) when… | Use [`js-langsmith-managed`](../js-langsmith-managed) (Managed Deep Agent) when… |
13
+ > | ------------------------------------------------------- | -------------------------------------------------------------------------------- |
14
+ > | You want full control over the graph, runtime, and middleware. | You want the least infrastructure — LangChain runs the agent for you. |
15
+ > | You ship custom code tools and bespoke graph logic. | Your tools are MCP servers and behavior fits instructions + skills + subagents. |
16
+ > | You're already on LangGraph and want the standard Agent Server API. | You're in the Managed Deep Agents private preview and want a hosted agent fast. |
17
+
18
+ ## Architecture
19
+
20
+ ```mermaid
21
+ flowchart TB
22
+ subgraph vercel["Vercel (Vite build)"]
23
+ SPA["React SPA"]
24
+ SP["StreamProvider + useStreamContext"]
25
+ SPA --- SP
26
+ end
27
+
28
+ subgraph langsmith["LangSmith Deployment (agent/)"]
29
+ LG["Agent Server /threads /runs"]
30
+ AGT["createDeepAgent graph"]
31
+ LG --> AGT
32
+ end
33
+
34
+ SP -->|"LangGraph SDK"| LG
35
+ ```
36
+
37
+ | Piece | Location | Deploy target |
38
+ | ---------------- | -------- | ------------------------------- |
39
+ | Deep agent graph | `agent/` | LangSmith (`pnpm deploy`) |
40
+ | Chat UI | `src/` | Vercel (`pnpm build` → `dist/`) |
41
+
42
+ ## Deploy the agent to LangSmith
43
+
44
+ ```bash
45
+ cd js-langsmith
46
+ cp .env.example .env # set OPENAI_API_KEY and LANGSMITH_API_KEY
47
+ pnpm install
48
+ pnpm run deploy
49
+ ```
50
+
51
+ This runs [`langgraphjs deploy`](https://docs.langchain.com/langsmith/cli#deploy). Set `LANGSMITH_DEPLOYMENT_NAME` in `.env` to control the deployment name (defaults to the directory name).
52
+
53
+ Copy the deployment **API URL** from the LangSmith UI (e.g. `https://your-app.us.langgraph.app`).
54
+
55
+ LangSmith Deployment provides durable checkpoint storage in production. The in-memory `MemorySaver` in `agent/index.ts` is only used for local `langgraph dev`.
56
+
57
+ ## Deploy the frontend to Vercel
58
+
59
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Flangchain-ai%2Fdeployment-cookbook&root-directory=js-langsmith&env=VITE_AGENT_API_URL,LANGSMITH_API_KEY&envDescription=LangSmith%20deployment%20URL%20and%20API%20key)
60
+
61
+ 1. Connect the repo in Vercel with **Root Directory** `js-langsmith`.
62
+ 2. Vercel auto-detects Vite — build output is `dist/`.
63
+ 3. Set environment variables:
64
+ - `VITE_AGENT_API_URL` — LangSmith deployment root URL, e.g. `https://your-app.us.langgraph.app`
65
+ - `LANGSMITH_API_KEY` — LangSmith API key (exposed to the build via `envPrefix`; preferably proxy through a backend instead)
66
+
67
+ ## CI/CD
68
+
69
+ The agent deploys via GitHub Actions on pushes to `js-langsmith/agent/` (and root config files):
70
+
71
+ | Workflow | Triggers on | Action |
72
+ | ------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------- |
73
+ | [`.github/workflows/deploy-langsmith-agent.yml`](../.github/workflows/deploy-langsmith-agent.yml) | Agent + root config changes | `langgraphjs deploy` to LangSmith |
74
+
75
+ | Name | Description |
76
+ | ----------------------------------------------- | ---------------------------------------- |
77
+ | `LANGSMITH_API_KEY` | LangSmith API key with deployment access |
78
+ | `LANGSMITH_DEPLOYMENT_NAME` (optional variable) | Deployment name override |
79
+
80
+ The frontend deploys through Vercel's Git integration.
81
+
82
+ ## Agent Server API
83
+
84
+ | SDK call | Purpose |
85
+ | ----------------------------------------- | ------------------------------------- |
86
+ | `client.threads.search()` | Thread sidebar |
87
+ | `client.threads.create()` / `delete()` | New / delete conversation |
88
+ | `StreamProvider` + `assistantId: "agent"` | Streaming chat, subagents, tool calls |
89
+
90
+ See the [Agent Server API reference](https://docs.langchain.com/langsmith/server-api-ref).
91
+
92
+ ## Local development
93
+
94
+ ```bash
95
+ cd js-langsmith
96
+ cp .env.example .env # OPENAI_API_KEY (+ LANGSMITH_API_KEY for deploy)
97
+ pnpm install
98
+ pnpm dev
99
+ ```
100
+
101
+ `pnpm dev` starts both:
102
+
103
+ - **LangGraph dev server** on [http://localhost:2025](http://localhost:2025)
104
+ - **Vite** on [http://localhost:5173](http://localhost:5173)
105
+
106
+ Open the Vite URL for the UI. Leave `VITE_AGENT_API_URL` unset — the client uses the Vite dev proxy at `/api/langgraph` (forwards to LangGraph on port 2025, avoiding CORS).
107
+
108
+ Individual processes:
109
+
110
+ ```bash
111
+ pnpm dev:agent # LangGraph only
112
+ pnpm dev:web # Vite only
113
+ ```
114
+
115
+ To test against a remote LangSmith deployment, point the client at it in `.env`
116
+ (it reuses the same `LANGSMITH_API_KEY` you deploy with — Vite exposes `LANGSMITH_`
117
+ vars to the client via `envPrefix`):
118
+
119
+ ```bash
120
+ VITE_AGENT_API_URL=https://your-app.us.langgraph.app
121
+ # LANGSMITH_API_KEY is already set above and is reused by the client.
122
+ ```
123
+
124
+ > [!WARNING]
125
+ > **API key in the browser.** Exposing `LANGSMITH_API_KEY` to the client ships your key in the built bundle — fine for a local demo, not for production. In a real app, proxy requests through your own backend with a custom `fetch` and never expose the key.
126
+
127
+ ## Project layout
128
+
129
+ ```
130
+ js-langsmith/
131
+ ├── package.json
132
+ ├── langgraph.json
133
+ ├── vite.config.ts
134
+ ├── index.html
135
+ ├── tsconfig.json # project references
136
+ ├── tsconfig.app.json # src/ (React)
137
+ ├── tsconfig.node.json # vite.config.ts + agent/
138
+ ├── agent/ # deep agent graph (LangSmith backend)
139
+ ├── src/ # Vite + React SPA
140
+ └── .env.example
141
+ ```
142
+
143
+ ## References
144
+
145
+ - [`js-langsmith-managed`](../js-langsmith-managed) — the same agent deployed as a fully-managed Managed Deep Agent
146
+ - [LangSmith Deployment](https://docs.langchain.com/langsmith/deployment)
147
+ - [LangGraph CLI](https://docs.langchain.com/langsmith/cli)
148
+ - [Deep Agents going to production](https://docs.langchain.com/oss/javascript/deepagents/going-to-production)
149
+ - [Agent Server API reference](https://docs.langchain.com/langsmith/server-api-ref)
@@ -0,0 +1,59 @@
1
+ import { MemorySaver } from "@langchain/langgraph";
2
+ import { ChatOpenAI } from "@langchain/openai";
3
+ import { createDeepAgent } from "deepagents";
4
+
5
+ import { stripReasoningReplay } from "./middleware.js";
6
+ import { calculator, searchWeb } from "./tools.js";
7
+
8
+ const coordinatorModel = new ChatOpenAI({
9
+ model: "gpt-5.4-mini",
10
+ reasoning: { effort: "low", summary: "auto" },
11
+ });
12
+
13
+ const subagentModel = new ChatOpenAI({ model: "gpt-5.4-mini" });
14
+
15
+ /**
16
+ * In-memory checkpointer for local `langgraph dev`.
17
+ *
18
+ * LangSmith Deployment replaces this with durable Postgres-backed storage
19
+ * in production — no code changes required.
20
+ */
21
+ export const checkpointer = new MemorySaver();
22
+
23
+ export const agent = createDeepAgent({
24
+ model: coordinatorModel,
25
+ middleware: [stripReasoningReplay],
26
+ checkpointer,
27
+ subagents: [
28
+ {
29
+ name: "researcher",
30
+ description:
31
+ "Researches a topic using the search_web tool and reports concise findings.",
32
+ tools: [searchWeb],
33
+ model: subagentModel,
34
+ systemPrompt:
35
+ "You are the researcher subagent. Use the search_web tool to look up " +
36
+ "the requested topic, then summarize the findings in two or three " +
37
+ "sentences. Always call search_web at least once before answering.",
38
+ },
39
+ {
40
+ name: "math-whiz",
41
+ description:
42
+ "Performs calculations using the calculator tool and explains the result.",
43
+ tools: [calculator],
44
+ model: subagentModel,
45
+ systemPrompt:
46
+ "You are the math-whiz subagent. Use the calculator tool to evaluate " +
47
+ "the requested expression, then state the result clearly. Always call " +
48
+ "the calculator tool before answering.",
49
+ },
50
+ ],
51
+ systemPrompt:
52
+ "You are a helpful coordinator. When a request involves looking something " +
53
+ "up, delegate it to the `researcher` subagent. When it involves math, " +
54
+ "delegate it to the `math-whiz` subagent. You may run both subagents for a " +
55
+ "single request. After the subagents respond, combine their results into a " +
56
+ "short, clearly labeled final answer.",
57
+ });
58
+
59
+ export type Agent = typeof agent;
@@ -0,0 +1,24 @@
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
+ function sanitizeForReplay(message: BaseMessage): BaseMessage {
9
+ if (!AIMessage.isInstance(message)) return message;
10
+
11
+ return new AIMessage({
12
+ id: message.id,
13
+ content: message.content,
14
+ tool_calls: message.tool_calls,
15
+ invalid_tool_calls: message.invalid_tool_calls,
16
+ usage_metadata: message.usage_metadata,
17
+ });
18
+ }
19
+
20
+ export const stripReasoningReplay = createMiddleware({
21
+ name: "StripReasoningReplay",
22
+ wrapModelCall: async (request, handler) =>
23
+ handler({ ...request, messages: request.messages.map(sanitizeForReplay) }),
24
+ });