@librechat/agents 3.2.68 → 3.3.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 (159) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +117 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +4 -0
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +243 -49
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +103 -28
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/langfuseToolOutputTracing.cjs +4 -0
  10. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/index.cjs +11 -2
  12. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  13. package/dist/cjs/llm/contextOverflowRecovery.cjs +132 -0
  14. package/dist/cjs/llm/contextOverflowRecovery.cjs.map +1 -0
  15. package/dist/cjs/llm/google/index.cjs +1 -1
  16. package/dist/cjs/llm/invoke.cjs +60 -3
  17. package/dist/cjs/llm/invoke.cjs.map +1 -1
  18. package/dist/cjs/llm/openai/index.cjs +1 -1
  19. package/dist/cjs/main.cjs +21 -7
  20. package/dist/cjs/messages/format.cjs +136 -4
  21. package/dist/cjs/messages/format.cjs.map +1 -1
  22. package/dist/cjs/messages/prune.cjs +16 -5
  23. package/dist/cjs/messages/prune.cjs.map +1 -1
  24. package/dist/cjs/messages/recency.cjs +2 -0
  25. package/dist/cjs/messages/recency.cjs.map +1 -1
  26. package/dist/cjs/prompts/activityLabel.cjs +101 -0
  27. package/dist/cjs/prompts/activityLabel.cjs.map +1 -0
  28. package/dist/cjs/run.cjs +177 -3
  29. package/dist/cjs/run.cjs.map +1 -1
  30. package/dist/cjs/stream.cjs +205 -49
  31. package/dist/cjs/stream.cjs.map +1 -1
  32. package/dist/cjs/summarization/node.cjs +55 -7
  33. package/dist/cjs/summarization/node.cjs.map +1 -1
  34. package/dist/cjs/tools/BashExecutor.cjs +2 -2
  35. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  36. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -2
  37. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  38. package/dist/cjs/tools/CodeExecutor.cjs +69 -8
  39. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  40. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +19 -15
  41. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  42. package/dist/cjs/tools/ToolNode.cjs +32 -3
  43. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  44. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
  45. package/dist/cjs/utils/errors.cjs +317 -52
  46. package/dist/cjs/utils/errors.cjs.map +1 -1
  47. package/dist/esm/agents/AgentContext.mjs +118 -4
  48. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  49. package/dist/esm/common/enum.mjs +4 -0
  50. package/dist/esm/common/enum.mjs.map +1 -1
  51. package/dist/esm/graphs/Graph.mjs +242 -48
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/graphs/MultiAgentGraph.mjs +103 -28
  54. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  55. package/dist/esm/langfuseToolOutputTracing.mjs +4 -1
  56. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  57. package/dist/esm/llm/bedrock/index.mjs +11 -2
  58. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  59. package/dist/esm/llm/contextOverflowRecovery.mjs +130 -0
  60. package/dist/esm/llm/contextOverflowRecovery.mjs.map +1 -0
  61. package/dist/esm/llm/google/index.mjs +1 -1
  62. package/dist/esm/llm/invoke.mjs +59 -4
  63. package/dist/esm/llm/invoke.mjs.map +1 -1
  64. package/dist/esm/llm/openai/index.mjs +1 -1
  65. package/dist/esm/main.mjs +10 -10
  66. package/dist/esm/messages/format.mjs +136 -5
  67. package/dist/esm/messages/format.mjs.map +1 -1
  68. package/dist/esm/messages/prune.mjs +14 -6
  69. package/dist/esm/messages/prune.mjs.map +1 -1
  70. package/dist/esm/messages/recency.mjs +2 -1
  71. package/dist/esm/messages/recency.mjs.map +1 -1
  72. package/dist/esm/prompts/activityLabel.mjs +100 -0
  73. package/dist/esm/prompts/activityLabel.mjs.map +1 -0
  74. package/dist/esm/run.mjs +178 -4
  75. package/dist/esm/run.mjs.map +1 -1
  76. package/dist/esm/stream.mjs +205 -49
  77. package/dist/esm/stream.mjs.map +1 -1
  78. package/dist/esm/summarization/node.mjs +55 -7
  79. package/dist/esm/summarization/node.mjs.map +1 -1
  80. package/dist/esm/tools/BashExecutor.mjs +3 -3
  81. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  82. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -3
  83. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  84. package/dist/esm/tools/CodeExecutor.mjs +62 -9
  85. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  86. package/dist/esm/tools/ProgrammaticToolCalling.mjs +20 -16
  87. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +32 -3
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
  91. package/dist/esm/utils/errors.mjs +317 -53
  92. package/dist/esm/utils/errors.mjs.map +1 -1
  93. package/dist/types/agents/AgentContext.d.ts +62 -3
  94. package/dist/types/common/enum.d.ts +5 -1
  95. package/dist/types/graphs/Graph.d.ts +16 -2
  96. package/dist/types/graphs/MultiAgentGraph.d.ts +1 -0
  97. package/dist/types/langfuseToolOutputTracing.d.ts +4 -0
  98. package/dist/types/llm/contextOverflowRecovery.d.ts +85 -0
  99. package/dist/types/llm/invoke.d.ts +35 -6
  100. package/dist/types/messages/format.d.ts +22 -0
  101. package/dist/types/messages/prune.d.ts +10 -2
  102. package/dist/types/messages/recency.d.ts +1 -0
  103. package/dist/types/prompts/activityLabel.d.ts +31 -0
  104. package/dist/types/run.d.ts +16 -0
  105. package/dist/types/tools/CodeExecutor.d.ts +14 -1
  106. package/dist/types/types/activityLabel.d.ts +53 -0
  107. package/dist/types/types/index.d.ts +1 -0
  108. package/dist/types/types/llm.d.ts +7 -4
  109. package/dist/types/types/stream.d.ts +7 -4
  110. package/dist/types/types/summarize.d.ts +22 -0
  111. package/dist/types/utils/__tests__/fixtures/contextOverflowSignatures.d.ts +40 -0
  112. package/dist/types/utils/errors.d.ts +65 -16
  113. package/dist/types/utils/redactSecrets.d.ts +3 -0
  114. package/package.json +7 -8
  115. package/src/agents/AgentContext.ts +188 -7
  116. package/src/agents/__tests__/AgentContext.overflow.test.ts +205 -0
  117. package/src/common/enum.ts +4 -0
  118. package/src/graphs/Graph.ts +409 -58
  119. package/src/graphs/MultiAgentGraph.ts +184 -46
  120. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +631 -0
  121. package/src/langfuseToolOutputTracing.ts +4 -1
  122. package/src/llm/__tests__/contextOverflowRecovery.test.ts +401 -0
  123. package/src/llm/__tests__/fallbackOverflow.test.ts +287 -0
  124. package/src/llm/bedrock/index.ts +25 -12
  125. package/src/llm/contextOverflowRecovery.ts +292 -0
  126. package/src/llm/invoke.ts +119 -4
  127. package/src/messages/foldToollessToolBlocks.test.ts +438 -0
  128. package/src/messages/format.ts +233 -5
  129. package/src/messages/prune.ts +24 -11
  130. package/src/messages/recency.ts +3 -1
  131. package/src/prompts/activityLabel.ts +177 -0
  132. package/src/run.ts +322 -3
  133. package/src/scripts/context-overflow-probe.ts +997 -0
  134. package/src/specs/activity-label-prompt.test.ts +128 -0
  135. package/src/specs/activity-label-trace-seed.test.ts +47 -0
  136. package/src/specs/agent-handoffs.test.ts +903 -1
  137. package/src/specs/bedrock-toolless.live.test.ts +123 -0
  138. package/src/specs/context-overflow-recovery.live.test.ts +213 -0
  139. package/src/splitStream.test.ts +882 -0
  140. package/src/stream.ts +315 -51
  141. package/src/summarization/__tests__/aggregator.test.ts +83 -0
  142. package/src/summarization/__tests__/node.test.ts +139 -0
  143. package/src/summarization/node.ts +99 -14
  144. package/src/tools/BashExecutor.ts +4 -2
  145. package/src/tools/BashProgrammaticToolCalling.ts +4 -7
  146. package/src/tools/CodeExecutor.ts +119 -8
  147. package/src/tools/ProgrammaticToolCalling.ts +29 -27
  148. package/src/tools/ToolNode.ts +50 -8
  149. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +297 -3
  150. package/src/types/activityLabel.ts +55 -0
  151. package/src/types/index.ts +1 -0
  152. package/src/types/llm.ts +8 -1
  153. package/src/types/stream.ts +7 -4
  154. package/src/types/summarize.ts +22 -0
  155. package/src/utils/__tests__/errors.test.ts +270 -0
  156. package/src/utils/__tests__/fixtures/contextOverflowSignatures.ts +336 -0
  157. package/src/utils/__tests__/redactSecrets.test.ts +56 -0
  158. package/src/utils/errors.ts +484 -66
  159. package/src/utils/redactSecrets.ts +61 -0
