@librechat/agents 3.1.77 → 3.1.78

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 (185) hide show
  1. package/dist/cjs/common/enum.cjs +54 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +155 -4
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +291 -0
  6. package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -0
  7. package/dist/cjs/main.cjs +90 -0
  8. package/dist/cjs/main.cjs.map +1 -1
  9. package/dist/cjs/messages/anthropicToolCache.cjs +102 -0
  10. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -0
  11. package/dist/cjs/messages/prune.cjs +27 -0
  12. package/dist/cjs/messages/prune.cjs.map +1 -1
  13. package/dist/cjs/messages/recency.cjs +99 -0
  14. package/dist/cjs/messages/recency.cjs.map +1 -0
  15. package/dist/cjs/run.cjs +30 -0
  16. package/dist/cjs/run.cjs.map +1 -1
  17. package/dist/cjs/summarization/node.cjs +100 -6
  18. package/dist/cjs/summarization/node.cjs.map +1 -1
  19. package/dist/cjs/tools/ToolNode.cjs +635 -23
  20. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  21. package/dist/cjs/tools/local/CompileCheckTool.cjs +227 -0
  22. package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -0
  23. package/dist/cjs/tools/local/FileCheckpointer.cjs +90 -0
  24. package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -0
  25. package/dist/cjs/tools/local/LocalCodingTools.cjs +1098 -0
  26. package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -0
  27. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +1042 -0
  28. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -0
  29. package/dist/cjs/tools/local/LocalExecutionTools.cjs +122 -0
  30. package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -0
  31. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +453 -0
  32. package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -0
  33. package/dist/cjs/tools/local/attachments.cjs +183 -0
  34. package/dist/cjs/tools/local/attachments.cjs.map +1 -0
  35. package/dist/cjs/tools/local/bashAst.cjs +129 -0
  36. package/dist/cjs/tools/local/bashAst.cjs.map +1 -0
  37. package/dist/cjs/tools/local/editStrategies.cjs +188 -0
  38. package/dist/cjs/tools/local/editStrategies.cjs.map +1 -0
  39. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +141 -0
  40. package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -0
  41. package/dist/cjs/tools/local/syntaxCheck.cjs +182 -0
  42. package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -0
  43. package/dist/cjs/tools/local/textEncoding.cjs +30 -0
  44. package/dist/cjs/tools/local/textEncoding.cjs.map +1 -0
  45. package/dist/cjs/tools/local/workspaceFS.cjs +51 -0
  46. package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -0
  47. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +31 -0
  48. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  49. package/dist/esm/common/enum.mjs +53 -1
  50. package/dist/esm/common/enum.mjs.map +1 -1
  51. package/dist/esm/graphs/Graph.mjs +156 -5
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/hooks/createWorkspacePolicyHook.mjs +289 -0
  54. package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -0
  55. package/dist/esm/main.mjs +17 -2
  56. package/dist/esm/main.mjs.map +1 -1
  57. package/dist/esm/messages/anthropicToolCache.mjs +99 -0
  58. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -0
  59. package/dist/esm/messages/prune.mjs +26 -1
  60. package/dist/esm/messages/prune.mjs.map +1 -1
  61. package/dist/esm/messages/recency.mjs +97 -0
  62. package/dist/esm/messages/recency.mjs.map +1 -0
  63. package/dist/esm/run.mjs +30 -0
  64. package/dist/esm/run.mjs.map +1 -1
  65. package/dist/esm/summarization/node.mjs +100 -6
  66. package/dist/esm/summarization/node.mjs.map +1 -1
  67. package/dist/esm/tools/ToolNode.mjs +635 -23
  68. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  69. package/dist/esm/tools/local/CompileCheckTool.mjs +223 -0
  70. package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -0
  71. package/dist/esm/tools/local/FileCheckpointer.mjs +87 -0
  72. package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -0
  73. package/dist/esm/tools/local/LocalCodingTools.mjs +1075 -0
  74. package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -0
  75. package/dist/esm/tools/local/LocalExecutionEngine.mjs +1022 -0
  76. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -0
  77. package/dist/esm/tools/local/LocalExecutionTools.mjs +117 -0
  78. package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -0
  79. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +448 -0
  80. package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -0
  81. package/dist/esm/tools/local/attachments.mjs +180 -0
  82. package/dist/esm/tools/local/attachments.mjs.map +1 -0
  83. package/dist/esm/tools/local/bashAst.mjs +126 -0
  84. package/dist/esm/tools/local/bashAst.mjs.map +1 -0
  85. package/dist/esm/tools/local/editStrategies.mjs +185 -0
  86. package/dist/esm/tools/local/editStrategies.mjs.map +1 -0
  87. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +137 -0
  88. package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -0
  89. package/dist/esm/tools/local/syntaxCheck.mjs +179 -0
  90. package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -0
  91. package/dist/esm/tools/local/textEncoding.mjs +27 -0
  92. package/dist/esm/tools/local/textEncoding.mjs.map +1 -0
  93. package/dist/esm/tools/local/workspaceFS.mjs +49 -0
  94. package/dist/esm/tools/local/workspaceFS.mjs.map +1 -0
  95. package/dist/esm/tools/subagent/SubagentExecutor.mjs +31 -0
  96. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  97. package/dist/types/common/enum.d.ts +39 -1
  98. package/dist/types/graphs/Graph.d.ts +34 -0
  99. package/dist/types/hooks/createWorkspacePolicyHook.d.ts +95 -0
  100. package/dist/types/hooks/index.d.ts +2 -0
  101. package/dist/types/index.d.ts +1 -0
  102. package/dist/types/messages/anthropicToolCache.d.ts +51 -0
  103. package/dist/types/messages/index.d.ts +2 -0
  104. package/dist/types/messages/prune.d.ts +11 -0
  105. package/dist/types/messages/recency.d.ts +64 -0
  106. package/dist/types/run.d.ts +21 -0
  107. package/dist/types/tools/ToolNode.d.ts +145 -2
  108. package/dist/types/tools/local/CompileCheckTool.d.ts +31 -0
  109. package/dist/types/tools/local/FileCheckpointer.d.ts +39 -0
  110. package/dist/types/tools/local/LocalCodingTools.d.ts +57 -0
  111. package/dist/types/tools/local/LocalExecutionEngine.d.ts +149 -0
  112. package/dist/types/tools/local/LocalExecutionTools.d.ts +9 -0
  113. package/dist/types/tools/local/LocalProgrammaticToolCalling.d.ts +21 -0
  114. package/dist/types/tools/local/attachments.d.ts +84 -0
  115. package/dist/types/tools/local/bashAst.d.ts +11 -0
  116. package/dist/types/tools/local/editStrategies.d.ts +28 -0
  117. package/dist/types/tools/local/index.d.ts +12 -0
  118. package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +38 -0
  119. package/dist/types/tools/local/syntaxCheck.d.ts +42 -0
  120. package/dist/types/tools/local/textEncoding.d.ts +21 -0
  121. package/dist/types/tools/local/workspaceFS.d.ts +49 -0
  122. package/dist/types/tools/subagent/SubagentExecutor.d.ts +29 -0
  123. package/dist/types/types/hitl.d.ts +56 -27
  124. package/dist/types/types/run.d.ts +8 -1
  125. package/dist/types/types/summarize.d.ts +30 -0
  126. package/dist/types/types/tools.d.ts +341 -6
  127. package/package.json +21 -2
  128. package/src/common/enum.ts +54 -0
  129. package/src/graphs/Graph.ts +173 -6
  130. package/src/hooks/__tests__/compactHooks.test.ts +38 -2
  131. package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +393 -0
  132. package/src/hooks/createWorkspacePolicyHook.ts +355 -0
  133. package/src/hooks/index.ts +6 -0
  134. package/src/index.ts +1 -0
  135. package/src/messages/__tests__/anthropicToolCache.test.ts +125 -0
  136. package/src/messages/__tests__/recency.test.ts +267 -0
  137. package/src/messages/anthropicToolCache.ts +116 -0
  138. package/src/messages/index.ts +2 -0
  139. package/src/messages/prune.ts +27 -1
  140. package/src/messages/recency.ts +155 -0
  141. package/src/run.ts +31 -0
  142. package/src/scripts/compare_pi_vs_ours.ts +840 -0
  143. package/src/scripts/local_engine.ts +166 -0
  144. package/src/scripts/local_engine_checkpointer.ts +205 -0
  145. package/src/scripts/local_engine_compile.ts +263 -0
  146. package/src/scripts/local_engine_hooks.ts +226 -0
  147. package/src/scripts/local_engine_image.ts +201 -0
  148. package/src/scripts/local_engine_ptc.ts +151 -0
  149. package/src/scripts/local_engine_workspace.ts +258 -0
  150. package/src/scripts/subagent-configurable-inheritance.ts +252 -0
  151. package/src/scripts/summarization-recency.ts +462 -0
  152. package/src/specs/prune.test.ts +39 -0
  153. package/src/summarization/__tests__/node.test.ts +499 -3
  154. package/src/summarization/node.ts +124 -7
  155. package/src/tools/ToolNode.ts +769 -20
  156. package/src/tools/__tests__/LocalExecutionTools.test.ts +2647 -0
  157. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +175 -0
  158. package/src/tools/__tests__/SubagentExecutor.test.ts +148 -0
  159. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +114 -0
  160. package/src/tools/__tests__/ToolNode.session.test.ts +84 -0
  161. package/src/tools/__tests__/directToolHITLResumeScope.test.ts +467 -0
  162. package/src/tools/__tests__/directToolHooks.test.ts +411 -0
  163. package/src/tools/__tests__/localToolNames.test.ts +73 -0
  164. package/src/tools/__tests__/workspaceSeam.test.ts +134 -0
  165. package/src/tools/local/CompileCheckTool.ts +278 -0
  166. package/src/tools/local/FileCheckpointer.ts +93 -0
  167. package/src/tools/local/LocalCodingTools.ts +1342 -0
  168. package/src/tools/local/LocalExecutionEngine.ts +1329 -0
  169. package/src/tools/local/LocalExecutionTools.ts +167 -0
  170. package/src/tools/local/LocalProgrammaticToolCalling.ts +594 -0
  171. package/src/tools/local/__tests__/FileCheckpointer.test.ts +120 -0
  172. package/src/tools/local/__tests__/editStrategies.test.ts +134 -0
  173. package/src/tools/local/attachments.ts +251 -0
  174. package/src/tools/local/bashAst.ts +151 -0
  175. package/src/tools/local/editStrategies.ts +188 -0
  176. package/src/tools/local/index.ts +12 -0
  177. package/src/tools/local/resolveLocalExecutionTools.ts +208 -0
  178. package/src/tools/local/syntaxCheck.ts +243 -0
  179. package/src/tools/local/textEncoding.ts +37 -0
  180. package/src/tools/local/workspaceFS.ts +89 -0
  181. package/src/tools/subagent/SubagentExecutor.ts +60 -0
  182. package/src/types/hitl.ts +56 -27
  183. package/src/types/run.ts +12 -1
  184. package/src/types/summarize.ts +31 -0
  185. package/src/types/tools.ts +359 -7
