@librechat/agents 3.1.75 → 3.1.77-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/dist/cjs/graphs/Graph.cjs +22 -3
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/hitl/askUserQuestion.cjs +67 -0
  4. package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -0
  5. package/dist/cjs/hooks/HookRegistry.cjs +54 -0
  6. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  7. package/dist/cjs/hooks/createToolPolicyHook.cjs +115 -0
  8. package/dist/cjs/hooks/createToolPolicyHook.cjs.map +1 -0
  9. package/dist/cjs/hooks/executeHooks.cjs +40 -1
  10. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  11. package/dist/cjs/hooks/types.cjs +1 -0
  12. package/dist/cjs/hooks/types.cjs.map +1 -1
  13. package/dist/cjs/langchain/google-common.cjs +3 -0
  14. package/dist/cjs/langchain/google-common.cjs.map +1 -0
  15. package/dist/cjs/langchain/index.cjs +86 -0
  16. package/dist/cjs/langchain/index.cjs.map +1 -0
  17. package/dist/cjs/langchain/language_models/chat_models.cjs +3 -0
  18. package/dist/cjs/langchain/language_models/chat_models.cjs.map +1 -0
  19. package/dist/cjs/langchain/messages/tool.cjs +3 -0
  20. package/dist/cjs/langchain/messages/tool.cjs.map +1 -0
  21. package/dist/cjs/langchain/messages.cjs +51 -0
  22. package/dist/cjs/langchain/messages.cjs.map +1 -0
  23. package/dist/cjs/langchain/openai.cjs +3 -0
  24. package/dist/cjs/langchain/openai.cjs.map +1 -0
  25. package/dist/cjs/langchain/prompts.cjs +11 -0
  26. package/dist/cjs/langchain/prompts.cjs.map +1 -0
  27. package/dist/cjs/langchain/runnables.cjs +19 -0
  28. package/dist/cjs/langchain/runnables.cjs.map +1 -0
  29. package/dist/cjs/langchain/tools.cjs +23 -0
  30. package/dist/cjs/langchain/tools.cjs.map +1 -0
  31. package/dist/cjs/langchain/utils/env.cjs +11 -0
  32. package/dist/cjs/langchain/utils/env.cjs.map +1 -0
  33. package/dist/cjs/llm/anthropic/index.cjs +145 -52
  34. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  35. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  36. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +21 -14
  37. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  38. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +84 -70
  39. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  40. package/dist/cjs/llm/bedrock/index.cjs +1 -1
  41. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  42. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +213 -3
  43. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  44. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +2 -1
  45. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  46. package/dist/cjs/llm/google/utils/common.cjs +5 -4
  47. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  48. package/dist/cjs/llm/openai/index.cjs +519 -655
  49. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  50. package/dist/cjs/llm/openai/utils/index.cjs +20 -458
  51. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  52. package/dist/cjs/llm/openrouter/index.cjs +57 -175
  53. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  54. package/dist/cjs/llm/vertexai/index.cjs +5 -3
  55. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  56. package/dist/cjs/main.cjs +112 -3
  57. package/dist/cjs/main.cjs.map +1 -1
  58. package/dist/cjs/messages/cache.cjs +2 -1
  59. package/dist/cjs/messages/cache.cjs.map +1 -1
  60. package/dist/cjs/messages/core.cjs +7 -6
  61. package/dist/cjs/messages/core.cjs.map +1 -1
  62. package/dist/cjs/messages/format.cjs +73 -15
  63. package/dist/cjs/messages/format.cjs.map +1 -1
  64. package/dist/cjs/messages/langchain.cjs +26 -0
  65. package/dist/cjs/messages/langchain.cjs.map +1 -0
  66. package/dist/cjs/messages/prune.cjs +7 -6
  67. package/dist/cjs/messages/prune.cjs.map +1 -1
  68. package/dist/cjs/run.cjs +400 -42
  69. package/dist/cjs/run.cjs.map +1 -1
  70. package/dist/cjs/tools/ToolNode.cjs +556 -56
  71. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  72. package/dist/cjs/tools/search/search.cjs +55 -66
  73. package/dist/cjs/tools/search/search.cjs.map +1 -1
  74. package/dist/cjs/tools/search/tavily-scraper.cjs +189 -0
  75. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -0
  76. package/dist/cjs/tools/search/tavily-search.cjs +372 -0
  77. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -0
  78. package/dist/cjs/tools/search/tool.cjs +26 -4
  79. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  80. package/dist/cjs/tools/search/utils.cjs +10 -3
  81. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  82. package/dist/esm/graphs/Graph.mjs +22 -3
  83. package/dist/esm/graphs/Graph.mjs.map +1 -1
  84. package/dist/esm/hitl/askUserQuestion.mjs +65 -0
  85. package/dist/esm/hitl/askUserQuestion.mjs.map +1 -0
  86. package/dist/esm/hooks/HookRegistry.mjs +54 -0
  87. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  88. package/dist/esm/hooks/createToolPolicyHook.mjs +113 -0
  89. package/dist/esm/hooks/createToolPolicyHook.mjs.map +1 -0
  90. package/dist/esm/hooks/executeHooks.mjs +40 -1
  91. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  92. package/dist/esm/hooks/types.mjs +1 -0
  93. package/dist/esm/hooks/types.mjs.map +1 -1
  94. package/dist/esm/langchain/google-common.mjs +2 -0
  95. package/dist/esm/langchain/google-common.mjs.map +1 -0
  96. package/dist/esm/langchain/index.mjs +5 -0
  97. package/dist/esm/langchain/index.mjs.map +1 -0
  98. package/dist/esm/langchain/language_models/chat_models.mjs +2 -0
  99. package/dist/esm/langchain/language_models/chat_models.mjs.map +1 -0
  100. package/dist/esm/langchain/messages/tool.mjs +2 -0
  101. package/dist/esm/langchain/messages/tool.mjs.map +1 -0
  102. package/dist/esm/langchain/messages.mjs +2 -0
  103. package/dist/esm/langchain/messages.mjs.map +1 -0
  104. package/dist/esm/langchain/openai.mjs +2 -0
  105. package/dist/esm/langchain/openai.mjs.map +1 -0
  106. package/dist/esm/langchain/prompts.mjs +2 -0
  107. package/dist/esm/langchain/prompts.mjs.map +1 -0
  108. package/dist/esm/langchain/runnables.mjs +2 -0
  109. package/dist/esm/langchain/runnables.mjs.map +1 -0
  110. package/dist/esm/langchain/tools.mjs +2 -0
  111. package/dist/esm/langchain/tools.mjs.map +1 -0
  112. package/dist/esm/langchain/utils/env.mjs +2 -0
  113. package/dist/esm/langchain/utils/env.mjs.map +1 -0
  114. package/dist/esm/llm/anthropic/index.mjs +146 -54
  115. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  116. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  117. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +21 -14
  118. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  119. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +84 -71
  120. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  121. package/dist/esm/llm/bedrock/index.mjs +1 -1
  122. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  123. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +214 -4
  124. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  125. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +2 -1
  126. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  127. package/dist/esm/llm/google/utils/common.mjs +5 -4
  128. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  129. package/dist/esm/llm/openai/index.mjs +520 -656
  130. package/dist/esm/llm/openai/index.mjs.map +1 -1
  131. package/dist/esm/llm/openai/utils/index.mjs +23 -459
  132. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  133. package/dist/esm/llm/openrouter/index.mjs +57 -175
  134. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  135. package/dist/esm/llm/vertexai/index.mjs +5 -3
  136. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  137. package/dist/esm/main.mjs +7 -0
  138. package/dist/esm/main.mjs.map +1 -1
  139. package/dist/esm/messages/cache.mjs +2 -1
  140. package/dist/esm/messages/cache.mjs.map +1 -1
  141. package/dist/esm/messages/core.mjs +7 -6
  142. package/dist/esm/messages/core.mjs.map +1 -1
  143. package/dist/esm/messages/format.mjs +73 -15
  144. package/dist/esm/messages/format.mjs.map +1 -1
  145. package/dist/esm/messages/langchain.mjs +23 -0
  146. package/dist/esm/messages/langchain.mjs.map +1 -0
  147. package/dist/esm/messages/prune.mjs +7 -6
  148. package/dist/esm/messages/prune.mjs.map +1 -1
  149. package/dist/esm/run.mjs +400 -42
  150. package/dist/esm/run.mjs.map +1 -1
  151. package/dist/esm/tools/ToolNode.mjs +557 -57
  152. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  153. package/dist/esm/tools/search/search.mjs +55 -66
  154. package/dist/esm/tools/search/search.mjs.map +1 -1
  155. package/dist/esm/tools/search/tavily-scraper.mjs +186 -0
  156. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -0
  157. package/dist/esm/tools/search/tavily-search.mjs +370 -0
  158. package/dist/esm/tools/search/tavily-search.mjs.map +1 -0
  159. package/dist/esm/tools/search/tool.mjs +26 -4
  160. package/dist/esm/tools/search/tool.mjs.map +1 -1
  161. package/dist/esm/tools/search/utils.mjs +10 -3
  162. package/dist/esm/tools/search/utils.mjs.map +1 -1
  163. package/dist/types/graphs/Graph.d.ts +7 -0
  164. package/dist/types/hitl/askUserQuestion.d.ts +55 -0
  165. package/dist/types/hitl/index.d.ts +6 -0
  166. package/dist/types/hooks/HookRegistry.d.ts +58 -0
  167. package/dist/types/hooks/createToolPolicyHook.d.ts +87 -0
  168. package/dist/types/hooks/index.d.ts +4 -1
  169. package/dist/types/hooks/types.d.ts +109 -3
  170. package/dist/types/index.d.ts +10 -0
  171. package/dist/types/langchain/google-common.d.ts +1 -0
  172. package/dist/types/langchain/index.d.ts +8 -0
  173. package/dist/types/langchain/language_models/chat_models.d.ts +1 -0
  174. package/dist/types/langchain/messages/tool.d.ts +1 -0
  175. package/dist/types/langchain/messages.d.ts +2 -0
  176. package/dist/types/langchain/openai.d.ts +1 -0
  177. package/dist/types/langchain/prompts.d.ts +1 -0
  178. package/dist/types/langchain/runnables.d.ts +2 -0
  179. package/dist/types/langchain/tools.d.ts +2 -0
  180. package/dist/types/langchain/utils/env.d.ts +1 -0
  181. package/dist/types/llm/anthropic/index.d.ts +22 -9
  182. package/dist/types/llm/anthropic/types.d.ts +5 -1
  183. package/dist/types/llm/anthropic/utils/message_outputs.d.ts +13 -6
  184. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +1 -1
  185. package/dist/types/llm/openai/index.d.ts +21 -24
  186. package/dist/types/llm/openrouter/index.d.ts +11 -9
  187. package/dist/types/llm/vertexai/index.d.ts +1 -0
  188. package/dist/types/messages/cache.d.ts +4 -1
  189. package/dist/types/messages/format.d.ts +4 -1
  190. package/dist/types/messages/langchain.d.ts +27 -0
  191. package/dist/types/run.d.ts +117 -1
  192. package/dist/types/tools/ToolNode.d.ts +26 -1
  193. package/dist/types/tools/search/tavily-scraper.d.ts +19 -0
  194. package/dist/types/tools/search/tavily-search.d.ts +4 -0
  195. package/dist/types/tools/search/types.d.ts +99 -5
  196. package/dist/types/tools/search/utils.d.ts +2 -2
  197. package/dist/types/types/graph.d.ts +23 -37
  198. package/dist/types/types/hitl.d.ts +272 -0
  199. package/dist/types/types/index.d.ts +1 -0
  200. package/dist/types/types/llm.d.ts +3 -3
  201. package/dist/types/types/run.d.ts +33 -0
  202. package/dist/types/types/stream.d.ts +1 -1
  203. package/dist/types/types/tools.d.ts +19 -0
  204. package/package.json +80 -17
  205. package/src/graphs/Graph.ts +33 -4
  206. package/src/graphs/__tests__/composition.smoke.test.ts +188 -0
  207. package/src/hitl/askUserQuestion.ts +72 -0
  208. package/src/hitl/index.ts +7 -0
  209. package/src/hooks/HookRegistry.ts +71 -0
  210. package/src/hooks/__tests__/createToolPolicyHook.test.ts +259 -0
  211. package/src/hooks/createToolPolicyHook.ts +184 -0
  212. package/src/hooks/executeHooks.ts +50 -1
  213. package/src/hooks/index.ts +6 -0
  214. package/src/hooks/types.ts +112 -0
  215. package/src/index.ts +22 -0
  216. package/src/langchain/google-common.ts +1 -0
  217. package/src/langchain/index.ts +8 -0
  218. package/src/langchain/language_models/chat_models.ts +1 -0
  219. package/src/langchain/messages/tool.ts +5 -0
  220. package/src/langchain/messages.ts +21 -0
  221. package/src/langchain/openai.ts +1 -0
  222. package/src/langchain/prompts.ts +1 -0
  223. package/src/langchain/runnables.ts +7 -0
  224. package/src/langchain/tools.ts +8 -0
  225. package/src/langchain/utils/env.ts +1 -0
  226. package/src/llm/anthropic/index.ts +252 -84
  227. package/src/llm/anthropic/llm.spec.ts +751 -102
  228. package/src/llm/anthropic/types.ts +9 -1
  229. package/src/llm/anthropic/utils/message_inputs.ts +37 -19
  230. package/src/llm/anthropic/utils/message_outputs.ts +119 -101
  231. package/src/llm/bedrock/index.ts +2 -2
  232. package/src/llm/bedrock/llm.spec.ts +341 -0
  233. package/src/llm/bedrock/utils/message_inputs.ts +303 -4
  234. package/src/llm/bedrock/utils/message_outputs.ts +2 -1
  235. package/src/llm/custom-chat-models.smoke.test.ts +836 -0
  236. package/src/llm/google/llm.spec.ts +339 -57
  237. package/src/llm/google/utils/common.ts +53 -48
  238. package/src/llm/openai/contentBlocks.test.ts +346 -0
  239. package/src/llm/openai/index.ts +856 -833
  240. package/src/llm/openai/utils/index.ts +107 -78
  241. package/src/llm/openai/utils/messages.test.ts +159 -0
  242. package/src/llm/openrouter/index.ts +124 -247
  243. package/src/llm/openrouter/reasoning.test.ts +8 -1
  244. package/src/llm/vertexai/index.ts +11 -5
  245. package/src/llm/vertexai/llm.spec.ts +28 -1
  246. package/src/messages/cache.test.ts +4 -3
  247. package/src/messages/cache.ts +3 -2
  248. package/src/messages/core.ts +16 -9
  249. package/src/messages/format.ts +96 -16
  250. package/src/messages/formatAgentMessages.test.ts +166 -1
  251. package/src/messages/langchain.ts +39 -0
  252. package/src/messages/prune.ts +12 -8
  253. package/src/run.ts +456 -47
  254. package/src/scripts/caching.ts +2 -3
  255. package/src/specs/summarization.test.ts +51 -58
  256. package/src/tools/ToolNode.ts +706 -63
  257. package/src/tools/__tests__/hitl.test.ts +3593 -0
  258. package/src/tools/search/search.ts +83 -73
  259. package/src/tools/search/tavily-scraper.ts +235 -0
  260. package/src/tools/search/tavily-search.ts +424 -0
  261. package/src/tools/search/tavily.test.ts +965 -0
  262. package/src/tools/search/tool.ts +36 -26
  263. package/src/tools/search/types.ts +133 -8
  264. package/src/tools/search/utils.ts +13 -5
  265. package/src/types/graph.ts +32 -87
  266. package/src/types/hitl.ts +303 -0
  267. package/src/types/index.ts +1 -0
  268. package/src/types/llm.ts +3 -3
  269. package/src/types/run.ts +33 -0
  270. package/src/types/stream.ts +1 -1
  271. package/src/types/tools.ts +19 -0
  272. package/src/utils/llmConfig.ts +1 -6
