@delofarag/ai-utils 1.5.0 → 1.7.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 (92) hide show
  1. package/README.md +648 -97
  2. package/dist/client/index.d.ts +5 -0
  3. package/dist/client/index.d.ts.map +1 -0
  4. package/dist/client/index.js +13 -0
  5. package/dist/client/index.js.map +1 -0
  6. package/dist/heart/agent.d.ts +6 -6
  7. package/dist/heart/agent.d.ts.map +1 -1
  8. package/dist/heart/agent.js +22 -22
  9. package/dist/heart/agent.js.map +1 -1
  10. package/dist/heart/chain.d.ts +4 -8
  11. package/dist/heart/chain.d.ts.map +1 -1
  12. package/dist/heart/chain.js +1 -1
  13. package/dist/heart/chain.js.map +1 -1
  14. package/dist/heart/deepAgent.d.ts +110 -0
  15. package/dist/heart/deepAgent.d.ts.map +1 -0
  16. package/dist/heart/deepAgent.js +304 -0
  17. package/dist/heart/deepAgent.js.map +1 -0
  18. package/dist/heart/tools/RAGTool.d.ts +3 -7
  19. package/dist/heart/tools/RAGTool.d.ts.map +1 -1
  20. package/dist/heart/tools/RAGTool.js +1 -1
  21. package/dist/heart/tools/RAGTool.js.map +1 -1
  22. package/dist/heart/tools/Tavily.d.ts +3 -7
  23. package/dist/heart/tools/Tavily.d.ts.map +1 -1
  24. package/dist/heart/tools/Tavily.js +1 -1
  25. package/dist/heart/tools/Tavily.js.map +1 -1
  26. package/dist/heart/tools/ToolRegistry.d.ts +14 -5
  27. package/dist/heart/tools/ToolRegistry.d.ts.map +1 -1
  28. package/dist/heart/tools/ToolRegistry.js +2 -3
  29. package/dist/heart/tools/ToolRegistry.js.map +1 -1
  30. package/dist/heart/tools/zodios/CombinedRegistry.js +1 -1
  31. package/dist/heart/tools/zodios/ZodiosToolRegistry.js +1 -1
  32. package/dist/helpers/chatbot.d.ts +12 -6
  33. package/dist/helpers/chatbot.d.ts.map +1 -1
  34. package/dist/helpers/chatbot.js +45 -7
  35. package/dist/helpers/chatbot.js.map +1 -1
  36. package/dist/helpers/deepagent/backend.d.ts +92 -0
  37. package/dist/helpers/deepagent/backend.d.ts.map +1 -0
  38. package/dist/helpers/deepagent/backend.js +93 -0
  39. package/dist/helpers/deepagent/backend.js.map +1 -0
  40. package/dist/helpers/deepagent/interruptOn.d.ts +93 -0
  41. package/dist/helpers/deepagent/interruptOn.d.ts.map +1 -0
  42. package/dist/helpers/deepagent/interruptOn.js +190 -0
  43. package/dist/helpers/deepagent/interruptOn.js.map +1 -0
  44. package/dist/helpers/deepagent/interruptTypes.d.ts +65 -0
  45. package/dist/helpers/deepagent/interruptTypes.d.ts.map +1 -0
  46. package/dist/helpers/deepagent/interruptTypes.js +2 -0
  47. package/dist/helpers/deepagent/interruptTypes.js.map +1 -0
  48. package/dist/helpers/deepagent/permissions.d.ts +58 -0
  49. package/dist/helpers/deepagent/permissions.d.ts.map +1 -0
  50. package/dist/helpers/deepagent/permissions.js +72 -0
  51. package/dist/helpers/deepagent/permissions.js.map +1 -0
  52. package/dist/helpers/deepagent/sandbox.d.ts +7 -0
  53. package/dist/helpers/deepagent/sandbox.d.ts.map +1 -0
  54. package/dist/helpers/deepagent/sandbox.js +8 -0
  55. package/dist/helpers/deepagent/sandbox.js.map +1 -0
  56. package/dist/helpers/deepagent/streamEvents.d.ts +10 -0
  57. package/dist/helpers/deepagent/streamEvents.d.ts.map +1 -0
  58. package/dist/helpers/deepagent/streamEvents.js +149 -0
  59. package/dist/helpers/deepagent/streamEvents.js.map +1 -0
  60. package/dist/helpers/helpers.d.ts +4 -3
  61. package/dist/helpers/helpers.d.ts.map +1 -1
  62. package/dist/helpers/helpers.js +25 -0
  63. package/dist/helpers/helpers.js.map +1 -1
  64. package/dist/helpers/llms.d.ts +18 -15
  65. package/dist/helpers/llms.d.ts.map +1 -1
  66. package/dist/helpers/llms.js +15 -2
  67. package/dist/helpers/llms.js.map +1 -1
  68. package/dist/helpers/memory.d.ts +18 -2
  69. package/dist/helpers/memory.d.ts.map +1 -1
  70. package/dist/helpers/memory.js +194 -48
  71. package/dist/helpers/memory.js.map +1 -1
  72. package/dist/helpers/rag.d.ts +1 -1
  73. package/dist/imports.d.ts +14 -2
  74. package/dist/imports.d.ts.map +1 -1
  75. package/dist/imports.js +15 -2
  76. package/dist/imports.js.map +1 -1
  77. package/dist/index.d.ts +8 -2
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +6 -2
  80. package/dist/index.js.map +1 -1
  81. package/dist/magic-funcs/evaluators/classify.d.ts +3 -11
  82. package/dist/magic-funcs/evaluators/classify.d.ts.map +1 -1
  83. package/dist/magic-funcs/evaluators/classify.js +1 -1
  84. package/dist/magic-funcs/evaluators/decide.d.ts +7 -11
  85. package/dist/magic-funcs/evaluators/decide.d.ts.map +1 -1
  86. package/dist/magic-funcs/evaluators/decide.js +1 -1
  87. package/dist/magic-funcs/parsers/extract.d.ts +2 -2
  88. package/dist/magic-funcs/parsers/extract.d.ts.map +1 -1
  89. package/dist/magic-funcs/parsers/extract.js.map +1 -1
  90. package/dist/magic-funcs/parsers/structure.d.ts +1 -1
  91. package/dist/magic-funcs/parsers/structure.js.map +1 -1
  92. package/package.json +28 -13