@@ -0,0 +1,38 @@
1
+ import type * as t from '@/types';
2
+ type ResolveLocalToolsResult = {
3
+ toolMap: t.ToolMap;
4
+ directToolNames: Set<string>;
5
+ /**
6
+ * Set when `local.fileCheckpointing === true` AND the auto-bind
7
+ * coding suite is in use. ToolNode stashes this on the node and
8
+ * exposes it via `getFileCheckpointer()` so the host can call
9
+ * `rewind()` after a failed batch. Manual review (finding E)
10
+ * flagged that the config flag was previously a no-op in the
11
+ * Run/ToolNode auto-bind path — only direct
12
+ * `createLocalCodingToolBundle()` callers could access the
13
+ * checkpointer.
14
+ */
15
+ fileCheckpointer?: t.LocalFileCheckpointer;
16
+ };
17
+ export declare function resolveLocalToolsForBinding(args: {
18
+ tools?: t.GraphTools;
19
+ toolExecution?: t.ToolExecutionConfig;
20
+ }): t.GraphTools | undefined;
21
+ export declare function resolveLocalToolRegistry(args: {
22
+ toolRegistry?: t.LCToolRegistry;
23
+ toolExecution?: t.ToolExecutionConfig;
24
+ }): t.LCToolRegistry | undefined;
25
+ export declare function resolveLocalExecutionTools(args: {
26
+ toolMap: t.ToolMap;
27
+ toolExecution?: t.ToolExecutionConfig;
28
+ /**
29
+ * Caller-provided checkpointer that overrides the bundle's
30
+ * auto-created one. The Graph layer threads a single per-Run
31
+ * instance so every ToolNode it compiles shares one snapshot
32
+ * store — without that, a multi-agent graph would each get a
33
+ * private checkpointer and `Run.rewindFiles()` couldn't reach
34
+ * any of them.
35
+ */
36
+ fileCheckpointer?: t.LocalFileCheckpointer;
37
+ }): ResolveLocalToolsResult;
38
+ export {};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Per-file syntax check used by `edit_file` / `write_file` to surface
3
+ * obvious errors immediately after the write — strictly cheaper than
4
+ * full LSP integration and catches the bulk of "you broke the file"
5
+ * regressions a vision-less agent loop would otherwise miss until
6
+ * the next call.
7
+ *
8
+ * Each checker is a tiny shell-out (or in-process function) keyed on
9
+ * file extension. Failures are returned as a single short message;
10
+ * the wiring layer decides whether to append it to the tool result
11
+ * advisorily (`auto`) or to throw and force the model to react
12
+ * (`strict`).
13
+ *
14
+ * We deliberately do NOT cover TypeScript here because per-file `tsc`
15
+ * is slow and per-file syntax (without type info) misses most TS
16
+ * errors anyway. Use the project-level `compile_check` tool for that.
17
+ */
18
+ import type * as t from '@/types';
19
+ export type SyntaxCheckOutcome = {
20
+ ok: true;
21
+ } | {
22
+ ok: false;
23
+ checker: string;
24
+ output: string;
25
+ };
26
+ export type SyntaxChecker = (path: string, config: t.LocalExecutionConfig) => Promise<SyntaxCheckOutcome>;
27
+ /**
28
+ * Test-only reset hook. Clears the per-backend probe cache so tests
29
+ * can swap in mocked spawn backends and reprobe deterministically.
30
+ *
31
+ * @internal Not part of the public SDK surface.
32
+ */
33
+ export declare function _resetSyntaxCheckProbeCacheForTests(): void;
34
+ /**
35
+ * Run the post-edit syntax check for `absolutePath`. Returns
36
+ * `null` when no checker matches the extension (most files), or a
37
+ * `SyntaxCheckOutcome`.
38
+ *
39
+ * Truncates `output` to `maxOutputChars` (default 4096) so a
40
+ * 10MB-of-errors transpiler dump can't blow the model context.
41
+ */
42
+ export declare function runPostEditSyntaxCheck(absolutePath: string, config: t.LocalExecutionConfig): Promise<SyntaxCheckOutcome | null>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * BOM and line-ending preservation helpers for the local engine's
3
+ * file-mutating tools. We never *introduce* a BOM or change line
4
+ * endings — only preserve what was already on disk so a Windows-
5
+ * checked-in source file stays CRLF and a UTF-8-with-BOM JSON file
6
+ * keeps its BOM after an edit.
7
+ *
8
+ * Inspired by opencode's `Bom` helper. Trimmed to the cases that
9
+ * actually matter for editing source code (UTF-8 BOM only;
10
+ * UTF-16/UTF-32 are out of scope).
11
+ */
12
+ export interface EncodedFile {
13
+ /** File contents with BOM stripped. */
14
+ text: string;
15
+ /** Whether the on-disk file started with a UTF-8 BOM. */
16
+ hasBom: boolean;
17
+ /** Detected newline style. CRLF wins if any CRLF is present. */
18
+ newline: '\n' | '\r\n';
19
+ }
20
+ export declare function decodeFile(raw: string): EncodedFile;
21
+ export declare function encodeFile(text: string, encoding: EncodedFile): string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Engine-agnostic filesystem seam for the local-coding tool suite.
3
+ *
4
+ * The current "local" engine maps every operation to Node's
5
+ * `fs/promises` against the host machine. A future engine — e.g. a
6
+ * stateful remote sandbox (e2b / Modal / Daytona / ssh-jail) —
7
+ * supplies its own `WorkspaceFS` implementation and reuses every
8
+ * tool factory unchanged. Same fuzzy-match `edit_file`, same
9
+ * checkpointer, same syntax-check, same image attachments — only
10
+ * the underlying I/O changes.
11
+ *
12
+ * Path semantics belong to the implementation. The local engine
13
+ * interprets paths as host filesystem paths; a remote engine would
14
+ * interpret them as remote-namespace paths. Tool factories don't
15
+ * inspect the strings beyond passing them through.
16
+ *
17
+ * Keep this surface minimal. Add a method only when an existing
18
+ * tool genuinely needs it; resist the temptation to mirror all of
19
+ * `fs/promises`.
20
+ */
21
+ import type { MakeDirectoryOptions, Stats, WriteFileOptions } from 'fs';
22
+ import type { FileHandle } from 'fs/promises';
23
+ export type ReaddirEntry = {
24
+ name: string;
25
+ isFile(): boolean;
26
+ isDirectory(): boolean;
27
+ isSymbolicLink(): boolean;
28
+ };
29
+ export interface WorkspaceFS {
30
+ readFile(path: string, encoding: 'utf8'): Promise<string>;
31
+ readFile(path: string): Promise<Buffer>;
32
+ writeFile(path: string, content: string | Buffer, options?: WriteFileOptions): Promise<void>;
33
+ stat(path: string): Promise<Stats>;
34
+ readdir(path: string, options: {
35
+ withFileTypes: true;
36
+ }): Promise<ReaddirEntry[]>;
37
+ readdir(path: string): Promise<string[]>;
38
+ mkdir(path: string, options?: MakeDirectoryOptions): Promise<void>;
39
+ realpath(path: string): Promise<string>;
40
+ unlink(path: string): Promise<void>;
41
+ /** Open a file for low-level read access (used by binary detection). */
42
+ open(path: string, flags: 'r'): Promise<FileHandle>;
43
+ }
44
+ /**
45
+ * Default `WorkspaceFS` backed by Node's `fs/promises` module.
46
+ * Returned by `getWorkspaceFS(config)` when the host hasn't supplied
47
+ * an override on `local.exec.fs`.
48
+ */
49
+ export declare const nodeWorkspaceFS: WorkspaceFS;
@@ -15,6 +15,35 @@ export type SubagentExecuteParams = {
15
15
  * without relying on event ordering heuristics.
16
16
  */
17
17
  parentToolCallId?: string;
18
+ /**
19
+ * Snapshot of the parent invocation's `config.configurable` at the
20
+ * spawn-tool call site. Inherited verbatim into the child workflow's
21
+ * `configurable` so host-set fields (`requestBody`, `user`,
22
+ * `userMCPAuthMap`, etc.) propagate — fixing MCP body-placeholder
23
+ * substitution and per-user lookups for subagent tool calls.
24
+ *
25
+ * Inheritance details (verified empirically against LangGraph):
26
+ * - host-set keys propagate as-is into the child's tool dispatches;
27
+ * - `thread_id` propagates (with `childRunId` as a fallback when
28
+ * parent did not supply one) — matches the "subagent is part of
29
+ * the same conversation" mental model and aligns with the
30
+ * `sessionId: this.parentRunId` convention this executor already
31
+ * uses for `SubagentStart` / `SubagentStop` hooks;
32
+ * - `parent_run_id` propagates when the host put it on parent's
33
+ * configurable;
34
+ * - `run_id` is *overwritten by the LangGraph runtime* at child
35
+ * invoke time regardless of what we forward — child's tool
36
+ * dispatches see the child graph's runtime runId in
37
+ * `configurable.run_id`, not the parent's. Hosts that need
38
+ * parent-scoped run identity for downstream consumers should
39
+ * plumb it via a host-defined key (e.g. `requestBody.messageId`),
40
+ * not `run_id`.
41
+ *
42
+ * A future revision will likely make this inheritance configurable
43
+ * per spawn type — background / async subagents may want isolation
44
+ * rather than sharing parent's host context.
45
+ */
46
+ parentConfigurable?: Record<string, unknown>;
18
47
  };