@@ -0,0 +1,101 @@
1
+ const require_langfuseToolOutputTracing = require("../langfuseToolOutputTracing.cjs");
2
+ //#region src/prompts/activityLabel.ts
3
+ /**
4
+ * Default system prompt for fast-model activity labeling.
5
+ *
6
+ * Style synthesized from Claude Code's tool-use summary prompt (git-subject
7
+ * register, past tense, distinctive nouns) and claude.ai's observed group
8
+ * headers (5–9 words describing a mixed reasoning + tool block, e.g.
9
+ * "Synthesized version data and curated comparative framework").
10
+ */
11
+ const ACTIVITY_LABEL_PROMPT = `Write a short label describing what this block of agent activity accomplished. It appears as the header of a collapsed activity group in a chat UI.
12
+
13
+ Rules:
14
+ - 5 to 9 words, past-tense verb first
15
+ - Name the most distinctive subject (file, API, topic); drop articles and filler
16
+ - Describe outcomes, not mechanics; if something failed, say so plainly
17
+ - Output only the label — no quotes, no punctuation at the end, no preamble
18
+
19
+ Examples:
20
+ - Searched Node.js release notes and changelogs
21
+ - Compared runtime versions across official sources
22
+ - Fixed failing auth middleware tests
23
+ - Read project config and dependency manifests
24
+ - Attempted database migration, hit permission errors`;
25
+ /** Truncates a serialized value for the label prompt. */
26
+ function truncateForLabel(value, maxLength) {
27
+ if (value.length <= maxLength) return value;
28
+ return value.slice(0, Math.max(0, maxLength - 1)) + "…";
29
+ }
30
+ const ABORT_SERIALIZATION = Symbol("abort-label-serialization");
31
+ /**
32
+ * Serializes a tool value for the prompt WITHOUT materializing huge JSON:
33
+ * the output is clipped to a few hundred characters anyway, so a multi-
34
+ * megabyte tool result must not be stringified in full on the label path.
35
+ * Strings clip immediately; structured values serialize under a character
36
+ * budget and degrade to a shape summary once it is exhausted.
37
+ */
38
+ function serializeForLabel(value, limit) {
39
+ if (value == null) return "";
40
+ if (typeof value === "string") return value.length > limit ? value.slice(0, limit + 1) : value;
41
+ let budget = limit * 4;
42
+ try {
43
+ return JSON.stringify(value, (_key, nested) => {
44
+ if (budget <= 0) throw ABORT_SERIALIZATION;
45
+ if (typeof nested === "string") {
46
+ const clipped = nested.length > limit ? nested.slice(0, limit) : nested;
47
+ budget -= clipped.length;
48
+ return clipped;
49
+ }
50
+ budget -= 8;
51
+ return nested;
52
+ }) ?? "";
53
+ } catch (error) {
54
+ if (error === ABORT_SERIALIZATION) return Array.isArray(value) ? `[Array(${value.length})]` : "[Object]";
55
+ return String(value);
56
+ }
57
+ }
58
+ const INPUT_CONTEXT_LIMIT = 200;
59
+ const MAX_THINKING_EXCERPTS = 4;
60
+ /** A label is 5-9 words; no batch needs more than this many entries to
61
+ * produce one, and the cap keeps a 200-call programmatic batch from
62
+ * building an enormous prompt out of per-field-bounded pieces. */
63
+ const MAX_PROMPT_ENTRIES = 12;
64
+ /**
65
+ * Builds the user prompt for a fast-model activity label. Pure — exported
66
+ * for direct testing of redaction and truncation behavior.
67
+ */
68
+ function buildActivityLabelPrompt({ entries, charLimit, thinkingExcerpts, lastAssistantText, redaction }) {
69
+ const clip = truncateForLabel;
70
+ /** Reasoning and intent text can quote tool output verbatim — including
71
+ * output from EARLIER calls to a redacted tool that this batch does not
72
+ * contain — so any active policy (global disable or a configured
73
+ * redacted-name list) drops both wholesale. There is no reliable way to
74
+ * scrub a quoted fragment out of free-form model prose. */
75
+ const excerptsRedacted = redaction != null && (redaction.enabled === false || redaction.redactedToolNames.size > 0);
76
+ const sections = [];
77
+ /** Intent text is free-form assistant prose that can quote a redacted
78
+ * tool result just as reasoning can, so it shares the excerpts' fate. */
79
+ if (!excerptsRedacted && lastAssistantText != null && lastAssistantText.length > 0) sections.push(`Intent (assistant's last message): ${clip(lastAssistantText, INPUT_CONTEXT_LIMIT)}`);
80
+ if (!excerptsRedacted && thinkingExcerpts != null && thinkingExcerpts.length > 0) sections.push("Reasoning excerpts:\n" + thinkingExcerpts.slice(0, MAX_THINKING_EXCERPTS).map((excerpt) => `- ${clip(excerpt, charLimit)}`).join("\n"));
81
+ if (entries.length > 0) {
82
+ const shown = entries.slice(0, MAX_PROMPT_ENTRIES);
83
+ const omitted = entries.length - shown.length;
84
+ sections.push("Tool calls:\n" + shown.map((entry) => {
85
+ const input = clip(serializeForLabel(entry.toolInput, charLimit), charLimit);
86
+ const redacted = redaction != null && require_langfuseToolOutputTracing.shouldRedactTool(entry.toolName, redaction);
87
+ let outcome;
88
+ if (redacted) outcome = redaction.redactionText;
89
+ else if (entry.status === "error") outcome = `ERROR: ${clip(entry.error ?? "unknown error", charLimit)}`;
90
+ else outcome = clip(serializeForLabel(entry.toolOutput, charLimit), charLimit);
91
+ return `- ${entry.toolName}(${input}) → ${outcome}`;
92
+ }).join("\n") + (omitted > 0 ? `\n- …and ${omitted} more tool ${omitted === 1 ? "call" : "calls"}` : ""));
93
+ }
94
+ sections.push("Label:");
95
+ return sections.join("\n\n");
96
+ }
97
+ //#endregion
98
+ exports.ACTIVITY_LABEL_PROMPT = ACTIVITY_LABEL_PROMPT;
99
+ exports.buildActivityLabelPrompt = buildActivityLabelPrompt;
100
+
101
+ //# sourceMappingURL=activityLabel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activityLabel.cjs","names":["shouldRedactTool"],"sources":["../../../src/prompts/activityLabel.ts"],"sourcesContent":["import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type { ActivityLabelToolEntry } from '@/types/activityLabel';\nimport { shouldRedactTool } from '@/langfuseToolOutputTracing';\n\n/**\n * Default system prompt for fast-model activity labeling.\n *\n * Style synthesized from Claude Code's tool-use summary prompt (git-subject\n * register, past tense, distinctive nouns) and claude.ai's observed group\n * headers (5–9 words describing a mixed reasoning + tool block, e.g.\n * \"Synthesized version data and curated comparative framework\").\n */\nexport const ACTIVITY_LABEL_PROMPT = `Write a short label describing what this block of agent activity accomplished. It appears as the header of a collapsed activity group in a chat UI.\n\nRules:\n- 5 to 9 words, past-tense verb first\n- Name the most distinctive subject (file, API, topic); drop articles and filler\n- Describe outcomes, not mechanics; if something failed, say so plainly\n- Output only the label — no quotes, no punctuation at the end, no preamble\n\nExamples:\n- Searched Node.js release notes and changelogs\n- Compared runtime versions across official sources\n- Fixed failing auth middleware tests\n- Read project config and dependency manifests\n- Attempted database migration, hit permission errors`;\n\n/** Truncates a serialized value for the label prompt. */\nexport function truncateForLabel(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n return value.slice(0, Math.max(0, maxLength - 1)) + '…';\n}\n\nconst ABORT_SERIALIZATION = Symbol('abort-label-serialization');\n\n/**\n * Serializes a tool value for the prompt WITHOUT materializing huge JSON:\n * the output is clipped to a few hundred characters anyway, so a multi-\n * megabyte tool result must not be stringified in full on the label path.\n * Strings clip immediately; structured values serialize under a character\n * budget and degrade to a shape summary once it is exhausted.\n */\nfunction serializeForLabel(value: unknown, limit: number): string {\n if (value == null) {\n return '';\n }\n if (typeof value === 'string') {\n return value.length > limit ? value.slice(0, limit + 1) : value;\n }\n let budget = limit * 4;\n try {\n return (\n JSON.stringify(value, (_key, nested: unknown) => {\n if (budget <= 0) {\n throw ABORT_SERIALIZATION;\n }\n if (typeof nested === 'string') {\n const clipped =\n nested.length > limit ? nested.slice(0, limit) : nested;\n budget -= clipped.length;\n return clipped;\n }\n budget -= 8;\n return nested;\n }) ?? ''\n );\n } catch (error) {\n if (error === ABORT_SERIALIZATION) {\n return Array.isArray(value) ? `[Array(${value.length})]` : '[Object]';\n }\n return String(value);\n }\n}\n\nconst INPUT_CONTEXT_LIMIT = 200;\nconst MAX_THINKING_EXCERPTS = 4;\n/** A label is 5-9 words; no batch needs more than this many entries to\n * produce one, and the cap keeps a 200-call programmatic batch from\n * building an enormous prompt out of per-field-bounded pieces. */\nconst MAX_PROMPT_ENTRIES = 12;\n\nexport type BuildActivityLabelPromptParams = {\n entries: ActivityLabelToolEntry[];\n charLimit: number;\n thinkingExcerpts?: string[];\n lastAssistantText?: string;\n /**\n * Resolved tool-output tracing policy. The label prompt becomes Langfuse\n * generation input, so outputs/errors excluded from tracing (global\n * disable or `redactedToolNames`) must never appear in it — the same\n * redaction the span processor applies to structured tool observations.\n */\n redaction?: ResolvedLangfuseToolOutputTracingConfig;\n};\n\n/**\n * Builds the user prompt for a fast-model activity label. Pure — exported\n * for direct testing of redaction and truncation behavior.\n */\nexport function buildActivityLabelPrompt({\n entries,\n charLimit,\n thinkingExcerpts,\n lastAssistantText,\n redaction,\n}: BuildActivityLabelPromptParams): string {\n const clip = truncateForLabel;\n /** Reasoning and intent text can quote tool output verbatim — including\n * output from EARLIER calls to a redacted tool that this batch does not\n * contain — so any active policy (global disable or a configured\n * redacted-name list) drops both wholesale. There is no reliable way to\n * scrub a quoted fragment out of free-form model prose. */\n const excerptsRedacted =\n redaction != null &&\n (redaction.enabled === false || redaction.redactedToolNames.size > 0);\n const sections: string[] = [];\n /** Intent text is free-form assistant prose that can quote a redacted\n * tool result just as reasoning can, so it shares the excerpts' fate. */\n if (\n !excerptsRedacted &&\n lastAssistantText != null &&\n lastAssistantText.length > 0\n ) {\n sections.push(\n `Intent (assistant's last message): ${clip(lastAssistantText, INPUT_CONTEXT_LIMIT)}`\n );\n }\n if (\n !excerptsRedacted &&\n thinkingExcerpts != null &&\n thinkingExcerpts.length > 0\n ) {\n sections.push(\n 'Reasoning excerpts:\\n' +\n thinkingExcerpts\n .slice(0, MAX_THINKING_EXCERPTS)\n .map((excerpt) => `- ${clip(excerpt, charLimit)}`)\n .join('\\n')\n );\n }\n if (entries.length > 0) {\n const shown = entries.slice(0, MAX_PROMPT_ENTRIES);\n const omitted = entries.length - shown.length;\n sections.push(\n 'Tool calls:\\n' +\n shown\n .map((entry) => {\n const input = clip(\n serializeForLabel(entry.toolInput, charLimit),\n charLimit\n );\n const redacted =\n redaction != null && shouldRedactTool(entry.toolName, redaction);\n let outcome: string;\n if (redacted) {\n outcome = redaction.redactionText;\n } else if (entry.status === 'error') {\n outcome = `ERROR: ${clip(entry.error ?? 'unknown error', charLimit)}`;\n } else {\n outcome = clip(\n serializeForLabel(entry.toolOutput, charLimit),\n charLimit\n );\n }\n return `- ${entry.toolName}(${input}) → ${outcome}`;\n })\n .join('\\n') +\n (omitted > 0\n ? `\\n- …and ${omitted} more tool ${omitted === 1 ? 'call' : 'calls'}`\n : '')\n );\n }\n sections.push('Label:');\n return sections.join('\\n\\n');\n}\n"],"mappings":";;;;;;;;;;AAYA,MAAa,wBAAwB;;;;;;;;;;;;;;;AAgBrC,SAAgB,iBAAiB,OAAe,WAA2B;CACzE,IAAI,MAAM,UAAU,WAClB,OAAO;CAET,OAAO,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,IAAI;AACtD;AAEA,MAAM,sBAAsB,OAAO,2BAA2B;;;;;;;;AAS9D,SAAS,kBAAkB,OAAgB,OAAuB;CAChE,IAAI,SAAS,MACX,OAAO;CAET,IAAI,OAAO,UAAU,UACnB,OAAO,MAAM,SAAS,QAAQ,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI;CAE5D,IAAI,SAAS,QAAQ;CACrB,IAAI;EACF,OACE,KAAK,UAAU,QAAQ,MAAM,WAAoB;GAC/C,IAAI,UAAU,GACZ,MAAM;GAER,IAAI,OAAO,WAAW,UAAU;IAC9B,MAAM,UACJ,OAAO,SAAS,QAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;IACnD,UAAU,QAAQ;IAClB,OAAO;GACT;GACA,UAAU;GACV,OAAO;EACT,CAAC,KAAK;CAEV,SAAS,OAAO;EACd,IAAI,UAAU,qBACZ,OAAO,MAAM,QAAQ,KAAK,IAAI,UAAU,MAAM,OAAO,MAAM;EAE7D,OAAO,OAAO,KAAK;CACrB;AACF;AAEA,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;;;;AAI9B,MAAM,qBAAqB;;;;;AAoB3B,SAAgB,yBAAyB,EACvC,SACA,WACA,kBACA,mBACA,aACyC;CACzC,MAAM,OAAO;;;;;;CAMb,MAAM,mBACJ,aAAa,SACZ,UAAU,YAAY,SAAS,UAAU,kBAAkB,OAAO;CACrE,MAAM,WAAqB,CAAC;;;CAG5B,IACE,CAAC,oBACD,qBAAqB,QACrB,kBAAkB,SAAS,GAE3B,SAAS,KACP,sCAAsC,KAAK,mBAAmB,mBAAmB,GACnF;CAEF,IACE,CAAC,oBACD,oBAAoB,QACpB,iBAAiB,SAAS,GAE1B,SAAS,KACP,0BACE,iBACG,MAAM,GAAG,qBAAqB,CAAC,CAC/B,KAAK,YAAY,KAAK,KAAK,SAAS,SAAS,GAAG,CAAC,CACjD,KAAK,IAAI,CAChB;CAEF,IAAI,QAAQ,SAAS,GAAG;EACtB,MAAM,QAAQ,QAAQ,MAAM,GAAG,kBAAkB;EACjD,MAAM,UAAU,QAAQ,SAAS,MAAM;EACvC,SAAS,KACP,kBACE,MACG,KAAK,UAAU;GACd,MAAM,QAAQ,KACZ,kBAAkB,MAAM,WAAW,SAAS,GAC5C,SACF;GACA,MAAM,WACJ,aAAa,QAAQA,kCAAAA,iBAAiB,MAAM,UAAU,SAAS;GACjE,IAAI;GACJ,IAAI,UACF,UAAU,UAAU;QACf,IAAI,MAAM,WAAW,SAC1B,UAAU,UAAU,KAAK,MAAM,SAAS,iBAAiB,SAAS;QAElE,UAAU,KACR,kBAAkB,MAAM,YAAY,SAAS,GAC7C,SACF;GAEF,OAAO,KAAK,MAAM,SAAS,GAAG,MAAM,MAAM;EAC5C,CAAC,CAAC,CACD,KAAK,IAAI,KACX,UAAU,IACP,YAAY,QAAQ,aAAa,YAAY,IAAI,SAAS,YAC1D,GACR;CACF;CACA,SAAS,KAAK,QAAQ;CACtB,OAAO,SAAS,KAAK,MAAM;AAC7B"}
package/dist/cjs/run.cjs CHANGED
@@ -1,6 +1,8 @@
1
+ const require_langfuseRuntimeContext = require("./langfuseRuntimeContext.cjs");
1
2
  const require_langfuseConfig = require("./langfuseConfig.cjs");