package/README.md CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  Ein praktisches Utility-Package für LLM-Apps mit LangChain:
4
4
 
5
- - `Chain`, `MemoryChain`, `Agent`
5
+ - `Chain`, `Agent`, `DeepAgent` (Filesystem, HITL, Sandboxes, Subagents)
6
+ - Memory via Checkpoint-Saver (`MemorySaver`, `SmartCheckpointSaver`, `SupabaseCheckpointSaver`)
6
7
  - RAG-Helper (FAISS, Supabase, In-Memory)
7
- - Tooling (`ToolRegistry`, `createRAGTool`, `tavilySearchTool`)
8
+ - Tooling (`ToolRegistry`, `CombinedToolRegistry`, `ZodiosToolRegistry`, `createRAGTool`, `tavilySearchTool`)
8
9
  - Magic-Funcs (Parser, Evaluator, Optimizer, Answerer)
9
10
  - Modalities (STT, TTS, Vision, Image Generation)
10
11
 
@@ -19,7 +20,7 @@ Im Package gilt als Standard-LLM-Default für die allgemeine Nutzung:
19
20
 
20
21
  Wenn du nichts explizit setzt, orientiere dich an diesem Default in deinen Aufrufen.
21
22
 
22
- Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusätzlich mit `type` gearbeitet, damit passende Modelle gewählt werden koennen.
23
+ Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusätzlich mit `type` gearbeitet, damit passende Modelle gewählt werden können.
23
24
 
24
25
  ---
25
26
 
@@ -29,6 +30,12 @@ Für modality-spezifische Flows (`stt`, `tts`, `vision`, `image-gen`) wird zusä
29
30
  npm i @delofarag/ai-utils
30
31
  ```
31
32
 
33
+ Peer dependency für Supabase-Features:
34
+
35
+ ```bash
36
+ npm i @delofarag/supabase-utils
37
+ ```
38
+
32
39
  ---
33
40
 
34
41
  ## Environment Variables
@@ -44,12 +51,12 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=...
44
51
  SUPABASE_SERVICE_ROLE_KEY=...
45
52
  ```
46
53
 
47
- ### Welche Variable wofuer?
54
+ ### Welche Variable wofür?
48
55
 
49
56
  - `OPENROUTER_API_KEY`: OpenRouter-Modelle und Modalities
50
57
  - `CHATGROQ_API_KEY`: wenn du `provider: "chatgroq"` nutzt
51
58
  - `TAVILY_API_KEY`: `websearch()` / `tavilySearchTool`
52
- - Supabase-Variablen: fuer `createSupabaseVectoreStore()` und `getSupabaseVectorStore()`
59
+ - Supabase-Variablen: für `createSupabaseVectoreStore()`, `getSupabaseVectorStore()` und `SupabaseCheckpointSaver`
53
60
 
54
61
  ---
55
62
 
@@ -78,19 +85,27 @@ getLLM({ provider: "openrouter", type: "vision" })
78
85
  getLLM({ provider: "openrouter", type: "image-gen" })
79
86
  ```
80
87
 
88
+ EU-Datenrouting (OpenRouter):
89
+
90
+ ```ts
91
+ getLLM({ provider: "openrouter", dataSafe: true })
92
+ ```
93
+
81
94
  ---
82
95
 
83
96
  ## Core Classes
84
97
 
98
+ > **DeepAgent-Dokumentation:** Abschnitt [3) DeepAgent](#3-deepagent) — Feature-Übersicht, Backend, HITL, Stream-Chunks, CLI-Testing.
99
+
85
100
  ## 1) `Chain`
86
101
 
87
- Stateless LLM-Chain fuer strukturierte Ergebnisse.
102
+ Stateless LLM-Chain für strukturierte Ergebnisse. Kein Memory — `thread_id` wird ignoriert.
88
103
 
89
- ### Besseres Praxisbeispiel (custom output schema)
104
+ ### Custom Output Schema
90
105
 
91
106
  ```ts
92
107
  import { Chain, getLLM } from "@delofarag/ai-utils"
93
- import { z } from "zod/v3"
108
+ import { z } from "zod/v4"
94
109
 