@@ -0,0 +1,303 @@
1
+ /**
2
+ * First-class human-in-the-loop (HITL) types for `@librechat/agents`.
3
+ * Surfaces the interrupt payload that `ToolNode` raises when a `PreToolUse`
4
+ * hook returns `decision: 'ask'` and HITL is enabled on the run, plus the
5
+ * resume-decision shape the host returns to continue or reject the tool.
6
+ *
7
+ * Mirrors the LangChain HITL middleware shape (action_requests /
8
+ * review_configs) so hosts and clients can share rendering/UI semantics
9
+ * across the langchain ecosystem.
10
+ */
11
+
12
+ /** Per-tool approval request emitted inside an interrupt payload. */
13
+ export interface ToolApprovalRequest {
14
+ /** Stable id of the tool call (matches LangGraph `ToolCall.id`). */
15
+ tool_call_id: string;
16
+ /** Tool name being invoked. */
17
+ name: string;
18
+ /**
19
+ * Arguments the tool is about to be invoked with — already resolved by
20
+ * any `{{tool<i>turn<n>}}` references and any `updatedInput` returned
21
+ * by the firing PreToolUse hook.
22
+ */
23
+ arguments: Record<string, unknown>;
24
+ /**
25
+ * Optional reason the hook supplied for asking (e.g., "destructive
26
+ * filesystem write"). Hosts can render this verbatim.
27
+ */
28
+ description?: string;
29
+ }
30
+
31
+ /** Allowed host-side decisions for a `tool_approval` interrupt. */
32
+ export type ToolApprovalDecisionType =
33
+ | 'approve'
34
+ | 'reject'
35
+ | 'edit'
36
+ | 'respond';
37
+
38
+ /** Per-action review configuration paired with each action_request. */
39
+ export interface ToolApprovalReviewConfig {
40
+ /** Tool name (matches the `name` field on the corresponding action_request). */
41
+ action_name: string;
42
+ /**
43
+ * Stable id of the tool call this review_config applies to (matches
44
+ * the `tool_call_id` of the corresponding action_request). Lets a UI
45
+ * map review_configs → action_requests directly when a batch
46
+ * contains the same tool called more than once — by-position
47
+ * mapping breaks down with duplicates.
48
+ */
49
+ tool_call_id: string;
50
+ /** Decisions the host UI is allowed to surface for this action. */
51
+ allowed_decisions: ToolApprovalDecisionType[];
52
+ }
53
+
54
+ /**
55
+ * Resume value the host returns through `Run.resume(decisions)` after a
56
+ * `tool_approval` interrupt. One entry per action_request, in the same
57
+ * order. Hosts may also return a record keyed by `tool_call_id`; the SDK
58
+ * handles either shape.
59
+ *
60
+ * Variants:
61
+ * - `approve`: run the tool with its original (or hook-rewritten) args.
62
+ * - `reject`: skip the tool, emit a blocked error `ToolMessage` with
63
+ * `reason` surfaced to the model.
64
+ * - `edit`: replace the tool's args with `updatedInput` (re-resolves
65
+ * any `{{tool<i>turn<n>}}` placeholders) and run the tool.
66
+ * - `respond`: skip the tool entirely and emit `responseText` as a
67
+ * successful `ToolMessage`. Mirrors LangChain HITL middleware's
68
+ * `respond` semantic — the human supplies the result the model sees,
69
+ * bypassing tool execution. Useful when the user wants to short-circuit
70
+ * a tool call with a hand-written answer (e.g., "don't actually run
71
+ * the search, just tell the model 'no relevant results'").
72
+ *
73
+ * Note on hook semantics: `respond` does NOT fire the per-tool
74
+ * `PostToolUse` hook (no real tool execution happened, so the
75
+ * "post-tool" semantic doesn't apply). It DOES appear in the
76
+ * `PostToolBatch` entry array with `status: 'success'` and the
77
+ * user-supplied text as `toolOutput`, so batch-level audit /
78
+ * convention hooks see the full set of outcomes.
79
+ */
80
+ export type ToolApprovalDecision =
81
+ | { type: 'approve' }
82
+ | { type: 'reject'; reason?: string }
83
+ | { type: 'edit'; updatedInput: Record<string, unknown> }
84
+ | { type: 'respond'; responseText: string };
85
+
86
+ /** Map form of resume decisions, keyed by tool call id. */
87
+ export type ToolApprovalDecisionMap = Record<string, ToolApprovalDecision>;
88
+
89
+ /**
90
+ * Categories of human-in-the-loop interrupts the SDK can raise. Hosts
91
+ * narrow on `HumanInterruptPayload.type` to determine which payload
92
+ * shape they're handling and which resume value to send back through
93
+ * `Run.resume()`.
94
+ *
95
+ * Exported as a discrete type so downstream consumers (notably
96
+ * LibreChat's wire types in `librechat-data-provider`) can mirror
97
+ * the discriminator alongside their own host-side `PendingAction`
98
+ * record without re-declaring the union themselves. Internal SDK
99
+ * code narrows directly on the literal strings via the type guards
100
+ * below; this type alias is primarily an integration-layer contract.
101
+ */
102
+ export type HumanInterruptType = 'tool_approval' | 'ask_user_question';
103
+
104
+ /**
105
+ * Structured payload the SDK passes to `interrupt()` when one or more
106
+ * pending tool calls require host approval. All `ask`-decision tool calls
107
+ * from a single ToolNode batch are bundled into one interrupt so the host
108
+ * can render and resolve them together.
109
+ *
110
+ * Resume value: `ToolApprovalDecision[]` (in `action_requests` order) or
111
+ * `ToolApprovalDecisionMap` (keyed by `tool_call_id`).
112
+ */
113
+ export interface ToolApprovalInterruptPayload {
114
+ type: 'tool_approval';
115
+ action_requests: ToolApprovalRequest[];
116
+ review_configs: ToolApprovalReviewConfig[];
117
+ }
118
+
119
+ /**
120
+ * Pre-defined option the user can pick when answering an
121
+ * `ask_user_question` interrupt. The selected option's `value` becomes
122
+ * the resume value's `answer` field.
123
+ */
124
+ export interface AskUserQuestionOption {
125
+ /** Human-readable label rendered in the host UI. */
126
+ label: string;
127
+ /** Value returned via `AskUserQuestionResolution.answer` if picked. */
128
+ value: string;
129
+ }
130
+
131
+ /** Question request emitted inside an `ask_user_question` interrupt. */
132
+ export interface AskUserQuestionRequest {
133
+ /** The question to ask the human. */
134
+ question: string;
135
+ /** Optional context / description rendered alongside the question. */
136
+ description?: string;
137
+ /**
138
+ * Optional pre-defined response options. When present, hosts can render
139
+ * a picker; the user may still type a free-form answer when the host
140
+ * UI allows it. Omit to require a free-form answer.
141
+ */
142
+ options?: AskUserQuestionOption[];
143
+ }
144
+
145
+ /**
146
+ * Structured payload the SDK passes to `interrupt()` when an agent (or
147
+ * a custom node) needs to ask the user a clarifying question. Mirrors
148
+ * Claude Code's `AskUserQuestion` semantic. Resume value:
149
+ * `AskUserQuestionResolution`.
150
+ */
151
+ export interface AskUserQuestionInterruptPayload {
152
+ type: 'ask_user_question';
153
+ question: AskUserQuestionRequest;
154
+ }
155
+
156
+ /**
157
+ * Discriminated union of every interrupt payload the SDK raises. New
158
+ * variants can be added without breaking existing handlers as long as
159
+ * those handlers check `payload.type` before reading variant-specific
160
+ * fields. Use the `isToolApprovalInterrupt` / `isAskUserQuestionInterrupt`
161
+ * type guards for ergonomic narrowing.
162
+ */
163
+ export type HumanInterruptPayload =
164
+ | ToolApprovalInterruptPayload
165
+ | AskUserQuestionInterruptPayload;
166
+
167
+ /** Resume value the host returns for an `ask_user_question` interrupt. */
168
+ export interface AskUserQuestionResolution {
169
+ /**
170
+ * The human's answer. Free-form text, or — when `options` were
171
+ * provided — one of the option `value`s. Hosts may also send any
172
+ * structured object their custom UI defines; see the host docs for
173
+ * what your downstream consumer expects.
174
+ */
175
+ answer: string;
176
+ }
177
+
178
+ /**
179
+ * Type guard narrowing an arbitrary value to a `ToolApprovalInterruptPayload`.
180
+ * Accepts `unknown` (not just `HumanInterruptPayload`) because hosts can
181
+ * raise custom interrupt payloads from custom nodes — `getInterrupt()`
182
+ * surfaces them as-is, and downstream code must validate the shape at
183
+ * runtime before reading variant-specific fields.
184
+ */
185
+ export function isToolApprovalInterrupt(
186
+ payload: unknown
187
+ ): payload is ToolApprovalInterruptPayload {
188
+ return (
189
+ typeof payload === 'object' &&
190
+ payload !== null &&
191
+ (payload as { type?: unknown }).type === 'tool_approval'
192
+ );
193
+ }
194
+
195
+ /**
196
+ * Type guard narrowing an arbitrary value to an
197
+ * `AskUserQuestionInterruptPayload`. Same `unknown`-tolerant contract
198
+ * as `isToolApprovalInterrupt`.
199
+ */
200
+ export function isAskUserQuestionInterrupt(
201
+ payload: unknown
202
+ ): payload is AskUserQuestionInterruptPayload {
203
+ return (
204
+ typeof payload === 'object' &&
205
+ payload !== null &&
206
+ (payload as { type?: unknown }).type === 'ask_user_question'
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Run-level configuration controlling HITL semantics. **HITL is OFF by
212
+ * default** for now — the SDK ships the interrupt machinery, but the
213
+ * default stays opt-in until host UIs (notably LibreChat) ship the
214
+ * approval-rendering affordances needed to surface interrupts to end
215
+ * users. Without that UI, an interrupt with no resolver looks like a
216
+ * hung tool-call card. Hosts opt in explicitly with
217
+ * `{ enabled: true }`. The intent is to flip this default to ON in a
218
+ * future minor once the consumer ecosystem is ready to render
219
+ * interrupts end-to-end.
220
+ *
221
+ * When enabled (`{ enabled: true }`):
222
+ *
223
+ * - `PreToolUse` hooks returning `decision: 'ask'` raise a real
224
+ * LangGraph `interrupt()` instead of being treated as a synchronous
225
+ * deny.
226
+ * - `Run.create` installs a `MemorySaver` checkpointer fallback on the
227
+ * run's compile options if the host did not provide one, since
228
+ * LangGraph requires a checkpointer to suspend and resume.
229
+ *
230
+ * When disabled (the default — omitted, or `{ enabled: false }`):
231
+ * `ask` decisions are fail-closed (blocked with an error
232
+ * `ToolMessage`) and no checkpointer is implicitly attached. This
233
+ * matches the pre-HITL behavior so existing hosts upgrading the SDK
234
+ * see no change until they're ready to wire the resume UI.
235
+ *
236
+ * ## Scope: event-driven tools only
237
+ *
238
+ * The interrupt path is wired into `ToolNode.dispatchToolEvents`, which
239
+ * runs when the agent uses event-driven tool dispatch (the path
240
+ * LibreChat and most production hosts take). Tools that execute via
241
+ * the direct path — i.e. tools listed in `directToolNames` (the
242
+ * graph-managed handoff and subagent tools) or tools on agents
243
+ * configured WITHOUT `eventDrivenMode` — bypass the hook system
244
+ * entirely. `PreToolUse` hooks do not fire for those tools and HITL
245
+ * approval does not gate them.
246
+ *
247
+ * Practical implications:
248
+ * - LibreChat-style hosts using event-driven dispatch get the full
249
+ * HITL surface across every tool the model calls.
250
+ * - Hosts using `AgentInputs.tools` directly without event-driven
251
+ * mode get policy enforcement for nothing — the hooks register
252
+ * but never fire. Either switch to event-driven mode or accept
253
+ * that direct tools are not approval-gated. This is documented
254
+ * also on `ToolNodeOptions.hookRegistry`.
255
+ * - Mixed direct + event batches (e.g. a handoff tool sharing an
256
+ * LLM turn with a regular tool) currently re-execute the direct
257
+ * half on resume, since LangGraph rolls back the entire ToolNode
258
+ * on `interrupt()` throw. Hosts whose direct tools have side
259
+ * effects (subagents that invoke models, handoffs that trigger
260
+ * downstream work) should avoid mixing those tools into the same
261
+ * batch as approval-gated event tools.
262
+ *
263
+ * ## Note on idempotency
264
+ *
265
+ * When an interrupt fires, LangGraph re-runs the interrupted node
266
+ * from the start on resume, which fires `PreToolUse` hooks again.
267
+ * Hooks that produce side effects (logging, external calls) will see
268
+ * two invocations per paused turn.
269
+ */
270
+ export interface HumanInTheLoopConfig {
271
+ /**
272
+ * Master switch. Defaults to `false` — omit the field (or pass
273
+ * `false`) to keep HITL off, or set `true` to opt in once the host
274
+ * UI is ready to render and resolve `tool_approval` interrupts.
275
+ */
276
+ enabled?: boolean;
277
+ }
278
+
279
+ /**
280
+ * Snapshot of an in-flight interrupt surfaced from `Run.processStream`
281
+ * via `run.getInterrupt()`. Hosts persist this alongside their job
282
+ * record so they can later call `Run.resume(decisions)` against a Run
283
+ * compiled with the same `thread_id` / checkpointer.
284
+ *
285
+ * The `payload` type defaults to `HumanInterruptPayload` (the SDK's
286
+ * built-in `tool_approval` / `ask_user_question` discriminated union)
287
+ * for ergonomic narrowing in the common case. Hosts that raise custom
288
+ * interrupt payloads from custom graph nodes can pass the type
289
+ * parameter (`run.getInterrupt<MyCustom>()` or
290
+ * `RunInterruptResult<MyCustom>`) — the SDK does not validate the
291
+ * runtime shape, it just transports whatever the node passed to
292
+ * `interrupt()`. Use the `isToolApprovalInterrupt` /
293
+ * `isAskUserQuestionInterrupt` guards (which accept `unknown`) when
294
+ * the source of the interrupt isn't statically known.
295
+ */
296
+ export interface RunInterruptResult<TPayload = HumanInterruptPayload> {
297
+ /** Stable id of the LangGraph interrupt (from `Interrupt.id`). */
298
+ interruptId: string;
299
+ /** `thread_id` the run was bound to — required to resume. */
300
+ threadId?: string;
301
+ /** Structured payload describing what needs human input. */
302
+ payload: TPayload;
303
+ }
@@ -1,5 +1,6 @@
1
1
  // src/types/index.ts
2
2
  export * from './graph';
3
+ export * from './hitl';
3
4
  export * from './llm';
4
5
  export * from './messages';
5
6
  export * from './run';
package/src/types/llm.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  } from '@langchain/openai';
13
13
  import type { GoogleGenerativeAIChatInput } from '@langchain/google-genai';
14
14
  import type { ChatVertexAIInput } from '@langchain/google-vertexai';
15
- import type { ChatDeepSeekCallOptions } from '@langchain/deepseek';
15
+ import type { ChatDeepSeekInput } from '@langchain/deepseek';
16
16
  import type { ChatOpenRouterCallOptions } from '@/llm/openrouter';
17
17
  import type { ChatBedrockConverseInput } from '@langchain/aws';
18
18
  import type { ChatMistralAIInput } from '@langchain/mistralai';
@@ -70,7 +70,7 @@ export type AnthropicReasoning = {
70
70
  export type GoogleThinkingConfig = {
71
71
  thinkingBudget?: number;
72
72
  includeThoughts?: boolean;
73
- thinkingLevel?: string;
73
+ thinkingLevel?: 'THINKING_LEVEL_UNSPECIFIED' | 'LOW' | 'MEDIUM' | 'HIGH';
74
74
  };
75
75
  export type OpenAIClientOptions = ChatOpenAIFields;
76
76
  export type AnthropicClientOptions = Omit<AnthropicInput, 'thinking'> & {
@@ -93,7 +93,7 @@ export type GoogleClientOptions = GoogleGenerativeAIChatInput & {
93
93
  customHeaders?: RequestOptions['customHeaders'];
94
94
  thinkingConfig?: GoogleThinkingConfig;
95
95
  };
96
- export type DeepSeekClientOptions = ChatDeepSeekCallOptions;
96
+ export type DeepSeekClientOptions = Partial<ChatDeepSeekInput>;
97
97
  export type XAIClientOptions = ChatXAIInput;
98
98
 
99
99
  export type ClientOptions =
package/src/types/run.ts CHANGED
@@ -12,6 +12,7 @@ import type * as e from '@/common/enum';
12
12
  import type * as g from '@/types/graph';
13
13
  import type * as l from '@/types/llm';
14
14
  import type { ToolSessionMap, ToolOutputReferencesConfig } from '@/types/tools';
15
+ import type { HumanInTheLoopConfig } from '@/types/hitl';
15
16
  import type { HookRegistry } from '@/hooks';
16
17
 
17
18
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -156,6 +157,38 @@ export type RunConfig = {
156
157
  * placeholders. Disabled by default so existing runs are unaffected.
157
158
  */
158
159
  toolOutputReferences?: ToolOutputReferencesConfig;
160
+ /**
161
+ * First-class human-in-the-loop (HITL) flow for this run.
162
+ *
163
+ * **HITL is OFF by default.** Omitting this field — or passing
164
+ * `{ enabled: false }` — keeps the pre-HITL fail-closed semantics
165
+ * where `ask` decisions collapse into a synchronous deny. Hosts opt
166
+ * in explicitly with `{ enabled: true }` once their UI can render
167
+ * and resolve `tool_approval` interrupts (otherwise the run just
168
+ * pauses with no resolver, which surfaces to end users as a hung
169
+ * tool-call card).
170
+ *
171
+ * Plan of record: the default flips back to ON in a future minor
172
+ * once the consumer ecosystem (notably LibreChat) ships HITL UI
173
+ * end-to-end. See `HumanInTheLoopConfig` JSDoc.
174
+ *
175
+ * When enabled (`{ enabled: true }`):
176
+ * - `PreToolUse` hooks returning `decision: 'ask'` raise a real
177
+ * LangGraph `interrupt()` instead of being treated as a synchronous
178
+ * deny. The graph pauses and the run exits cleanly.
179
+ * - If `graphConfig.compileOptions.checkpointer` is missing, the SDK
180
+ * installs an in-memory `MemorySaver` as a fallback so scripts and
181
+ * tests can resume without external infrastructure. Production
182
+ * hosts should always provide a durable checkpointer.
183
+ * - Hosts inspect the pending interrupt via `run.getInterrupt()` and
184
+ * continue with `Run.resume(decisions)` against a Run rebuilt with
185
+ * the same `thread_id` and checkpointer.
186
+ *
187
+ * When disabled (the default): `ask` decisions remain fail-closed
188
+ * (blocked with an error `ToolMessage`) and no checkpointer is
189
+ * implicitly attached.
190
+ */
191
+ humanInTheLoop?: HumanInTheLoopConfig;
159
192
  };
160
193
 
161
194
  export type ProvidedCallbacks =
@@ -191,7 +191,7 @@ export interface ExtendedMessageContent {
191
191
  type?: string;
192
192
  text?: string;
193
193
  input?: string;
194
- index?: number;
194
+ index?: string | number;
195
195
  id?: string;
196
196
  name?: string;
197
197
  }
@@ -5,6 +5,7 @@ import type { ToolCall } from '@langchain/core/messages/tool';
5
5
  import type { HookRegistry } from '@/hooks';
6
6
  import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
7
7
  import type { MessageContentComplex, ToolErrorData } from './stream';
8
+ import type { HumanInTheLoopConfig } from './hitl';
8
9
 
9
10
  /** Replacement type for `import type { ToolCall } from '@langchain/core/messages/tool'` in order to have stringified args typed */
10
11
  export type CustomToolCall = {
@@ -56,6 +57,24 @@ export type ToolNodeOptions = {
56
57
  * routed through `directToolNames` bypass hook dispatch entirely.
57
58
  */
58
59
  hookRegistry?: HookRegistry;
60
+ /**
61
+ * Run-scoped HITL config. **HITL is OFF by default** — omitting this
62
+ * field (or passing `{ enabled: false }`) keeps the pre-HITL
63
+ * fail-closed behavior where a `PreToolUse` `ask` decision collapses
64
+ * into a blocked `ToolMessage`. Hosts opt in with
65
+ * `{ enabled: true }` once their UI can render and resolve a
66
+ * `tool_approval` interrupt; that engages the interrupt path where
67
+ * `ask` raises a real LangGraph `interrupt()` carrying a
68
+ * `HumanInterruptPayload` and the host resumes with
69
+ * `Run.resume(decisions)`.
70
+ *
71
+ * Mirrors `RunConfig.humanInTheLoop` (which is the canonical place
72
+ * to set this); the Graph threads it down to every ToolNode it
73
+ * compiles. Same caveat: the interrupt path is only wired into the
74
+ * event-driven dispatch (`dispatchToolEvents`), not into
75
+ * `directToolNames` execution — direct tools bypass HITL entirely.
76
+ */
77
+ humanInTheLoop?: HumanInTheLoopConfig;
59
78
  /** Max context tokens for the agent — used to compute tool result truncation limits. */
60
79
  maxContextTokens?: number;
61
80
  /**
@@ -56,9 +56,7 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
56
56
  provider: Providers.OPENROUTER,
57
57
  streaming: true,
58
58
  streamUsage: true,
59
- // model: 'anthropic/claude-sonnet-4',
60
- // model: 'moonshotai/kimi-k2-thinking',
61
- model: 'google/gemini-3-pro-preview',
59
+ model: process.env.OPENROUTER_MODEL ?? 'openai/gpt-4o-mini',
62
60
  apiKey: process.env.OPENROUTER_API_KEY,
63
61
  configuration: {
64
62
  baseURL: process.env.OPENROUTER_BASE_URL,
@@ -67,9 +65,6 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
67
65
  'X-Title': 'LibreChat',
68
66
  },
69
67
  },
70
- reasoning: {
71
- max_tokens: 8000,
72
- },
73
68
  modelKwargs: {
74
69
  max_tokens: 10000,
75
70
  },