2
3
  const require_langfuseRuntimeScope = require("./langfuseRuntimeScope.cjs");
3
4
  const require_langfuse = require("./langfuse.cjs");
5
+ const require_activityLabel = require("./prompts/activityLabel.cjs");
4
6
  const require_callbacks = require("./utils/callbacks.cjs");
5
7
  require("./common/enum.cjs");
6
8
  require("./common/index.cjs");
@@ -9,16 +11,16 @@ const require_tokens = require("./utils/tokens.cjs");
9
11
  const require_instrumentation = require("./instrumentation.cjs");
10
12
  const require_llm = require("./utils/llm.cjs");
11
13
  const require_events = require("./events.cjs");
14
+ const require_init = require("./llm/init.cjs");
12
15
  const require_executeHooks = require("./hooks/executeHooks.cjs");
13
16
  require("./hooks/index.cjs");
14
- const require_init = require("./llm/init.cjs");
15
17
  const require_Graph = require("./graphs/Graph.cjs");
16
18
  const require_MultiAgentGraph = require("./graphs/MultiAgentGraph.cjs");
17
- let _langchain_core_messages = require("@langchain/core/messages");
18
19
  let _langchain_core_prompts = require("@langchain/core/prompts");
19
20
  let _langchain_core_runnables = require("@langchain/core/runnables");