95
110
  const productBriefSchema = z.object({
96
111
  title: z.string().describe("Kurzer Produktname"),
@@ -106,7 +121,7 @@ const chain = new Chain({
106
121
  })
107
122
 
108
123
  const result = await chain.invoke({
109
- product: "AI-Notizapp fuer Teams",
124
+ product: "AI-Notizapp für Teams",
110
125
  market: "DACH SaaS"
111
126
  })
112
127
  ```
@@ -115,7 +130,7 @@ const result = await chain.invoke({
115
130
 
116
131
  ```ts
117
132
  import { Chain, createFaissStore } from "@delofarag/ai-utils"
118
- import { z } from "zod/v3"
133
+ import { z } from "zod/v4"
119
134
 
120
135
  const vectorStore = await createFaissStore(["Dokument A", "Dokument B"])
121
136
 
@@ -130,121 +145,560 @@ await chain.addContext(["Dokument C"])
130
145
  const answer = await chain.invoke({ question: "Was steht in Dokument C?" })
131
146
  ```
132
147
 
148
+ ### Streaming
149
+
150
+ ```ts
151
+ for await (const chunk of chain.stream({ question: "Erkläre das kurz." })) {
152
+ process.stdout.write(chunk)
153
+ }
154
+ ```
155
+
133
156
  ---
134
157
 
135
- ## 2) `MemoryChain`
158
+ ## 2) `Agent`
136
159
 
137
- `Chain` + Conversation Memory ueber `thread_id`.
160
+ Tool-using Agent auf Basis von `createReactAgent`. Unterstützt optional Checkpointer (Thread-State) und strukturierten Output.
138
161
 
139
162
  ### Basis
140
163
 
141
164
  ```ts
142
- import { MemoryChain, getLLM } from "@delofarag/ai-utils"
165
+ import { Agent, ToolRegistry, getLLM } from "@delofarag/ai-utils"
166
+ import { z } from "zod/v4"
167
+
168
+ const tools = new ToolRegistry([
169
+ {
170
+ name: "sum",
171
+ description: "Addiert zwei Zahlen",
172
+ schema: z.object({ a: z.number(), b: z.number() }),
173
+ func: ({ a, b }) => a + b
174
+ }
175
+ ]).allTools
143
176
 
144
- const memoryChain = new MemoryChain({
177
+ const agent = new Agent({
145
178
  llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
146
- prompt: "Du bist ein hilfreicher Assistent."
179
+ prompt: "Du darfst Tools nutzen wenn nötig.",
180
+ tools
147
181
  })
148
182
 
149
- await memoryChain.invoke({ thread_id: "u1", input: "Ich heisse Max." })
150
- const r2 = await memoryChain.invoke({ thread_id: "u1", input: "Wie heisse ich?" })
183
+ const result = await agent.invoke({ input: "Was ist 8 + 13?" })
151
184
  ```
152
185
 
153
- ### RAG mit `MemoryChain`
186
+ ### Checkpointer mit `Agent`
187
+
188
+ Conversation State läuft über Checkpoint-Saver + `thread_id`:
154
189
 
155
190
  ```ts
156
- import { MemoryChain, createFaissStore } from "@delofarag/ai-utils"
157
- import { z } from "zod/v3"
191
+ import { Agent, MemorySaver, SmartCheckpointSaver, getLLM } from "@delofarag/ai-utils"
192
+
193
+ const checkpointer = new SmartCheckpointSaver(new MemorySaver(), {
194
+ llm: getLLM({ provider: "openrouter" }),
195
+ messagesBeforeSummary: 12,
196
+ maxSummaries: 7
197
+ })
158
198
 
159
- const vectorStore = await createFaissStore(["Policy A", "Policy B"])
199
+ const agent = new Agent({
200
+ tools: [...],
201
+ prompt: "Du bist ein hilfreicher Assistent.",
202
+ checkpointer
203
+ })
160
204
 
161
- const memoryChain = new MemoryChain({
162
- prompt: "Nutze Kontext und Gespraechshistorie.",
205
+ await agent.invoke({ thread_id: "u1", input: "Ich heisse Max." })
206
+ const r2 = await agent.invoke({ thread_id: "u1", input: "Wie heisse ich?" })
207
+ ```
208
+
209
+ ### Strukturierter Output
210
+
211
+ ```ts
212
+ const agent = new Agent({
213
+ tools,
214
+ output: z.object({
215
+ answer: z.string(),
216
+ confidence: z.number()
217
+ })
218
+ })
219
+
220
+ const result = await agent.invoke({ input: "Analysiere das..." })
221
+ // result: { answer: string, confidence: number }
222
+ ```
223
+
224
+ ### RAG als Tool
225
+
226
+ ```ts
227
+ import { Agent, createRAGTool, createFaissStore } from "@delofarag/ai-utils"
228
+
229
+ const vectorStore = await createFaissStore(["Release Notes 2026-04", "Known Issues"])
230
+ const ragTool = createRAGTool({
163
231
  vectorStore,
164
- output: z.object({ output: z.string() })
232
+ name: "search_docs",
233
+ description: "Sucht relevante Produktdokumente"
165
234
  })
166
235
 
167
- const response = await memoryChain.invoke({
168
- thread_id: "support-77",
169
- question: "Welche Regel steht in Policy B?"
236
+ const agent = new Agent({
237
+ prompt: "Nutze search_docs für faktenbasierte Antworten.",
238
+ tools: [ragTool]
170
239
  })
171
240
  ```
172
241
 
242
+ ### Streaming
243
+
244
+ ```ts
245
+ for await (const chunk of agent.stream({ input: "Erkläre mir das.", thread_id: "u1" })) {
246
+ process.stdout.write(chunk)
247
+ }
248
+ ```
249
+
173
250
  ---
174
251
 
175
- ## 3) `Agent`
252
+ ## 3) `DeepAgent`
253
+
254
+ LangChain Deep Agent (`createDeepAgent()`) als typisierte Wrapper-Klasse. Für autonome Tasks mit Filesystem, Planning, Subagents und optional Sandboxes.
255
+
256
+ ### DeepAgent auf einen Blick
257
+
258
+ | Feature | Kurz erklärt |
259
+ |---|---|
260
+ | **Filesystem-Tools** | `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep` (built-in) |
261
+ | **Custom Tools** | Eigene Tools via `ToolRegistry` / `DynamicStructuredTool` |
262
+ | **`execute`** | Shell-Befehle — nur mit Shell/Sandbox-Backend (`createLocalShellBackend`, Deno, Daytona) |
263
+ | **`backend`** | Wo Dateien physisch liegen + wie der Agent Pfade sieht |
264
+ | **`permissions`** | Statische FS-Regeln (allow/deny) — **kein** User-Dialog |
265
+ | **`interruptOn`** | Human-in-the-Loop — pausiert **vor** Tool-Ausführung, User entscheidet |
266
+ | **`checkpointer` + `thread_id`** | Conversation-State über Runs hinweg (Pflicht für HITL) |
267
+ | **`invoke` / `stream`** | Gleiche API für Message **und** HITL-Resume via `decision` |
268
+ | **`showToolCalls`** | Streamt `[tool:start]` / `[tool:end]` Events (nur `stream()`) |
269
+ | **`agentsMd`** | AGENTS.md-Dateien als Startup-Kontext |
270
+ | **`subagents`** | Delegation an spezialisierte Sub-Agents |
271
+ | **`skills`** | Skill-Dateien vom Backend laden |
272
+ | **`output`** | Optional strukturierter Zod-Output |
273
+
274
+ ### Wann `DeepAgent` statt `Agent`?
275
+
276
+ | | `Agent` | `DeepAgent` |
277
+ |---|---|---|
278
+ | Tool-using ReAct | ja | ja |
279
+ | Built-in Filesystem | nein | ja |
280
+ | Backend / Sandbox | nein | ja |
281
+ | Subagents / Skills | nein | ja |
282
+ | HITL (`interruptOn`) | nein | ja |
283
+ | Typischer Use-Case | Chatbots, API-Tools | Coding Agents, autonome Tasks |
176
284
 
177
- Tool-using Agent auf Basis von `createReactAgent`.
285
+ ---
178
286
 
179
- ### Basis
287
+ ### Alle Props
288
+
289
+ `DeepAgent` wrappt intern LangChains `createDeepAgent()`. Die Tabelle unten listet die Props, die du über `@delofarag/ai-utils` setzt — für den **vollen Einblick aller nativen Parameter** (z. B. `cache`, `debug`, Middleware-Details, Backend-Protokolle) in die **LangChain Deep Agents Docs** schauen:
290
+
291
+ - [Deep Agents Overview](https://docs.langchain.com/oss/javascript/deepagents/overview)
292
+ - [`createDeepAgent()` Reference (JS)](https://reference.langchain.com/javascript/deepagents/agent/createDeepAgent)
293
+
294
+ Einige Namen weichen in ai-utils ab: `prompt` → `systemPrompt`, `agentsMd` → `memory`, `interruptOn` → `interrupt_on`.
180
295
 
181
296
  ```ts
182
- import { Agent, ToolRegistry, getLLM } from "@delofarag/ai-utils"
183
- import { z } from "zod/v3"
297
+ new DeepAgent({
298
+ prompt, // string | string[] System-Prompt(s)
299
+ llm, // BaseChatModel (Default: OpenRouter gpt-5.4-mini)
300
+ tools, // readonly DynamicStructuredTool[] (z.B. ToolRegistry.allTools)
301
+ output, // Zod-Schema für strukturierten Output
302
+ checkpointer, // BaseCheckpointSaver | boolean (Default: MemorySaver)
303
+ backend, // DeepAgentBackend — siehe Backend-Abschnitt
304
+ permissions, // FilesystemPermission[] — statische FS-Regeln
305
+ interruptOn, // pro Tool: decisions + question (HITL)
306
+ agentsMd, // string[] — Pfade zu AGENTS.md
307
+ subagents, // SubAgent[]
308
+ skills, // string[] — Skill-Pfade relativ zum Backend
309
+ middleware, // AgentMiddleware[]
310
+ store, // BaseStore — LangGraph Store
311
+ name, // Agent-Name
312
+ contextSchema, // Runtime-Context-Schema
313
+ })
314
+ ```
315
+
316
+ **Wichtig:** `backend` erwartet eine **Instanz** (oder Factory) — kein `Promise`. Async Backends vorher mit `await` erstellen:
317
+
318
+ ```ts
319
+ // ❌ backend: createLocalShellBackend({ ... })
320
+ // ✅ backend: await createLocalShellBackend({ ... })
321
+ ```
322
+
323
+ Typ: `DeepAgentBackend` (= native deepagents `AnyBackendProtocol | Factory`).
324
+
325
+ ---
326
+
327
+ ### Basis: Coding Agent mit isoliertem Workspace
328
+
329
+ ```ts
330
+ import path from "node:path"
331
+ import {
332
+ DeepAgent,
333
+ createLocalShellBackend,
334
+ workspacePermissions,
335
+ ToolRegistry,
336
+ MemorySaver,
337
+ getLLM,
338
+ } from "@delofarag/ai-utils"
339
+ import { z } from "zod/v4"
184
340
 
185
341
  const tools = new ToolRegistry([
186
342
  {
187
- name: "sum",
188
- description: "Addiert zwei Zahlen",
189
- schema: z.object({ a: z.number(), b: z.number() }),
190
- func: ({ a, b }) => a + b
191
- }
343
+ name: "get_weather",
344
+ description: "Wetter für eine Stadt",
345
+ schema: z.object({ city: z.string() }),
346
+ func: async ({ city }) => `${city}: sonnig`,
347
+ },
192
348
  ]).allTools
193
349
 
194
- const agent = new Agent({
350
+ const agent = new DeepAgent({
195
351
  llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
196
- prompt: "Du darfst Tools nutzen wenn noetig.",
197
- tools
352
+ prompt: "Du bist ein Coding Agent. Arbeite nur im Workspace.",
353
+ tools,
354
+ checkpointer: new MemorySaver(),
355
+ backend: await createLocalShellBackend({
356
+ rootDir: path.join(process.cwd(), "coding_space"),
357
+ route: "/workspace/",
358
+ }),
359
+ permissions: workspacePermissions("/workspace/"),
198
360
  })
199
361
 
200
- const result = await agent.invoke({ input: "Was ist 8 + 13?" })
362
+ const answer = await agent.invoke({
363
+ input: "Erstelle eine kleine HTML-Seite",
364
+ thread_id: "session-1",
365
+ })
201
366
  ```
202
367
 
203
- ### RAG mit `Agent` (als Tool)
368
+ ---
369
+
370
+ ### Backend: `rootDir` vs `route`
371
+
372
+ Zwei getrennte Konzepte — oft verwechselt:
373
+
374
+ | Prop | Bedeutung | Beispiel |
375
+ |---|---|---|
376
+ | **`rootDir`** | Echter Ordner auf der Festplatte | `"./coding_space"` oder absoluter Pfad |
377
+ | **`route`** | Virtueller Pfad, den der **Agent sieht** | `"/workspace/"` |
378
+
379
+ Mapping:
380
+
381
+ ```
382
+ Agent schreibt: /workspace/index.html
383
+
384
+ Physisch auf Disk: coding_space/index.html
385
+ ```
386
+
387
+ Der Agent kennt `coding_space` nicht — nur `/workspace/`. Du entscheidest mit `rootDir`, wo Dateien wirklich landen.
388
+
389
+ **Relative Pfade für `rootDir` funktionieren** — deepagents resolved sie via `path.resolve(rootDir)`.
390
+
391
+ ---
392
+
393
+ ### Backend-Helper
394
+
395
+ | Helper | Async? | `execute`? | Wofür |
396
+ |---|---|---|---|
397
+ | `createStateBackend()` | nein | nein | Ephemeral — Dateien nur im Agent-State |
398
+ | `createFilesystemBackend({ rootDir })` | nein | nein | Direktes FS ohne StateBackend |
399
+ | `createWorkspaceBackend({ rootDir, route? })` | nein | nein | StateBackend + FS unter `route` (empfohlen für reines Coden) |
400
+ | `createLocalShellBackend({ rootDir, route? })` | **ja** | **ja** | Wie Workspace + Shell auf Host (**nur Dev**) |
401
+ | `createDenoSandbox()` | **ja** | ja | Isolierte Deno-Sandbox |
402
+ | `createDaytonaSandbox()` | **ja** | ja | Isolierte Daytona-Sandbox |
204
403
 
205
404
  ```ts
206
- import { Agent, ToolRegistry, createRAGTool, createFaissStore } from "@delofarag/ai-utils"
405
+ // Reines Filesystem (kein Shell)
406
+ backend: createWorkspaceBackend({
407
+ rootDir: path.join(process.cwd(), "coding_space"),
408
+ route: "/workspace/",
409
+ })
207
410
 
208
- const vectorStore = await createFaissStore(["Release Notes 2026-04", "Known Issues"])
209
- const ragTool = createRAGTool({
210
- vectorStore,
211
- name: "search_docs",
212
- description: "Sucht relevante Produktdokumente"
411
+ // Mit Shell (Dev only execute läuft auf dem Host!)
412
+ backend: await createLocalShellBackend({
413
+ rootDir: path.join(process.cwd(), "coding_space"),
414
+ route: "/workspace/",
213
415
  })
416
+ ```
214
417
 
215
- const registry = new ToolRegistry([
216
- {
217
- name: "search_docs",
218
- description: "Sucht relevante Produktdokumente",
219
- schema: ragTool.schema as any,
220
- func: ragTool.func as any
418
+ ---
419
+
420
+ ### `permissions` vs `interruptOn`
421
+
422
+ Zwei verschiedene Sicherheitsmechanismen:
423
+
424
+ | | `permissions` | `interruptOn` |
425
+ |---|---|---|
426
+ | **Was** | Statische Regeln | User-Dialog vor Tool-Ausführung |
427
+ | **Wann** | Sofort beim Tool-Call | Pause + warte auf Entscheidung |
428
+ | **Tools** | Nur FS: `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep` | Alle konfigurierten Tools inkl. Custom + `execute` |
429
+ | **`execute`** | Wird **nicht** enforced | Kann konfiguriert werden |
430
+
431
+ Helper für `permissions`:
432
+
433
+ ```ts
434
+ import { workspacePermissions, allowRead, denyWrite } from "@delofarag/ai-utils"
435
+
436
+ permissions: workspacePermissions("/workspace/")
437
+ // = lesen + schreiben nur unter /workspace/**, sonst deny
438
+ ```
439
+
440
+ ---
441
+
442
+ ### Built-in Filesystem-Tools
443
+
444
+ DeepAgent bringt diese Tools automatisch mit (via deepagents Middleware):
445
+
446
+ - `ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`
447
+ - `execute` — nur wenn Backend Shell/Sandbox unterstützt
448
+
449
+ Custom Tools aus `ToolRegistry` kommen dazu. Für `interruptOn`-Autocomplete werden alle verfügbaren Tool-Namen typisiert.
450
+
451
+ ---
452
+
453
+ ### `invoke()` — Message und HITL-Resume in einer API
454
+
455
+ **Normale Message:**
456
+
457
+ ```ts
458
+ const result = await agent.invoke({
459
+ input: "Analysiere die Codebase",
460
+ thread_id: "u1",
461
+ })
462
+ // result: string (oder Zod-Output wenn output gesetzt)
463
+ ```
464
+
465
+ **Nach Interrupt — Resume:**
466
+
467
+ ```ts
468
+ let result = await agent.invoke({ input: "Schreib eine Datei", thread_id: "u1" })
469
+
470
+ if (typeof result === "object" && result.kind === "interrupt") {
471
+ result = await agent.invoke({
472
+ thread_id: "u1",
473
+ decision: "approve",
474
+ })
475
+ }
476
+ ```
477
+
478
+ **Entscheidungs-Typen (`DeepAgentUserDecision`):**
479
+
480
+ ```ts
481
+ "approve"
482
+ "reject"
483
+ { type: "reject", message: "Zu gefährlich" }
484
+ { type: "edit", args: { file_path: "/workspace/safe.txt", content: "..." } }
485
+ ```
486
+
487
+ **Mehrere parallele Tool-Calls (Batch-Interrupt):**
488
+
489
+ ```ts
490
+ await agent.invoke({
491
+ thread_id: "u1",
492
+ decisions: ["approve", "approve"],
493
+ })
494
+ ```
495
+
496
+ Regeln:
497
+ - `thread_id` **Pflicht** wenn `checkpointer` gesetzt
498
+ - `decision`/`decisions` und `input` **nicht gleichzeitig**
499
+ - `checkpointer` **Pflicht** für HITL-Resume
500
+
501
+ ---
502
+
503
+ ### Human-in-the-Loop (`interruptOn`)
504
+
505
+ Pausiert den Agent **vor** Tool-Ausführung. Tools die **nicht** im Objekt stehen, laufen ohne Pause.
506
+
507
+ ```ts
508
+ const agent = new DeepAgent({
509
+ checkpointer: new MemorySaver(),
510
+ tools: myTools,
511
+ interruptOn: {
512
+ write_file: {
513
+ decisions: ["approve", "reject"],
514
+ question: "Datei schreiben?",
515
+ },
516
+ edit_file: {
517
+ decisions: ["approve", "edit", "reject"],
518
+ question: (call) =>
519
+ `Editieren?\nPfad: ${call.args.file_path ?? "?"}`,
520
+ },
521
+ get_weather: {
522
+ decisions: ["approve", "reject"],
523
+ question: (call) => `Wetter für ${call.args.city} abrufen?`,
524
+ },
525
+ execute: {
526
+ decisions: ["approve", "reject"],
527
+ question: "Shell-Befehl ausführen?",
528
+ },
529
+ },
530
+ })
531
+ ```
532
+
533
+ **Config pro Tool:**
534
+
535
+ | Feld | Typ | Beschreibung |
536
+ |---|---|---|
537
+ | `decisions` | `("approve" \| "edit" \| "reject")[]` | Erlaubte User-Antworten |
538
+ | `question` | `string \| (toolCall) => string` | Frage an den User (statisch oder dynamisch) |
539
+
540
+ **Typisierte Keys (Autocomplete):** alle FS-Tools + deine Custom-Tools + `execute` (wenn Shell-Backend). Mit `interruptOn` gesetzt liefert `invoke()`/`stream()` zusätzlich `DeepAgentInterrupt`-Chunks.
541
+
542
+ HITL-Helper (optional, low-level):
543
+
544
+ ```ts
545
+ import {
546
+ approveDecision,
547
+ rejectDecision,
548
+ editDecision,
549
+ approveAll,
550
+ createResumeCommand,
551
+ isInterruptResult,
552
+ mapResultToInterrupt,
553
+ } from "@delofarag/ai-utils"
554
+ ```
555
+
556
+ ---
557
+
558
+ ### `stream()` — Text, Interrupts und Tool-Events
559
+
560
+ Gleiche Input-API wie `invoke()` — plus optionale Tool-Events:
561
+
562
+ ```ts
563
+ for await (const chunk of agent.stream({
564
+ input: "Baue eine Website",
565
+ thread_id: "u1",
566
+ showToolCalls: true,
567
+ })) {
568
+ if (typeof chunk === "string") {
569
+ process.stdout.write(chunk)
570
+ } else if (chunk.kind === "interrupt") {
571
+ console.log("Interrupt:", chunk.question)
572
+ console.log("Decisions:", chunk.decisions)
573
+ } else if (chunk.kind === "tool") {
574
+ console.log(`[tool:${chunk.phase}]`, chunk.toolName)
221
575
  }
222
- ])
576
+ }
577
+ ```
223
578
 
224
- const agent = new Agent({
225
- prompt: "Nutze search_docs fuer faktenbasierte Antworten.",
226
- tools: [...registry.allTools, ragTool]
579
+ **Stream-Chunk-Typen:**
580
+
581
+ | Chunk | Wann | Shape |
582
+ |---|---|---|
583
+ | `string` | LLM-Text-Tokens | `"Hallo..."` |
584
+ | `DeepAgentInterrupt` | HITL-Pause | `{ kind: "interrupt", question, decisions, toolName?, args? }` |
585
+ | `DeepAgentInterruptBatch` | Mehrere Tools gleichzeitig | `{ kind: "interrupt", items: [...] }` |
586
+ | `DeepAgentToolEvent` | Mit `showToolCalls: true` | `{ kind: "tool", phase: "start"\|"end", toolName, args? }` |
587
+
588
+ **Resume im Stream:**
589
+
590
+ ```ts
591
+ for await (const chunk of agent.stream({
592
+ thread_id: "u1",
593
+ decision: "approve",
594
+ showToolCalls: true,
595
+ })) { ... }
596
+ ```
597
+
598
+ Frontend ohne LangChain-Bundle:
599
+
600
+ ```ts
601
+ import { isInterrupt, isToolEvent } from "@delofarag/ai-utils/client"
602
+ ```
603
+
604
+ ---
605
+
606
+ ### `agentsMd` vs `checkpointer`
607
+
608
+ | Prop | LangChain-Parameter | Bedeutung |
609
+ |---|---|---|
610
+ | `checkpointer` | `checkpointer` | Thread-State über `thread_id` (Chat-Verlauf) |
611
+ | `agentsMd` | `memory` | AGENTS.md-Pfade als Startup-Kontext (kein Chat-Memory) |
612
+
613
+ ```ts
614
+ const agent = new DeepAgent({
615
+ agentsMd: ["./AGENTS.md", "./.deepagents/AGENTS.md"],
616
+ checkpointer: new MemorySaver(),
617
+ })
618
+ ```
619
+
620
+ ---
621
+
622
+ ### Skills
623
+
624
+ Skill-Dateien vom Backend laden (deepagents native Feature):
625
+
626
+ ```ts
627
+ const agent = new DeepAgent({
628
+ backend: await createLocalShellBackend({ rootDir: "./coding_space" }),
629
+ skills: ["./skills/refactor/SKILL.md", "./skills/test/SKILL.md"],
630
+ })
631
+ ```
632
+
633
+ Pfade relativ zum Backend — der Agent kann Skills zur Laufzeit einlesen.
634
+
635
+ ---
636
+
637
+ ### Sandbox (Production)
638
+
639
+ ```ts
640
+ import { DeepAgent, createDenoSandbox } from "@delofarag/ai-utils"
641
+
642
+ const sandbox = await createDenoSandbox()
643
+ const agent = new DeepAgent({
644
+ backend: sandbox,
645
+ permissions: workspacePermissions("/workspace/"),
227
646
  })
647
+
648
+ // sandbox.close() wenn fertig
649
+ ```
650
+
651
+ ---
652
+
653
+ ### Runtime-Methoden
654
+
655
+ ```ts
656
+ agent.addTool(extraTool) // Tool nachträglich hinzufügen
657
+ agent.currentTools // string[] — alle registrierten Tool-Namen
658
+ ```
659
+
660
+ ---
661
+
662
+ ### API-Route Pattern (NDJSON Stream)
663
+
664
+ ```ts
665
+ import { StreamResponse } from "@delofarag/ai-utils"
666
+ import { isInterrupt } from "@delofarag/ai-utils/client"
667
+
668
+ // Gleiche stream()-Methode für Message und HITL-Resume
669
+ return StreamResponse(
670
+ agent.stream({
671
+ thread_id,
672
+ ...(decision ? { decision } : { input: message }),
673
+ showToolCalls: true,
674
+ })
675
+ )
228
676
  ```
229
677
 
678
+ Response: `Content-Type: application/x-ndjson` — ein JSON-Objekt pro Zeile.
679
+
230
680
  ---
231
681
 
232
- ## Tool Registry (eigene Section)
682
+ ## Tool Registry
683
+
684
+ ### `ToolRegistry`
685
+
686
+ Konvertiert einfache Tool-Definitionen zu `DynamicStructuredTool`:
233
687
 
234
- `ToolRegistry` konvertiert einfache Tool-Definitionen zu `DynamicStructuredTool` und bietet:
688
+ - `getTool(name)` wirft `Error` wenn Tool nicht existiert (kein `undefined`)
689
+ - `getTools(...names)` — mehrere Tools, wirft bei unbekanntem Namen
690
+ - `allTools` — alle Tools als Array
235
691
 
236
- - `getTool(name)`
237
- - `getTools(...names)`
238
- - `allTools`
692
+ Tool-Namen werden als Literal-Typen erhalten — wichtig für `interruptOn`-Autocomplete bei `new DeepAgent()`.
239
693
 
240
694
  ```ts
241
695
  import { ToolRegistry } from "@delofarag/ai-utils"
242
- import { z } from "zod/v3"
696
+ import { z } from "zod/v4"
243
697
 
244
698
  const registry = new ToolRegistry([
245
699
  {
246
700
  name: "get_weather",
247
- description: "Liefert Wetter fuer eine Stadt",
701
+ description: "Liefert Wetter für eine Stadt",
248
702
  schema: z.object({ city: z.string() }),
249
703
  func: async ({ city }) => `${city}: sonnig`
250
704
  },
@@ -260,7 +714,28 @@ const weatherTool = registry.getTool("get_weather")
260
714
  const tools = registry.allTools
261
715
  ```
262
716
 
263
- ### Tavily Tooling
717
+ ### `CombinedToolRegistry` + `ZodiosToolRegistry`
718
+
719
+ Kombiniert manuelle Tools mit einem Zodios-API-Client (max. 1 Client):
720
+
721
+ ```ts
722
+ import { CombinedToolRegistry } from "@delofarag/ai-utils"
723
+ import { Zodios } from "zodios"
724
+
725
+ const registry = new CombinedToolRegistry([
726
+ {
727
+ name: "get_weather",
728
+ description: "Wetter abfragen",
729
+ schema: z.object({ city: z.string() }),
730
+ func: async ({ city }) => `${city}: sonnig`
731
+ },
732
+ myZodiosClient
733
+ ] as const)
734
+
735
+ const agent = new Agent({ tools: registry.allTools })
736
+ ```
737
+
738
+ ### Tavily
264
739
 
265
740
  ```ts
266
741
  import { tavilySearchTool, TavilySearch } from "@delofarag/ai-utils"
@@ -277,14 +752,16 @@ const response = await tavily.invoke({ query: "latest AI regulation EU" })
277
752
 
278
753
  ---
279
754
 
280
- ## Memory Section (ausfuehrlich)
755
+ ## Memory (Checkpoint-Saver)
281
756
 
282
- ### `MemorySaver` (in-memory, schnell fuer local/dev)
757
+ Thread-State wird über LangGraph Checkpoint-Saver an `Agent` / `DeepAgent` gehängt — nicht über eine eigene Chain-Klasse.
758
+
759
+ ### `MemorySaver` (in-memory, schnell für local/dev)
283
760
 
284
761
  ```ts
285
762
  import { MemorySaver, SmartCheckpointSaver, getLLM } from "@delofarag/ai-utils"
286
763
 
287
- const memory = new SmartCheckpointSaver(new MemorySaver(), {
764
+ const checkpointer = new SmartCheckpointSaver(new MemorySaver(), {
288
765
  llm: getLLM({ provider: "openrouter", model: "openai/gpt-5.4-mini" }),
289
766
  messagesBeforeSummary: 12,
290
767
  maxSummaries: 7
@@ -293,13 +770,11 @@ const memory = new SmartCheckpointSaver(new MemorySaver(), {
293
770
 
294
771
  ### `SmartCheckpointSaver`
295
772
 
296
- Was es macht:
297
-
298
- - fasst alte Chatverlaeufe zusammen
773
+ - fasst alte Chatverläufe zusammen
299
774
  - reduziert Token-Kosten
300
- - erhaelt wichtige Fakten ueber mehrere Sessions
775
+ - erhält wichtige Fakten über mehrere Sessions
301
776
 
302
- Wichtige Optionen:
777
+ Optionen:
303
778
 
304
779
  - `messagesBeforeSummary` (default `12`)
305
780
  - `maxSummaries` (default `7`)
@@ -308,7 +783,7 @@ Wichtige Optionen:
308
783
 
309
784
  ### `SupabaseCheckpointSaver`
310
785
 
311
- Persistiert Checkpoints in Supabase.
786
+ Persistiert Checkpoints in Supabase:
312
787
 
313
788
  ```ts
314
789
  import { SupabaseCheckpointSaver, type SupabaseCheckpointRow } from "@delofarag/ai-utils"
@@ -319,22 +794,26 @@ const checkpointsTable = new SupabaseTable<SupabaseCheckpointRow>({
319
794
  })
320
795
 
321
796
  const saver = new SupabaseCheckpointSaver(checkpointsTable)
797
+
798
+ const agent = new Agent({
799
+ tools: [...],
800
+ checkpointer: saver
801
+ })
322
802
  ```
323
803
 
324
- Typischer Einsatz:
804
+ ### Checkpoint-Helpers
325
805
 
326
806
  ```ts
327
- import { MemoryChain } from "@delofarag/ai-utils"
328
-
329
- const memoryChain = new MemoryChain({
330
- prompt: "Du bist ein Support Assistant.",
331
- memory: saver
332
- })
807
+ import {
808
+ formatCheckpointMessagesForLLM,
809
+ getMessagesArrayFromCheckpoint,
810
+ chatSummarizer
811
+ } from "@delofarag/ai-utils"
333
812
  ```
334
813
 
335
814
  ---
336
815
 
337
- ## RAG Utilities (Detail)
816
+ ## RAG Utilities
338
817
 
339
818
  ### Vector Stores
340
819
 
@@ -369,20 +848,24 @@ const ragTool = createRAGTool({
369
848
  ## Magic-Funcs
370
849
 
371
850
  ### Answerers
851
+
372
852
  - `ask({ question, llm? })`
373
853
  - `websearch(query)` (braucht `TAVILY_API_KEY`)
374
854
 
375
855
  ### Evaluators
856
+
376
857
  - `classify({ data, classes, context?, llm? })`
377
858
  - `decide({ material, kriteria_to_decide, llm? })`
378
859
 
379
860
  ### Parsers
861
+
380
862
  - `extract({ data, schema, goal?, llm? })`
381
863
  - `structure({ data, into, retries?, llm? })`
382
864
  - `rewrite({ data, instruction, llm? })`
383
865
  - `summarize({ data, fokuss?, maxWords?, llm? })`
384
866
 
385
867
  ### Optimizers
868
+
386
869
  - `promptify({ request, agentRole?, llm? })`
387
870
  - `ragify({ data, llm? })`
388
871
 
@@ -390,7 +873,7 @@ Beispiel:
390
873
 
391
874
  ```ts
392
875
  import { classify, extract, summarize } from "@delofarag/ai-utils"
393
- import { z } from "zod/v3"
876
+ import { z } from "zod/v4"
394
877
 
395
878
  const sentiment = await classify({
396
879
  data: "Das Produkt ist wirklich gut.",
@@ -419,7 +902,7 @@ const short = await summarize({
419
902
  ### STT
420
903
 
421
904
  - `stt(...)`
422
- - `createSTTPhoneSocketSession(...)` fuer live phone socket chunks (Twilio/Telnyx-style)
905
+ - `createSTTPhoneSocketSession(...)` für live phone socket chunks (Twilio/Telnyx-style)
423
906
 
424
907
  ```ts
425
908
  import { stt } from "@delofarag/ai-utils"
@@ -433,7 +916,7 @@ const result = await stt({
433
916
  ### TTS
434
917
 
435
918
  - `tts(...)`
436
- - `streamTTSOverPhoneSocket(...)` fuer chunked outbound audio
919
+ - `streamTTSOverPhoneSocket(...)` für chunked outbound audio
437
920
 
438
921
  ```ts
439
922
  import { tts, streamTTSOverPhoneSocket } from "@delofarag/ai-utils"
@@ -479,29 +962,97 @@ const generated = await generateImages({
479
962
  ## Session / Stream Helpers
480
963
 
481
964
  ```ts
482
- import { session, StreamResponse } from "@delofarag/ai-utils"
965
+ import { session, StreamResponse, logChunk } from "@delofarag/ai-utils"
966
+ ```
967
+
968
+ ### `session()` — CLI-Testloop
969
+
970
+ Interaktive Konsole für `Agent.stream()` oder `DeepAgent.stream()`.
971
+
972
+ | Prop | Default | Beschreibung |
973
+ |---|---|---|
974
+ | `streamable` | — | Objekt mit `.stream({ input, thread_id, ... })` |
975
+ | `breakword` | `"exit"` | Beendet die Session |
976
+ | `id` | Timestamp | Wird als `thread_id` genutzt |
977
+ | `numberOfMessages` | `Infinity` | Max. User+Assistant-Runden |
978
+ | `isDeepAgent` | `false` | HITL-Modus für DeepAgent (siehe unten) |
979
+
980
+ **Normaler Agent:**
981
+
982
+ ```ts
983
+ await session({
984
+ streamable: agent,
985
+ breakword: "exit",
986
+ id: "dev-session-1",
987
+ })
483
988
  ```
484
989
 
485
- - `session({ streamable, ... })`: CLI-like interactive loop
486
- - `StreamResponse(asyncIterable)`: streambares NDJSON-HTTP-Response-Objekt
990
+ **DeepAgent mit HITL + Tool-Logs:**
991
+
992
+ ```ts
993
+ await session({
994
+ streamable: deepAgent,
995
+ isDeepAgent: true,
996
+ id: "dev-session-1",
997
+ })
998
+ ```
999
+
1000
+ Mit `isDeepAgent: true` passiert automatisch:
1001
+
1002
+ - `showToolCalls: true` — `[tool:start]` / `[tool:end]` in der Konsole
1003
+ - Nach einem Interrupt wartet die Session auf deine Entscheidung statt neuer Message
1004
+ - Erkannte Antworten: `approve`, `ja`, `ok`, `reject`, `reject: Grund hier`
1005
+ - `logChunk()` formatiert Text, Interrupts und Tool-Events lesbar
1006
+
1007
+ Flow:
1008
+
1009
+ ```
1010
+ You: Schreib eine Datei
1011
+ Assistant: [tool:start] write_file {...}
1012
+ [interrupt] Datei schreiben?
1013
+ decisions: approve, reject
1014
+ You: approve
1015
+ Assistant: [tool:end] write_file
1016
+ Fertig!
1017
+ ```
1018
+
1019
+ ### `StreamResponse()` — NDJSON für HTTP
1020
+
1021
+ Wrappt einen `AsyncIterable` als HTTP-Response (`application/x-ndjson`). Jeder Chunk = eine JSON-Zeile. Ideal für `DeepAgent.stream()` in API-Routes.
1022
+
1023
+ ### `logChunk()`
1024
+
1025
+ Hilfsfunktion für Stream-Output in der Konsole — nutzt intern `isInterrupt()` und `isToolEvent()`.
487
1026
 
488
1027
  ---
489
1028
 
490
- ## Export Overview (high-level)
1029
+ ## Export Overview
491
1030
 
492
- Top-level Exports decken u. a. ab:
1031
+ Top-level Exports (`@delofarag/ai-utils`):
493
1032
 
494
- - Helpers (`helpers`, `memory`, `rag`, `llms`, `chatbot`)
495
- - Core (`Agent`, `Chain`, `MemoryChain`)
496
- - Tools (`ToolRegistry`, `Tavily`, `RAGTool`, Zodios registries)
1033
+ - Helpers (`helpers`, `memory`, `rag`, `llms`, `chatbot`, `logChunk`)
1034
+ - Core (`Agent`, `Chain`, `DeepAgent`)
1035
+ - DeepAgent (`createWorkspaceBackend`, `createLocalShellBackend`, `createDenoSandbox`, `workspacePermissions`, `interruptOn`-Helper, alle `DeepAgent*`-Types)
1036
+ - Memory (`MemorySaver`, `SmartCheckpointSaver`, `SupabaseCheckpointSaver`, `chatSummarizer`)
1037
+ - Tools (`ToolRegistry`, `CombinedToolRegistry`, `ZodiosToolRegistry`, `Tavily`, `RAGTool`)
497
1038
  - Magic-Funcs (answerers/evaluators/parsers/optimizers)
498
1039
  - Modalities (`stt`, `tts`, `vision`, `generateImages`)
499
1040
 
1041
+ Client-Subpath (`@delofarag/ai-utils/client`) — ohne LangChain-Bundle, für Frontend:
1042
+
1043
+ - Types: `DeepAgentInterrupt`, `DeepAgentToolEvent`, `DeepAgentStreamChunk`, ...
1044
+ - Guards: `isInterrupt()`, `isToolEvent()`
1045
+
500
1046
  ---
501
1047
 
502
1048
  ## Empfehlungen
503
1049
 
504
- - Fuer strukturierte Outputs immer `zod/v3` verwenden.
505
- - Fuer Produktion API-Keys als ENV setzen, nicht hardcoden.
506
- - Bei langen Chats `SmartCheckpointSaver` verwenden.
1050
+ - Für strukturierte Outputs immer `zod/v4` verwenden.
1051
+ - Für Produktion API-Keys als ENV setzen, nicht hardcoden.
1052
+ - Bei langen Chats `SmartCheckpointSaver` am `Agent` verwenden.
507
1053
  - RAG als Tool im `Agent` ist in der Praxis oft robuster als RAG-only Prompting.
1054
+ - `Chain` für stateless Tasks, `Agent` für Tools und Conversation Memory, `DeepAgent` für autonome Coding-/Research-Agents.
1055
+ - DeepAgent: `rootDir` + `route` klar trennen — Agent sieht nur den virtuellen Pfad.
1056
+ - DeepAgent HITL: immer `checkpointer` + `thread_id`; `interruptOn` pro Tool konfigurieren.
1057
+ - DeepAgent Shell: `createLocalShellBackend` nur lokal/Dev — `execute` läuft auf dem Host.
1058
+ - DeepAgent testen: `session({ streamable: agent, isDeepAgent: true })`.