19
48
  export type SubagentExecuteResult = {
20
49
  content: string;
@@ -203,39 +203,68 @@ export declare function isAskUserQuestionInterrupt(payload: unknown): payload is
203
203
  * matches the pre-HITL behavior so existing hosts upgrading the SDK
204
204
  * see no change until they're ready to wire the resume UI.
205
205
  *
206
- * ## Scope: event-driven tools only
206
+ * ## Scope: every tool the ToolNode runs
207
207
  *
208
- * The interrupt path is wired into `ToolNode.dispatchToolEvents`, which
209
- * runs when the agent uses event-driven tool dispatch (the path
210
- * LibreChat and most production hosts take). Tools that execute via
211
- * the direct path i.e. tools listed in `directToolNames` (the
212
- * graph-managed handoff and subagent tools) or tools on agents
213
- * configured WITHOUT `eventDrivenMode` bypass the hook system
214
- * entirely. `PreToolUse` hooks do not fire for those tools and HITL
215
- * approval does not gate them.
208
+ * The interrupt path is wired into both `dispatchToolEvents` (the
209
+ * event-driven path) and `runDirectToolWithLifecycleHooks` (the
210
+ * direct path used by `directToolNames` entries graph-managed
211
+ * handoff/subagent tools and every in-process `graphTool` instance).
212
+ * `PreToolUse` hooks fire for every tool the ToolNode invokes, and
213
+ * HITL approval gates every tool whose hook returns `'ask'` —
214
+ * regardless of whether the tool is dispatched as an event or
215
+ * invoked in-process. This convergence happened in two follow-up
216
+ * commits to the original HITL surface (see `Graph.ts` —
217
+ * `hookRegistry`/`humanInTheLoop` are passed in both
218
+ * event-driven and legacy branches; and `ToolNode.runDirectToolWithLifecycleHooks`
219
+ * — direct-path tools build their own single-tool `tool_approval`
220
+ * payload and raise `interrupt()` the same way the event path does).
216
221
  *
217
222
  * Practical implications:
218
- * - LibreChat-style hosts using event-driven dispatch get the full
219
- * HITL surface across every tool the model calls.
220
- * - Hosts using `AgentInputs.tools` directly without event-driven
221
- * mode get policy enforcement for nothing the hooks register
222
- * but never fire. Either switch to event-driven mode or accept
223
- * that direct tools are not approval-gated. This is documented
224
- * also on `ToolNodeOptions.hookRegistry`.
225
- * - Mixed direct + event batches (e.g. a handoff tool sharing an
226
- * LLM turn with a regular tool) currently re-execute the direct
227
- * half on resume, since LangGraph rolls back the entire ToolNode
228
- * on `interrupt()` throw. Hosts whose direct tools have side
229
- * effects (subagents that invoke models, handoffs that trigger
230
- * downstream work) should avoid mixing those tools into the same
231
- * batch as approval-gated event tools.
223
+ * - Every host gets the full HITL surface across every tool the
224
+ * model calls event-dispatched, direct, mixed.
225
+ * - `createToolPolicyHook` and `createWorkspacePolicyHook` apply
226
+ * uniformly. A hook can be registered without knowing or caring
227
+ * which path the tool will take.
228
+ * - Direct tools that the host opted into via `directToolNames` no
229
+ * longer bypass policy. If you need a tool to skip the hook
230
+ * surface entirely, omit it from any registered matcher.
231
+ *
232
+ * ## Resume re-execution: every tool in the interrupted batch
233
+ *
234
+ * LangGraph rolls back to the start of the interrupted node on
235
+ * resume. That means **every tool in the same batch as the one that
236
+ * interrupted re-runs from the top on the resume pass**, not just
237
+ * the interrupting tool, and not just the direct half (this used to
238
+ * be framed as a direct-tool-specific concern; it is not — it
239
+ * applies to event-dispatched siblings too). Practical contract:
240
+ *
241
+ * - The body of the interrupting tool itself runs **once** total
242
+ * (the first pass interrupted *before* the body, the resume pass
243
+ * ran the body after the host's decision was applied).
244
+ * - The body of any sibling tool that already executed in the
245
+ * same batch before the interrupting tool runs **twice** — once
246
+ * on the first pass, once on the resume pass.
247
+ * - `PreToolUse` hooks fire **once per pass per tool**. A hook
248
+ * that always returns `'ask'` will loop forever on resume; real
249
+ * hooks should be deterministic w.r.t. inputs and use the
250
+ * `'ask' → host approves → resume → hook returns 'allow'`
251
+ * pattern, where the second-pass `allow` reflects the host
252
+ * having recorded the approval (e.g., a session-scoped approved-
253
+ * paths set keyed by `runId`).
254
+ *
255
+ * Consequence: any tool with side effects MUST be idempotent if
256
+ * there's any chance another tool in the same batch could trigger
257
+ * an interrupt. This applies equally to direct tools (handoffs,
258
+ * subagents) and to event tools.
232
259
  *
233
260
  * ## Note on idempotency
234
261
  *
235
- * When an interrupt fires, LangGraph re-runs the interrupted node
236
- * from the start on resume, which fires `PreToolUse` hooks again.
237
- * Hooks that produce side effects (logging, external calls) will see
238
- * two invocations per paused turn.
262
+ * Same root cause as the resume re-execution above: LangGraph
263
+ * re-runs the interrupted node from the start on resume, which
264
+ * fires `PreToolUse` hooks again. Hooks that produce side effects
265
+ * (logging, external calls) will see at least two invocations per
266
+ * paused turn — exactly two for the interrupting tool, possibly
267
+ * more across siblings.
239
268
  */
240
269
  export interface HumanInTheLoopConfig {
241
270
  /**
@@ -7,7 +7,7 @@ import type * as s from '@/types/stream';
7
7
  import type * as e from '@/common/enum';
8
8
  import type * as g from '@/types/graph';
9
9
  import type * as l from '@/types/llm';
10
- import type { ToolSessionMap, ToolOutputReferencesConfig } from '@/types/tools';
10
+ import type { ToolSessionMap, ToolExecutionConfig, ToolOutputReferencesConfig } from '@/types/tools';
11
11
  import type { HumanInTheLoopConfig } from '@/types/hitl';
12
12
  import type { HookRegistry } from '@/hooks';
13
13
  export type ZodObjectAny = z.ZodObject<any, any, any, any>;
@@ -145,6 +145,13 @@ export type RunConfig = {
145
145
  * placeholders. Disabled by default so existing runs are unaffected.
146
146
  */
147
147
  toolOutputReferences?: ToolOutputReferencesConfig;
148
+ /**
149
+ * Selects the execution backend for built-in code tools. Omit this to keep
150
+ * the remote LibreChat Code API sandbox. Set `{ engine: 'local' }` to run
151
+ * code execution locally and auto-bind the local coding tool suite unless
152
+ * `local.includeCodingTools` is set to `false`.
153
+ */
154
+ toolExecution?: ToolExecutionConfig;
148
155
  /**
149
156
  * First-class human-in-the-loop (HITL) flow for this run.
150
157
  *
@@ -4,6 +4,29 @@ export type SummarizationTrigger = {
4
4
  type: 'token_ratio' | 'remaining_tokens' | 'messages_to_refine' | (string & {});
5
5
  value: number;
6
6
  };
7
+ /**
8
+ * Controls how many recent messages are preserved verbatim during
9
+ * compaction. The most recent user-led turn is always preserved
10
+ * regardless of these caps, so a single oversized first message is
11
+ * never destroyed by summarization.
12
+ */
13
+ export type RetainRecentConfig = {
14
+ /**
15
+ * Maximum number of recent user-led turns to keep in the tail. A turn
16
+ * begins at a HumanMessage and includes every following AIMessage and
17
+ * ToolMessage up to (but not including) the next HumanMessage. Cutting
18
+ * at turn boundaries guarantees tool_use / tool_result pairs are never
19
+ * split. Set to `0` to disable the recency window (legacy behavior:
20
+ * summarize everything). Defaults to `2`.
21
+ */
22
+ turns?: number;
23
+ /**
24
+ * Optional cap on retained-recent tokens beyond the most recent turn.
25
+ * Older turns are added whole only while cumulative tokens stay below
26
+ * the cap. Defaults to undefined (no cap; bounded only by `turns`).
27
+ */
28
+ tokens?: number;
29
+ };
7
30
  export type SummarizationConfig = {
8
31
  provider?: Providers;
9
32
  model?: string;
@@ -14,6 +37,13 @@ export type SummarizationConfig = {
14
37
  maxSummaryTokens?: number;
15
38
  /** Fraction of the token budget reserved as headroom (0–1). Defaults to 0.05. */
16
39
  reserveRatio?: number;
40
+ /**
41
+ * Recent-message preservation policy. When unset, defaults to
42
+ * `{ turns: 2 }` so the last two user-led turns are kept verbatim
43
+ * while older content is summarized. Setting `{ turns: 0 }` reverts
44
+ * to the legacy behavior of summarizing every message.
45
+ */
46
+ retainRecent?: RetainRecentConfig;
17
47
  };
18
48
  export interface SummarizeResult {
19
49
  text: string;