20
21
  let _langchain_openai = require("@langchain/openai");
21
22
  let _langchain_core_callbacks_base = require("@langchain/core/callbacks/base");
23
+ let _langchain_core_messages = require("@langchain/core/messages");
22
24
  let _langchain_langgraph = require("@langchain/langgraph");
23
25
  //#region src/run.ts
24
26
  const defaultOmitOptions = new Set([
@@ -99,6 +101,10 @@ var Run = class Run {
99
101
  * lets callers assert the type they expect.
100
102
  */
101
103
  _interrupt;
104
+ /** Per-run sequence for batch-unique activity-label trace-seed fallbacks. */
105
+ activityLabelSeq = 0;
106
+ /** Distinguishes sibling forks started from the same explicit checkpoint. */
107
+ checkpointForkSeq = 0;
102
108
  _haltedReason;
103
109
  constructor(config) {
104
110
  const runId = config.runId ?? "";
@@ -462,7 +468,18 @@ var Run = class Run {
462
468
  * block so the sidecars survive both ends of the interrupt
463
469
  * boundary.
464
470
  */
465
- if (!isResume) graph.resetValues(streamOptions?.keepContent);
471
+ if (!isResume) {
472
+ const checkpointThreadId = typeof config.configurable?.thread_id === "string" ? config.configurable.thread_id : void 0;
473
+ const checkpointNamespace = typeof config.configurable?.checkpoint_ns === "string" ? config.configurable.checkpoint_ns : "";
474
+ const checkpointId = typeof config.configurable?.checkpoint_id === "string" ? config.configurable.checkpoint_id : "";
475
+ const checkpointScope = checkpointThreadId == null ? void 0 : JSON.stringify([
476
+ checkpointThreadId,
477
+ checkpointNamespace,
478
+ checkpointId,
479
+ checkpointId === "" ? 0 : ++this.checkpointForkSeq
480
+ ]);
481
+ graph.resetValues(streamOptions?.keepContent, checkpointScope);
482
+ }
466
483
  this._interrupt = void 0;
467
484
  this._haltedReason = void 0;
468
485
  this.hookRegistry?.clearHaltSignal(this.id);
@@ -960,6 +977,163 @@ var Run = class Run {
960
977
  await require_langfuse.disposeLangfuseHandler(titleLangfuseHandler);
961
978
  }
962
979
  }
980
+ /**
981
+ * Generates a short activity label for a completed tool/reasoning block
982
+ * using a fast model. Mirrors `generateTitle`'s Langfuse wiring so the
983
+ * call is traced under the conversation's session (sessionId from
984
+ * `chainOptions.configurable.thread_id`) with its own tags — never as an
985
+ * orphan trace. The payload contains no human messages by design: intent
986
+ * comes from `lastAssistantText`, content from reasoning excerpts and
987
+ * tool entries.
988
+ */
989
+ async generateActivityLabel({ provider, clientOptions, entries, thinkingExcerpts, lastAssistantText, prompt, charLimit = 600, chainOptions, traceSeed, agentId }) {
990
+ if (entries.length === 0 && !(thinkingExcerpts && thinkingExcerpts.length > 0)) return {};
991
+ const labelSeq = ++this.activityLabelSeq;
992
+ /** Resolve the LABELED agent's context: its Langfuse overlay carries the
993
+ * trace metadata and the tool-output redaction policy that must govern
994
+ * this label. */
995
+ const requestedContext = this.Graph == null || agentId == null ? void 0 : this.Graph.agentContexts.get(agentId);
996
+ /** Fail closed: an explicit but unknown/stale `agentId` must NOT silently
997
+ * fall back to the default agent, whose redaction policy may be weaker
998
+ * than the labeled agent's. Skip generation entirely instead. */
999
+ if (agentId != null && requestedContext == null) return {};
1000
+ const labelContext = this.Graph == null ? void 0 : requestedContext ?? this.Graph.agentContexts.get(this.Graph.defaultAgentId);
1001
+ const traceMetadata = require_langfuse.createLangfuseTraceMetadata({
1002
+ messageId: "activity-label-" + this.id,
1003
+ agentName: labelContext?.name
1004
+ });
1005
+ const labelRunName = require_langfuse.getLangfuseTraceName(traceMetadata, "LibreChat Activity Label");
1006
+ /** Shallow-cloned: activity labels run once per tool batch, and writing
1007
+ * the Langfuse handler back onto a host-reused `chainOptions` would
1008
+ * accumulate duplicate callbacks across batches. */
1009
+ const labelChainOptions = { ...chainOptions ?? {} };
1010
+ const labelUserId = typeof labelChainOptions.configurable?.user_id === "string" ? labelChainOptions.configurable.user_id : void 0;
1011
+ const labelSessionId = typeof labelChainOptions.configurable?.thread_id === "string" ? labelChainOptions.configurable.thread_id : void 0;
1012
+ const labelLangfuseConfig = require_langfuseConfig.resolveLangfuseConfig(this.langfuse, labelContext?.langfuse);
1013
+ require_instrumentation.initializeLangfuseTracing(labelLangfuseConfig);
1014
+ /** Seed policy, threading two constraints:
1015
+ * 1. `runWithLangfuseRuntimeContext` SPREADS the surrounding context, so
1016
+ * an absent seed INHERITS the parent run's and collapses every label
1017
+ * into that trace. When a parent seed is active we must override it
1018
+ * with a per-label one.
1019
+ * 2. Without deterministic tracing there is no parent seed, and forcing
1020
+ * one here would make label trace ids deterministic when neither
1021
+ * `processStream` nor `generateTitle` are — so leave it unset.
1022
+ * Seeded when determinism is opted into OR a parent seed is live;
1023
+ * otherwise unseeded, matching the other generation paths. */
1024
+ const inheritedTraceSeed = require_langfuseRuntimeContext.getTraceIdSeed();
1025
+ const labelTraceSeed = labelLangfuseConfig?.deterministicTraceId === true || inheritedTraceSeed != null ? traceSeed ?? `activity-label-${this.id}-${labelSeq}` : void 0;
1026
+ const labelRuntimeScope = require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
1027
+ runLangfuse: this.langfuse,
1028
+ langfuseOverlay: labelContext?.langfuse,
1029
+ traceIdSeed: labelTraceSeed
1030
+ });
1031
+ /** Handler only when a session id resolved from
1032
+ * `chainOptions.configurable.thread_id`: without it the label call has
1033
+ * no conversation identity, and tracing it would create an orphan
1034
+ * trace outside any session — worse than not tracing at all. */
1035
+ /** Declared then conditionally assigned (title precedent): a ternary
1036
+ * around the object literal makes eslint's indent rule and prettier
1037
+ * disagree, and both gate CI. */
1038
+ let labelLangfuseHandler;
1039
+ if (labelSessionId != null) labelLangfuseHandler = require_langfuse.createLangfuseHandler({
1040
+ langfuse: labelLangfuseConfig,
1041
+ userId: labelUserId,
1042
+ sessionId: labelSessionId,
1043
+ traceMetadata,
1044
+ tags: ["librechat", "activity-label"],
1045
+ traceIdSeed: labelLangfuseConfig?.deterministicTraceId === true ? labelTraceSeed : void 0
1046
+ });
1047
+ if (labelLangfuseHandler != null) labelChainOptions.callbacks = require_callbacks.appendCallbacks(labelChainOptions.callbacks, [labelLangfuseHandler]);
1048
+ /** The label prompt becomes Langfuse generation input, so the resolved
1049
+ * tool-output redaction policy (global disable / redactedToolNames)
1050
+ * applies to it exactly as to structured tool observations. */
1051
+ let redaction = require_langfuseConfig.hasToolOutputTracingConfig(this.langfuse, labelContext?.langfuse) ? require_langfuseConfig.resolveToolOutputTracingConfig(this.langfuse, labelContext?.langfuse) : void 0;
1052
+ /** Multi-agent graph with no `agentId`: the caller did not say WHICH
1053
+ * agent ran this batch, so resolving from the default agent could trace
1054
+ * raw output that a stricter sibling's policy forbids. Fold every
1055
+ * agent's policy into the strictest one instead of guessing. */
1056
+ const agentContexts = this.Graph?.agentContexts;
1057
+ if (agentId == null && agentContexts != null && agentContexts.size > 1) for (const context of agentContexts.values()) {
1058
+ if (!require_langfuseConfig.hasToolOutputTracingConfig(this.langfuse, context.langfuse)) continue;
1059
+ const candidate = require_langfuseConfig.resolveToolOutputTracingConfig(this.langfuse, context.langfuse);
1060
+ if (redaction == null) {
1061
+ redaction = candidate;
1062
+ continue;
1063
+ }
1064
+ redaction = {
1065
+ enabled: redaction.enabled === false ? false : candidate.enabled,
1066
+ redactedToolNames: new Set([...redaction.redactedToolNames, ...candidate.redactedToolNames]),
1067
+ redactedToolNameMatchMode: redaction.redactedToolNameMatchMode === "partial" || candidate.redactedToolNameMatchMode === "partial" ? "partial" : "exact",
1068
+ redactionText: redaction.redactionText
1069
+ };
1070
+ }
1071
+ /** An active redaction policy suppresses free-form reasoning/intent, so
1072
+ * a reasoning-only block has nothing describable left — skip the model
1073
+ * call rather than paying for a label built from the prompt alone. */
1074
+ const freeFormSuppressed = redaction != null && (redaction.enabled === false || redaction.redactedToolNames.size > 0);
1075
+ if (entries.length === 0 && freeFormSuppressed) return {};
1076
+ const userPrompt = require_activityLabel.buildActivityLabelPrompt({
1077
+ entries,
1078
+ charLimit,
1079
+ thinkingExcerpts,
1080
+ lastAssistantText,
1081
+ redaction
1082
+ });
1083
+ const model = require_init.initializeModel({
1084
+ provider,
1085
+ clientOptions: {
1086
+ ...clientOptions ?? {},
1087
+ streaming: false
1088
+ }
1089
+ });
1090
+ /** Distinct run id per label call: callback/tracing integrations key
1091
+ * in-flight runs by it, so reusing the parent run's id would collide
1092
+ * across successive (or concurrent) label batches. */
1093
+ const labelRunId = `${this.id}-activity-${labelSeq}`;
1094
+ const invokeConfig = Object.assign({}, labelChainOptions, {
1095
+ run_id: labelRunId,
1096
+ runId: labelRunId,
1097
+ runName: labelChainOptions.runName ?? labelRunName
1098
+ });
1099
+ const invokeLabel = (runtimeConfig) => require_langfuse.withLangfuseAttributes({
1100
+ langfuse: labelLangfuseConfig,
1101
+ userId: labelUserId,
1102
+ sessionId: labelSessionId,
1103
+ traceName: runtimeConfig.runName ?? labelRunName,
1104
+ traceMetadata,
1105
+ tags: ["librechat", "activity-label"]
1106
+ }, () => model.invoke([new _langchain_core_messages.SystemMessage(prompt ?? require_activityLabel.ACTIVITY_LABEL_PROMPT), new _langchain_core_messages.HumanMessage(userPrompt)], runtimeConfig));
1107
+ const extractLabel = (response) => {
1108
+ const content = response?.content;
1109
+ let text = "";
1110
+ if (typeof content === "string") text = content;
1111
+ else if (Array.isArray(content)) text = content.map((block) => typeof block === "string" ? block : block.text ?? "").join("");
1112
+ return text.trim().replace(/^["']|["']$/g, "");
1113
+ };
1114
+ try {
1115
+ let response;
1116
+ try {
1117
+ response = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(labelRuntimeScope, () => invokeLabel(invokeConfig));
1118
+ } catch (error) {
1119
+ /** Retry ONLY recognized callback/tracer failures (the EventStream
1120
+ * tracer class of errors the stripped-callbacks fallback exists
1121
+ * for). Aborts and provider failures rethrow — retrying those
1122
+ * doubles traffic/cost and can restart cancelled requests. */
1123
+ const aborted = labelChainOptions.signal?.aborted === true || error?.name === "AbortError";
1124
+ const callbackFailure = /callback|tracer|event.?stream/i.test(String(error?.stack ?? error?.message ?? ""));
1125
+ if (aborted || !callbackFailure) throw error;
1126
+ const langfuseHandler = require_callbacks.findCallback(invokeConfig.callbacks, require_langfuse.isLangfuseCallbackHandler);
1127
+ const { callbacks: _cb, ...rest } = invokeConfig;
1128
+ const safeConfig = Object.assign({}, rest, { callbacks: langfuseHandler ? [langfuseHandler] : [] });
1129
+ response = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(labelRuntimeScope, () => invokeLabel(safeConfig));
1130
+ }
1131
+ const label = extractLabel(response);
1132
+ return label.length > 0 ? { label } : {};
1133
+ } finally {
1134
+ await require_langfuse.disposeLangfuseHandler(labelLangfuseHandler);
1135
+ }
1136
+ }
963
1137
  };
964
1138
  function findLastMessageOfType(messages, type) {
965
1139
  for (let i = messages.length - 1; i >= 0; i--) if (messages[i].getType() === type) return messages[i];