@librechat/agents 3.4.3 → 3.4.5

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 (99) hide show
  1. package/dist/cjs/graphs/Graph.cjs +27 -14
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  4. package/dist/cjs/hitl/askUserQuestions.cjs +66 -0
  5. package/dist/cjs/hitl/askUserQuestions.cjs.map +1 -0
  6. package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs +46 -0
  7. package/dist/cjs/hitl/askUserQuestionsInterrupt.cjs.map +1 -0
  8. package/dist/cjs/hitl/index.cjs +2 -0
  9. package/dist/cjs/instrumentation.cjs +3 -3
  10. package/dist/cjs/langfuse.cjs +3 -3
  11. package/dist/cjs/langfuseRuntimeScope.cjs +1 -1
  12. package/dist/cjs/langfuseToolOutputTracing.cjs +2 -2
  13. package/dist/cjs/main.cjs +11 -1
  14. package/dist/cjs/messages/assistantPhase.cjs +59 -0
  15. package/dist/cjs/messages/assistantPhase.cjs.map +1 -0
  16. package/dist/cjs/messages/index.cjs +1 -0
  17. package/dist/cjs/prompts/activityLabel.cjs +76 -0
  18. package/dist/cjs/prompts/activityLabel.cjs.map +1 -1
  19. package/dist/cjs/run.cjs +200 -10
  20. package/dist/cjs/run.cjs.map +1 -1
  21. package/dist/cjs/session/AgentSession.cjs +1 -1
  22. package/dist/cjs/stream.cjs +45 -8
  23. package/dist/cjs/stream.cjs.map +1 -1
  24. package/dist/cjs/tools/ToolNode.cjs +3 -3
  25. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +81 -6
  26. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  27. package/dist/cjs/types/hitl.cjs +13 -0
  28. package/dist/cjs/types/hitl.cjs.map +1 -0
  29. package/dist/cjs/utils/callbacks.cjs +8 -0
  30. package/dist/cjs/utils/callbacks.cjs.map +1 -1
  31. package/dist/esm/graphs/Graph.mjs +27 -14
  32. package/dist/esm/graphs/Graph.mjs.map +1 -1
  33. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  34. package/dist/esm/hitl/askUserQuestions.mjs +66 -0
  35. package/dist/esm/hitl/askUserQuestions.mjs.map +1 -0
  36. package/dist/esm/hitl/askUserQuestionsInterrupt.mjs +43 -0
  37. package/dist/esm/hitl/askUserQuestionsInterrupt.mjs.map +1 -0
  38. package/dist/esm/hitl/index.mjs +2 -0
  39. package/dist/esm/instrumentation.mjs +3 -3
  40. package/dist/esm/langfuse.mjs +3 -3
  41. package/dist/esm/langfuseRuntimeScope.mjs +1 -1
  42. package/dist/esm/langfuseToolOutputTracing.mjs +2 -2
  43. package/dist/esm/main.mjs +5 -2
  44. package/dist/esm/messages/assistantPhase.mjs +57 -0
  45. package/dist/esm/messages/assistantPhase.mjs.map +1 -0
  46. package/dist/esm/messages/index.mjs +1 -0
  47. package/dist/esm/prompts/activityLabel.mjs +74 -1
  48. package/dist/esm/prompts/activityLabel.mjs.map +1 -1
  49. package/dist/esm/run.mjs +202 -12
  50. package/dist/esm/run.mjs.map +1 -1
  51. package/dist/esm/session/AgentSession.mjs +1 -1
  52. package/dist/esm/stream.mjs +45 -8
  53. package/dist/esm/stream.mjs.map +1 -1
  54. package/dist/esm/tools/ToolNode.mjs +3 -3
  55. package/dist/esm/tools/subagent/SubagentExecutor.mjs +81 -6
  56. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  57. package/dist/esm/types/hitl.mjs +13 -0
  58. package/dist/esm/types/hitl.mjs.map +1 -0
  59. package/dist/esm/utils/callbacks.mjs +8 -1
  60. package/dist/esm/utils/callbacks.mjs.map +1 -1
  61. package/dist/types/hitl/askUserQuestions.d.ts +24 -0
  62. package/dist/types/hitl/askUserQuestionsInterrupt.d.ts +11 -0
  63. package/dist/types/hitl/index.d.ts +2 -0
  64. package/dist/types/messages/assistantPhase.d.ts +22 -0
  65. package/dist/types/messages/index.d.ts +1 -0
  66. package/dist/types/prompts/activityLabel.d.ts +21 -1
  67. package/dist/types/run.d.ts +15 -2
  68. package/dist/types/types/activityLabel.d.ts +63 -0
  69. package/dist/types/types/assistantPhase.d.ts +6 -0
  70. package/dist/types/types/graph.d.ts +8 -1
  71. package/dist/types/types/hitl.d.ts +31 -2
  72. package/dist/types/types/index.d.ts +1 -0
  73. package/dist/types/types/stream.d.ts +11 -0
  74. package/dist/types/utils/callbacks.d.ts +1 -0
  75. package/package.json +2 -1
  76. package/src/graphs/Graph.ts +33 -9
  77. package/src/graphs/__tests__/Graph.reasoning.test.ts +57 -0
  78. package/src/hitl/askUserQuestions.ts +126 -0
  79. package/src/hitl/askUserQuestionsInterrupt.ts +115 -0
  80. package/src/hitl/index.ts +6 -0
  81. package/src/messages/assistantPhase.test.ts +75 -0
  82. package/src/messages/assistantPhase.ts +91 -0
  83. package/src/messages/index.ts +1 -0
  84. package/src/prompts/activityLabel.ts +177 -1
  85. package/src/run.ts +403 -21
  86. package/src/specs/activity-label-prompt.test.ts +123 -1
  87. package/src/specs/activity-phase-label.test.ts +306 -0
  88. package/src/specs/ask-user-questions.live.test.ts +185 -0
  89. package/src/specs/ask-user-questions.test.ts +293 -0
  90. package/src/stream.ts +69 -12
  91. package/src/tools/__tests__/SubagentExecutor.test.ts +436 -0
  92. package/src/tools/subagent/SubagentExecutor.ts +160 -8
  93. package/src/types/activityLabel.ts +65 -0
  94. package/src/types/assistantPhase.ts +6 -0
  95. package/src/types/graph.ts +8 -0
  96. package/src/types/hitl.ts +36 -2
  97. package/src/types/index.ts +1 -0
  98. package/src/types/stream.ts +9 -0
  99. package/src/utils/callbacks.ts +21 -0
@@ -4,8 +4,8 @@ import { HARD_MAX_TOOL_RESULT_CHARS } from "../utils/truncation.mjs";
4
4
  import { serializeToolContentBounded } from "../utils/toolContent.mjs";
5
5
  import { StandardGraph } from "./Graph.mjs";
6
6
  import { PromptTemplate } from "@langchain/core/prompts";
7
- import { AIMessage, HumanMessage, ToolMessage, getBufferString } from "@langchain/core/messages";
8
7
  import { Annotation, Command, END, START, StateGraph, messagesStateReducer } from "@langchain/langgraph";
8
+ import { AIMessage, HumanMessage, ToolMessage, getBufferString } from "@langchain/core/messages";
9
9
  import { tool } from "@langchain/core/tools";
10
10
  //#region src/graphs/MultiAgentGraph.ts
11
11
  /** Pattern to extract instructions from transfer ToolMessage content */
@@ -0,0 +1,66 @@
1
+ import { ASK_USER_QUESTION_ID_PATTERN, isAskUserQuestionRequest } from "./askUserQuestionsInterrupt.mjs";
2
+ import { interrupt } from "@langchain/langgraph";
3
+ //#region src/hitl/askUserQuestions.ts
4
+ function validateQuestions(questions) {
5
+ if (questions.length === 0) throw new RangeError("askUserQuestions requires at least one question.");
6
+ if (questions.length > 4) throw new RangeError(`askUserQuestions accepts at most 4 questions.`);
7
+ const ids = /* @__PURE__ */ new Set();
8
+ for (const question of questions) {
9
+ if (!isAskUserQuestionRequest(question)) throw new TypeError("askUserQuestions requires each question and option to have valid string fields.");
10
+ if (!ASK_USER_QUESTION_ID_PATTERN.test(question.id)) throw new Error("askUserQuestions requires each question id to match [A-Za-z][A-Za-z0-9_-]{0,63}.");
11
+ if (ids.has(question.id)) throw new Error(`askUserQuestions requires unique question ids; received "${question.id}" more than once.`);
12
+ ids.add(question.id);
13
+ }
14
+ return questions[0];
15
+ }
16
+ function validateResolution(value, questions) {
17
+ if (typeof value !== "object" || value === null) throw new TypeError("askUserQuestions requires an answers object.");
18
+ const answers = value.answers;
19
+ if (typeof answers !== "object" || answers === null || Array.isArray(answers)) throw new TypeError("askUserQuestions requires an answers object.");
20
+ const validated = {};
21
+ for (const question of questions) {
22
+ const descriptor = Object.getOwnPropertyDescriptor(answers, question.id);
23
+ const answer = descriptor?.value;
24
+ if (descriptor == null || typeof answer !== "string") throw new TypeError(`askUserQuestions requires a string answer for question id "${question.id}".`);
25
+ validated[question.id] = answer;
26
+ }
27
+ return { answers: validated };
28
+ }
29
+ /**
30
+ * Suspend once to collect answers to several related questions. The first
31
+ * question is also included in the legacy `question` field so existing hosts
32
+ * can render a useful fallback during a staged rollout.
33
+ *
34
+ * Question ids must be non-empty and unique within the batch. The helper
35
+ * accepts at most four questions so hosts can render the interaction as one
36
+ * focused decision surface rather than an unbounded form.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const { answers } = askUserQuestions({
41
+ * questions: [
42
+ * { id: 'environment', question: 'Which environment?' },
43
+ * { id: 'region', question: 'Which region?' },
44
+ * ],
45
+ * });
46
+ * return `Deploy to ${answers.environment} in ${answers.region}`;
47
+ * ```
48
+ */
49
+ function askUserQuestions(request, options) {
50
+ const first = validateQuestions(request.questions);
51
+ return validateResolution(interrupt({
52
+ type: "ask_user_question",
53
+ question: {
54
+ question: first.question,
55
+ ...first.description != null && { description: first.description },
56
+ ...first.options != null && { options: first.options },
57
+ ...first.multiSelect != null && { multiSelect: first.multiSelect }
58
+ },
59
+ questions: request.questions,
60
+ ...options?.toolCallId != null && options.toolCallId !== "" && { tool_call_id: options.toolCallId }
61
+ }), request.questions);
62
+ }
63
+ //#endregion
64
+ export { askUserQuestions };
65
+
66
+ //# sourceMappingURL=askUserQuestions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askUserQuestions.mjs","names":[],"sources":["../../../src/hitl/askUserQuestions.ts"],"sourcesContent":["import { interrupt } from '@langchain/langgraph';\nimport type {\n AskUserQuestionBatchItem,\n AskUserQuestionRequest,\n AskUserQuestionsInterruptPayload,\n AskUserQuestionsRequest,\n AskUserQuestionsResolution,\n} from '@/types/hitl';\nimport {\n ASK_USER_QUESTION_ID_PATTERN,\n isAskUserQuestionRequest,\n MAX_ASK_USER_QUESTIONS,\n} from './askUserQuestionsInterrupt';\n\nfunction validateQuestions(\n questions: readonly AskUserQuestionBatchItem[]\n): AskUserQuestionBatchItem {\n if (questions.length === 0) {\n throw new RangeError('askUserQuestions requires at least one question.');\n }\n if (questions.length > MAX_ASK_USER_QUESTIONS) {\n throw new RangeError(\n `askUserQuestions accepts at most ${MAX_ASK_USER_QUESTIONS} questions.`\n );\n }\n\n const ids = new Set<string>();\n for (const question of questions) {\n if (!isAskUserQuestionRequest(question)) {\n throw new TypeError(\n 'askUserQuestions requires each question and option to have valid string fields.'\n );\n }\n if (!ASK_USER_QUESTION_ID_PATTERN.test(question.id)) {\n throw new Error(\n 'askUserQuestions requires each question id to match [A-Za-z][A-Za-z0-9_-]{0,63}.'\n );\n }\n if (ids.has(question.id)) {\n throw new Error(\n `askUserQuestions requires unique question ids; received \"${question.id}\" more than once.`\n );\n }\n ids.add(question.id);\n }\n return questions[0];\n}\n\ninterface AskUserQuestionsResolutionCandidate {\n answers?: unknown;\n}\n\nfunction validateResolution(\n value: unknown,\n questions: readonly AskUserQuestionBatchItem[]\n): AskUserQuestionsResolution {\n if (typeof value !== 'object' || value === null) {\n throw new TypeError('askUserQuestions requires an answers object.');\n }\n const answers = (value as AskUserQuestionsResolutionCandidate).answers;\n if (\n typeof answers !== 'object' ||\n answers === null ||\n Array.isArray(answers)\n ) {\n throw new TypeError('askUserQuestions requires an answers object.');\n }\n\n const validated: Record<string, string> = {};\n for (const question of questions) {\n const descriptor = Object.getOwnPropertyDescriptor(answers, question.id);\n const answer: unknown = descriptor?.value;\n if (descriptor == null || typeof answer !== 'string') {\n throw new TypeError(\n `askUserQuestions requires a string answer for question id \"${question.id}\".`\n );\n }\n validated[question.id] = answer;\n }\n return { answers: validated };\n}\n\n/**\n * Suspend once to collect answers to several related questions. The first\n * question is also included in the legacy `question` field so existing hosts\n * can render a useful fallback during a staged rollout.\n *\n * Question ids must be non-empty and unique within the batch. The helper\n * accepts at most four questions so hosts can render the interaction as one\n * focused decision surface rather than an unbounded form.\n *\n * @example\n * ```ts\n * const { answers } = askUserQuestions({\n * questions: [\n * { id: 'environment', question: 'Which environment?' },\n * { id: 'region', question: 'Which region?' },\n * ],\n * });\n * return `Deploy to ${answers.environment} in ${answers.region}`;\n * ```\n */\nexport function askUserQuestions(\n request: AskUserQuestionsRequest,\n options?: { toolCallId?: string }\n): AskUserQuestionsResolution {\n const first = validateQuestions(request.questions);\n const fallback: AskUserQuestionRequest = {\n question: first.question,\n ...(first.description != null && { description: first.description }),\n ...(first.options != null && { options: first.options }),\n ...(first.multiSelect != null && { multiSelect: first.multiSelect }),\n };\n const payload: AskUserQuestionsInterruptPayload = {\n type: 'ask_user_question',\n question: fallback,\n questions: request.questions,\n ...(options?.toolCallId != null &&\n options.toolCallId !== '' && { tool_call_id: options.toolCallId }),\n };\n\n const resolution = interrupt<AskUserQuestionsInterruptPayload, unknown>(\n payload\n );\n return validateResolution(resolution, request.questions);\n}\n"],"mappings":";;;AAcA,SAAS,kBACP,WAC0B;CAC1B,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,WAAW,kDAAkD;CAEzE,IAAI,UAAU,SAAA,GACZ,MAAM,IAAI,WACR,+CACF;CAGF,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,YAAY,WAAW;EAChC,IAAI,CAAC,yBAAyB,QAAQ,GACpC,MAAM,IAAI,UACR,iFACF;EAEF,IAAI,CAAC,6BAA6B,KAAK,SAAS,EAAE,GAChD,MAAM,IAAI,MACR,kFACF;EAEF,IAAI,IAAI,IAAI,SAAS,EAAE,GACrB,MAAM,IAAI,MACR,4DAA4D,SAAS,GAAG,kBAC1E;EAEF,IAAI,IAAI,SAAS,EAAE;CACrB;CACA,OAAO,UAAU;AACnB;AAMA,SAAS,mBACP,OACA,WAC4B;CAC5B,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,MAAM,IAAI,UAAU,8CAA8C;CAEpE,MAAM,UAAW,MAA8C;CAC/D,IACE,OAAO,YAAY,YACnB,YAAY,QACZ,MAAM,QAAQ,OAAO,GAErB,MAAM,IAAI,UAAU,8CAA8C;CAGpE,MAAM,YAAoC,CAAC;CAC3C,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,aAAa,OAAO,yBAAyB,SAAS,SAAS,EAAE;EACvE,MAAM,SAAkB,YAAY;EACpC,IAAI,cAAc,QAAQ,OAAO,WAAW,UAC1C,MAAM,IAAI,UACR,8DAA8D,SAAS,GAAG,GAC5E;EAEF,UAAU,SAAS,MAAM;CAC3B;CACA,OAAO,EAAE,SAAS,UAAU;AAC9B;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,iBACd,SACA,SAC4B;CAC5B,MAAM,QAAQ,kBAAkB,QAAQ,SAAS;CAkBjD,OAAO,mBAHY,UACjB;EARA,MAAM;EACN,UAAU;GAPV,UAAU,MAAM;GAChB,GAAI,MAAM,eAAe,QAAQ,EAAE,aAAa,MAAM,YAAY;GAClE,GAAI,MAAM,WAAW,QAAQ,EAAE,SAAS,MAAM,QAAQ;GACtD,GAAI,MAAM,eAAe,QAAQ,EAAE,aAAa,MAAM,YAAY;EAIjD;EACjB,WAAW,QAAQ;EACnB,GAAI,SAAS,cAAc,QACzB,QAAQ,eAAe,MAAM,EAAE,cAAc,QAAQ,WAAW;CAI5D,CAE2B,GAAG,QAAQ,SAAS;AACzD"}
@@ -0,0 +1,43 @@
1
+ import { isAskUserQuestionInterrupt } from "../types/hitl.mjs";
2
+ //#region src/hitl/askUserQuestionsInterrupt.ts
3
+ /** Maximum questions supported by one batched clarification interaction. */
4
+ const MAX_ASK_USER_QUESTIONS = 4;
5
+ /** Safe identifier format for answer-map keys in a batched question. */
6
+ const ASK_USER_QUESTION_ID_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
7
+ function isAskUserQuestionOption(value) {
8
+ if (typeof value !== "object" || value === null) return false;
9
+ const option = value;
10
+ return typeof option.label === "string" && typeof option.value === "string";
11
+ }
12
+ function isAskUserQuestionOptions(value) {
13
+ if (!Array.isArray(value)) return false;
14
+ for (let index = 0; index < value.length; index++) if (!Object.hasOwn(value, index) || !isAskUserQuestionOption(value[index])) return false;
15
+ return true;
16
+ }
17
+ function isAskUserQuestionRequest(value) {
18
+ if (typeof value !== "object" || value === null) return false;
19
+ const question = value;
20
+ return typeof question.question === "string" && (question.description === void 0 || typeof question.description === "string") && (question.options === void 0 || isAskUserQuestionOptions(question.options)) && (question.multiSelect === void 0 || typeof question.multiSelect === "boolean");
21
+ }
22
+ function isAskUserQuestionBatchItem(value) {
23
+ if (!isAskUserQuestionRequest(value)) return false;
24
+ const question = value;
25
+ return typeof question.id === "string" && ASK_USER_QUESTION_ID_PATTERN.test(question.id) && (question.header === void 0 || typeof question.header === "string");
26
+ }
27
+ /**
28
+ * Type guard for the batched form of an `ask_user_question` interrupt. Hosts
29
+ * use this to select the multi-question UI and `AskUserQuestionsResolution`.
30
+ */
31
+ function isAskUserQuestionsInterrupt(payload) {
32
+ if (!isAskUserQuestionInterrupt(payload) || !isAskUserQuestionRequest(payload.question) || payload.tool_call_id !== void 0 && typeof payload.tool_call_id !== "string" || !Array.isArray(payload.questions) || payload.questions.length === 0 || payload.questions.length > 4) return false;
33
+ const ids = /* @__PURE__ */ new Set();
34
+ for (const question of payload.questions) {
35
+ if (!isAskUserQuestionBatchItem(question) || ids.has(question.id)) return false;
36
+ ids.add(question.id);
37
+ }
38
+ return true;
39
+ }
40
+ //#endregion
41
+ export { ASK_USER_QUESTION_ID_PATTERN, MAX_ASK_USER_QUESTIONS, isAskUserQuestionRequest, isAskUserQuestionsInterrupt };
42
+
43
+ //# sourceMappingURL=askUserQuestionsInterrupt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askUserQuestionsInterrupt.mjs","names":[],"sources":["../../../src/hitl/askUserQuestionsInterrupt.ts"],"sourcesContent":["import type {\n AskUserQuestionBatchItem,\n AskUserQuestionOption,\n AskUserQuestionRequest,\n AskUserQuestionsInterruptPayload,\n} from '@/types/hitl';\nimport { isAskUserQuestionInterrupt } from '@/types/hitl';\n\n/** Maximum questions supported by one batched clarification interaction. */\nexport const MAX_ASK_USER_QUESTIONS = 4;\n\n/** Safe identifier format for answer-map keys in a batched question. */\nexport const ASK_USER_QUESTION_ID_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;\n\ninterface AskUserQuestionOptionCandidate {\n label?: unknown;\n value?: unknown;\n}\n\ninterface AskUserQuestionCandidate {\n question?: unknown;\n description?: unknown;\n options?: unknown;\n multiSelect?: unknown;\n}\n\ninterface AskUserQuestionBatchItemCandidate extends AskUserQuestionCandidate {\n id?: unknown;\n header?: unknown;\n}\n\nfunction isAskUserQuestionOption(\n value: unknown\n): value is AskUserQuestionOption {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const option = value as AskUserQuestionOptionCandidate;\n return typeof option.label === 'string' && typeof option.value === 'string';\n}\n\nfunction isAskUserQuestionOptions(\n value: unknown\n): value is AskUserQuestionOption[] {\n if (!Array.isArray(value)) {\n return false;\n }\n for (let index = 0; index < value.length; index++) {\n if (!Object.hasOwn(value, index) || !isAskUserQuestionOption(value[index])) {\n return false;\n }\n }\n return true;\n}\n\nexport function isAskUserQuestionRequest(\n value: unknown\n): value is AskUserQuestionRequest {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const question = value as AskUserQuestionCandidate;\n return (\n typeof question.question === 'string' &&\n (question.description === undefined ||\n typeof question.description === 'string') &&\n (question.options === undefined ||\n isAskUserQuestionOptions(question.options)) &&\n (question.multiSelect === undefined ||\n typeof question.multiSelect === 'boolean')\n );\n}\n\nfunction isAskUserQuestionBatchItem(\n value: unknown\n): value is AskUserQuestionBatchItem {\n if (!isAskUserQuestionRequest(value)) {\n return false;\n }\n const question = value as AskUserQuestionBatchItemCandidate;\n return (\n typeof question.id === 'string' &&\n ASK_USER_QUESTION_ID_PATTERN.test(question.id) &&\n (question.header === undefined || typeof question.header === 'string')\n );\n}\n\n/**\n * Type guard for the batched form of an `ask_user_question` interrupt. Hosts\n * use this to select the multi-question UI and `AskUserQuestionsResolution`.\n */\nexport function isAskUserQuestionsInterrupt(\n payload: unknown\n): payload is AskUserQuestionsInterruptPayload {\n if (\n !isAskUserQuestionInterrupt(payload) ||\n !isAskUserQuestionRequest(payload.question) ||\n (payload.tool_call_id !== undefined &&\n typeof payload.tool_call_id !== 'string') ||\n !Array.isArray(payload.questions) ||\n payload.questions.length === 0 ||\n payload.questions.length > MAX_ASK_USER_QUESTIONS\n ) {\n return false;\n }\n\n const ids = new Set<string>();\n for (const question of payload.questions) {\n if (!isAskUserQuestionBatchItem(question) || ids.has(question.id)) {\n return false;\n }\n ids.add(question.id);\n }\n return true;\n}\n"],"mappings":";;;AASA,MAAa,yBAAyB;;AAGtC,MAAa,+BAA+B;AAmB5C,SAAS,wBACP,OACgC;CAChC,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAET,MAAM,SAAS;CACf,OAAO,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,UAAU;AACrE;AAEA,SAAS,yBACP,OACkC;CAClC,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,OAAO;CAET,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SACxC,IAAI,CAAC,OAAO,OAAO,OAAO,KAAK,KAAK,CAAC,wBAAwB,MAAM,MAAM,GACvE,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,yBACd,OACiC;CACjC,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAET,MAAM,WAAW;CACjB,OACE,OAAO,SAAS,aAAa,aAC5B,SAAS,gBAAgB,KAAA,KACxB,OAAO,SAAS,gBAAgB,cACjC,SAAS,YAAY,KAAA,KACpB,yBAAyB,SAAS,OAAO,OAC1C,SAAS,gBAAgB,KAAA,KACxB,OAAO,SAAS,gBAAgB;AAEtC;AAEA,SAAS,2BACP,OACmC;CACnC,IAAI,CAAC,yBAAyB,KAAK,GACjC,OAAO;CAET,MAAM,WAAW;CACjB,OACE,OAAO,SAAS,OAAO,YACvB,6BAA6B,KAAK,SAAS,EAAE,MAC5C,SAAS,WAAW,KAAA,KAAa,OAAO,SAAS,WAAW;AAEjE;;;;;AAMA,SAAgB,4BACd,SAC6C;CAC7C,IACE,CAAC,2BAA2B,OAAO,KACnC,CAAC,yBAAyB,QAAQ,QAAQ,KACzC,QAAQ,iBAAiB,KAAA,KACxB,OAAO,QAAQ,iBAAiB,YAClC,CAAC,MAAM,QAAQ,QAAQ,SAAS,KAChC,QAAQ,UAAU,WAAW,KAC7B,QAAQ,UAAU,SAAA,GAElB,OAAO;CAGT,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,YAAY,QAAQ,WAAW;EACxC,IAAI,CAAC,2BAA2B,QAAQ,KAAK,IAAI,IAAI,SAAS,EAAE,GAC9D,OAAO;EAET,IAAI,IAAI,SAAS,EAAE;CACrB;CACA,OAAO;AACT"}
@@ -1,2 +1,4 @@
1
1
  import "./askUserQuestion.mjs";
2
+ import "./askUserQuestionsInterrupt.mjs";
3
+ import "./askUserQuestions.mjs";
2
4
  export {};
@@ -1,11 +1,11 @@
1
- import { traceIdFromSeed } from "./langfuseRuntimeContext.mjs";
2
1
  import { isPresent } from "./utils/misc.mjs";
2
+ import { traceIdFromSeed } from "./langfuseRuntimeContext.mjs";
3
3
  import { resolveLangfuseConfigForSpan, resolveTraceIdSeedForSpan } from "./langfuseRuntimeScope.mjs";
4
+ import { createLangfuseSpanProcessor } from "./langfuseToolOutputTracing.mjs";
4
5
  import { getLangfuseDestinationKey, getLangfuseSpanProcessorParams, registerLangfuseManagedSpan } from "./langfuseSpanRegistry.mjs";
5
6
  import { createLibreChatTraceAttributes } from "./langfuse.mjs";
6
- import { createLangfuseSpanProcessor } from "./langfuseToolOutputTracing.mjs";
7
- import { ROOT_CONTEXT, context, createContextKey } from "@opentelemetry/api";
8
7
  import { setLangfuseTracerProvider } from "@langfuse/tracing";
8
+ import { ROOT_CONTEXT, context, createContextKey } from "@opentelemetry/api";
9
9
  import { randomBytes } from "node:crypto";
10
10
  import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
11
11
  import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
@@ -2,12 +2,12 @@ import { isPresent, parseBooleanEnv } from "./utils/misc.mjs";
2
2
  import { hasLangfuseConfigCredentials, hasLangfuseEnvConfig, hasLangfuseEnvCredentials, resolveToolOutputTracingConfig } from "./langfuseConfig.mjs";
3
3
  import { resolveLangfuseConfigForSpan, resolveLangfuseScopeAgentId, resolveLangfuseScopeRunId, resolveTraceIdSeedForSpan, withLangfuseRuntimeScope } from "./langfuseRuntimeScope.mjs";
4
4
  import { getLangfuseManagedSpanDestination, resolveLangfuseDestinationKey } from "./langfuseSpanRegistry.mjs";
5
- import { AIMessage, AIMessageChunk } from "@langchain/core/messages";
6
5
  import { isGraphInterrupt, isParentCommand } from "@langchain/langgraph";
6
+ import { AIMessage, AIMessageChunk } from "@langchain/core/messages";
7
+ import { getLangfuseTracerProvider, propagateAttributes } from "@langfuse/tracing";
8
+ import { context, trace } from "@opentelemetry/api";
7
9
  import { CallbackHandler } from "@langfuse/langchain";
8
10
  import { LangfuseOtelContextKeys } from "@langfuse/core";
9
- import { context, trace } from "@opentelemetry/api";
10
- import { getLangfuseTracerProvider, propagateAttributes } from "@langfuse/tracing";
11
11
  //#region src/langfuse.ts
12
12
  const TRACE_METADATA_MAX_LENGTH = 200;
13
13
  const LANGFUSE_FORCE_FLUSH_ON_DISPOSE = "LANGFUSE_FORCE_FLUSH_ON_DISPOSE";
@@ -1,5 +1,5 @@
1
- import { getLangfuseRuntimeConfig, getLangfuseRuntimeToolOutputTracingConfig, getLangfuseScopeAgentId, getLangfuseScopeRunId, getTraceIdSeed, hasLangfuseRuntimeContextValue, replaceLangfuseRuntimeContext, runWithLangfuseRuntimeContext } from "./langfuseRuntimeContext.mjs";
2
1
  import { hasToolOutputTracingConfig, resolveLangfuseConfig, resolveToolOutputTracingConfig } from "./langfuseConfig.mjs";
2
+ import { getLangfuseRuntimeConfig, getLangfuseRuntimeToolOutputTracingConfig, getLangfuseScopeAgentId, getLangfuseScopeRunId, getTraceIdSeed, hasLangfuseRuntimeContextValue, replaceLangfuseRuntimeContext, runWithLangfuseRuntimeContext } from "./langfuseRuntimeContext.mjs";
3
3
  import { context, createContextKey } from "@opentelemetry/api";
4
4
  //#region src/langfuseRuntimeScope.ts
5
5
  const langfuseToolOutputTracingConfigKey = createContextKey("librechat.langfuse.tool-output-tracing");
@@ -1,8 +1,8 @@
1
1
  import { hasToolOutputTracingConfig, normalizeToolName, resolveLangfuseConfig, resolveToolOutputTracingConfig } from "./langfuseConfig.mjs";
2
- import { resolveToolOutputTracingConfigForSpan } from "./langfuseRuntimeScope.mjs";
3
2
  import { shapeLangfuseSpan, shouldDropLangfuseSpan } from "./langfuseTraceShaping.mjs";
4
- import { LangfuseOtelSpanAttributes } from "@langfuse/tracing";
3
+ import { resolveToolOutputTracingConfigForSpan } from "./langfuseRuntimeScope.mjs";
5
4
  import { LangfuseSpanProcessor } from "@langfuse/otel";
5
+ import { LangfuseOtelSpanAttributes } from "@langfuse/tracing";
6
6
  //#region src/langfuseToolOutputTracing.ts
7
7
  const LANGGRAPH_TOOL_NODE_PREFIX = "tools=";
8
8
  const SERVER_TOOL_RESULT_PREFIX = "{\"serverToolResult\":";
package/dist/esm/main.mjs CHANGED
@@ -20,12 +20,13 @@ import { coalesceAdjacentUserTurns, strictAlternationProviders } from "./message
20
20
  import { PREDECESSOR_HANDOFF_CUE, appendPredecessorHandoffCue, removePredecessorHandoffCue } from "./messages/handoffCue.mjs";
21
21
  import { REMOVE_ALL_MESSAGES, createRemoveAllMessage, messagesStateReducer } from "./messages/reducer.mjs";
22
22
  import { DEFAULT_RETAIN_RECENT_TURNS, splitAtRecencyBoundary } from "./messages/recency.mjs";
23
+ import { getAssistantTextPhase, getMessageCreationContentMetadata, splitAssistantTextContentByPhase } from "./messages/assistantPhase.mjs";
23
24
  import "./messages/index.mjs";
24
25
  import { joinKeys, resetIfNotEmpty } from "./utils/graph.mjs";
25
26
  import { isAnthropicLike, isGoogleLike, isOpenAILike } from "./utils/llm.mjs";
26
27
  import { resolveFetchProxyAgent, shouldBypassProxy } from "./utils/proxy.mjs";
27
- import { handleServerToolResult, handleToolCallChunks, handleToolCalls, toolResultTypes } from "./tools/handlers.mjs";
28
28
  import { DEFAULT_MAX_TOOL_CALL_ARG_BYTES, StreamLimitExceededError, resolveStreamLimits } from "./llm/streamLimits.mjs";
29
+ import { handleServerToolResult, handleToolCallChunks, handleToolCalls, toolResultTypes } from "./tools/handlers.mjs";
29
30
  import { INTENT_ARG, INTENT_DESCRIPTION, INTENT_LABEL_MARKER, INTENT_PROPERTY, applyOutcome, isIntentLabelProperty, outcomeFieldsFromResult, readIntent, readOutcomeFields, resolveToolOutcome, stripIntent, withIntent, withoutIntent } from "./tools/intentArg.mjs";
30
31
  import { ChatModelStreamHandler, SDK_STREAM_DISPATCH, createContentAggregator, dispatchesChatModelStream, getChunkContent } from "./stream.mjs";
31
32
  import { HandlerRegistry, LLMStreamHandler, ModelEndHandler, TestChatStreamHandler, TestLLMStreamHandler, ToolEndHandler, composeEventHandlers, createMetadataAggregator } from "./events.mjs";
@@ -101,6 +102,8 @@ import { createRunHandlers } from "./session/handlers.mjs";
101
102
  import { AgentSession, createAgentSession } from "./session/AgentSession.mjs";
102
103
  import "./session/index.mjs";
103
104
  import { askUserQuestion } from "./hitl/askUserQuestion.mjs";
105
+ import { ASK_USER_QUESTION_ID_PATTERN, MAX_ASK_USER_QUESTIONS, isAskUserQuestionsInterrupt } from "./hitl/askUserQuestionsInterrupt.mjs";
106
+ import { askUserQuestions } from "./hitl/askUserQuestions.mjs";
104
107
  import "./hitl/index.mjs";
105
108
  import { AIMessage, AIMessageChunk, BaseMessage, BaseMessageChunk, HumanMessage, SystemMessage, ToolMessage, getBufferString, isAIMessage, isBaseMessage, isToolMessage } from "./langchain/messages.mjs";
106
109
  import { PromptTemplate } from "./langchain/prompts.mjs";
@@ -108,4 +111,4 @@ import { Runnable, RunnableLambda, RunnableSequence } from "./langchain/runnable
108
111
  import { DynamicStructuredTool, StructuredTool, Tool, tool } from "./langchain/tools.mjs";
109
112
  import "./langchain/index.mjs";
110
113
  import { BaseCheckpointSaver, Command, INTERRUPT, MemorySaver, interrupt, isInterrupted } from "@langchain/langgraph";
111
- export { AIMessage, AIMessageChunk, ANTHROPIC_TOOL_TOKEN_MULTIPLIER, AgentSession, BASH_SHELL_GUIDANCE, BaseCheckpointSaver, BaseMessage, BaseMessageChunk, BashExecutionToolDefinition, BashExecutionToolDescription, BashExecutionToolName, BashExecutionToolSchema, BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, BashToolOutputReferencesGuide, CALIBRATION_RATIO_MAX, CALIBRATION_RATIO_MIN, CLOUDFLARE_BASH_CODING_TOOL_NAMES, CLOUDFLARE_CODING_TOOL_NAMES, CODE_API_AUTHORIZATION_ERROR_MESSAGE, CODE_API_EXECUTION_FAILED_ERROR_MESSAGE, CODE_API_INVALID_REQUEST_ERROR_MESSAGE, CODE_API_RATE_LIMITED_ERROR_MESSAGE, CODE_API_UNAVAILABLE_ERROR_MESSAGE, CODE_ARTIFACT_PATH_GUIDANCE, CODE_EXECUTION_TOOLS, Calculator, CalculatorSchema, CalculatorToolDefinition, CalculatorToolDescription, CalculatorToolName, Callback, ChatModelStreamHandler, ChatOpenRouter, CloudflareBashExecutionToolDescription, CloudflareCodeExecutionToolDescription, CodeApiRequestError, CodeExecutionToolDefinition, CodeExecutionToolDescription, CodeExecutionToolName, CodeExecutionToolSchema, Command, CommonEvents, CompileCheckToolName, Constants, ContentTypes, CustomChatMistralAI, CustomOpenAIClient, DATE_RANGE, DEFAULT_CONTEXT_PRUNING_SETTINGS, DEFAULT_COUNTRY_DESCRIPTION, DEFAULT_HOOK_TIMEOUT_MS, DEFAULT_MAX_SEALS, DEFAULT_MAX_TOOL_CALL_ARG_BYTES, DEFAULT_PROMPT_CACHE_TTL, DEFAULT_QUERY_DESCRIPTION, DEFAULT_RECURSION_LIMIT, DEFAULT_RESERVE_RATIO, DEFAULT_RETAIN_RECENT_TURNS, DEFAULT_STREAM_DELAY, DEFAULT_SUBAGENT_DESCRIPTION, DEFAULT_TOOL_TOKEN_MULTIPLIER, DynamicStructuredTool, EnvVar, FAILED_EXECUTION_FILE_REMINDER, FakeChatModel, Graph, GraphEvents, GraphNodeActions, GraphNodeKeys, HARD_MAX_TOOL_RESULT_CHARS, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE, HOOK_EVENTS, HOOK_INJECTED_MESSAGES_CAPABLE, HOOK_PREEMPT_BOUNDARY_CAPABLE, HandlerRegistry, HookRegistry, HumanMessage, IMAGE_TOKEN_SAFETY_MARGIN, INTENT_ARG, INTENT_DESCRIPTION, INTENT_LABEL_MARKER, INTENT_PROPERTY, INTERRUPT, JsonlSessionStore, LLMStreamHandler, LOCAL_CODING_BUNDLE_NAMES, LOCAL_CODING_TOOL_NAMES, LOCAL_SPAWN_TIMEOUT_MS, LocalBashExecutionToolDescription, LocalCodeExecutionToolDescription, LocalEditFileToolName, LocalEditFileToolSchema, LocalFileCheckpointerImpl, LocalGlobSearchToolName, LocalGlobSearchToolSchema, LocalGrepSearchToolName, LocalGrepSearchToolSchema, LocalListDirectoryToolName, LocalListDirectoryToolSchema, LocalReadFileToolSchema, LocalWriteFileToolName, LocalWriteFileToolSchema, MAX_CACHE_SIZE, MAX_PATTERN_LENGTH, MemorySaver, ModelEndHandler, MultiAgentGraph, OPENAI_RESPONSES_REPLAY_POSITIONS_KEY, ORIGINAL_CONTENT_MAX_CHARS, PREDECESSOR_HANDOFF_CUE, PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS, ProgrammaticToolCallingDefinition, ProgrammaticToolCallingDescription, ProgrammaticToolCallingName, ProgrammaticToolCallingSchema, PromptTemplate, Providers, REMOVE_ALL_MESSAGES, REPLY_PRIMER_TOKENS, ReadFileToolDefinition, ReadFileToolDescription, ReadFileToolName, ReadFileToolSchema, Run, Runnable, RunnableCallable, RunnableLambda, RunnableSequence, SDK_STREAM_DISPATCH, STATEFUL_BASH_NOTE, STATEFUL_ENV_NOTE, SessionManager, SkillToolDefinition, SkillToolDescription, SkillToolName, SkillToolSchema, StandardGraph, StatefulBashExecutionToolDescription, StatefulCodeExecutionToolDescription, StepTypes, StreamLimitExceededError, StructuredTool, SubagentExecutor, SubagentToolDefinition, SubagentToolDescription, SubagentToolName, SubagentToolSchema, SystemMessage, TMP_SCRATCH_OUTPUT_REMINDER, TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY, TestChatStreamHandler, TestLLMStreamHandler, TitleMethod, TokenEncoderManager, Tool, ToolCallTypes, ToolEndHandler, ToolMessage, ToolNode, ToolSearchToolDefinition, ToolSearchToolDescription, ToolSearchToolName, ToolSearchToolSchema, UnsafeTokenMeasurementError, WebSearchToolDefinition, WebSearchToolDescription, WebSearchToolName, WebSearchToolSchema, _createBashProgramForTests, _resetLocalEngineWarningsForTests, _resetRipgrepCacheForTests, _resetSyntaxCheckProbeCacheForTests, _resetUnrecognizedTriggerWarnings, addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, appendCodeSessionFileSummary, appendFailedExecutionFileReminder, appendPredecessorHandoffCue, appendTmpScratchReminder, applyContextPruning, applyEdit, applyOutcome, applyPreToolUseHooksForBridge, apportionTokenCounts, askUserQuestion, attemptInvoke, bashAstFindingsToErrors, buildAnthropicCacheControl, buildBashExecutionToolDescription, buildBashExecutionToolSchema, buildBedrockCachePoint, buildChildInputs, buildCodeApiExecutionErrorMessage, buildCodeApiHttpErrorMessage, buildCodeExecutionToolDescription, buildCodeExecutionToolSchema, buildSandboxRuntimeConfig, buildSubagentToolParams, calculateMaxToolCallInputChars, calculateMaxToolResultChars, calculateMaxTotalToolOutputSize, calculateTotalTokens, canSealPreempt, checkValidNumber, clampCalibrationRatio, classifyAttachment, clientExecTimeoutMs, clientFsTimeoutMs, cloneMessage, coalesceAdjacentUserTurns, composeAbortSignals, composeEventHandlers, computeAdaptivePieceSize, convertInjectedMessages, convertMessagesToContent, countNestedGroups, countrySchema, createAgentSession, createBashExecutionTool, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, createCloudflareBashExecutionTool, createCloudflareBashProgrammaticToolCallingTool, createCloudflareBridgeRuntime, createCloudflareCodeExecutionTool, createCloudflareCodingToolBundle, createCloudflareCodingTools, createCloudflareExecutionTool, createCloudflareLocalExecutionConfig, createCloudflareProgrammaticToolCallingTool, createCloudflareWorkspaceFS, createCodeExecutionTool, createCompileCheckTool, createCompileCheckToolDefinition, createContentAggregator, createFakeStreamingLLM, createGraph, createHandlers, createLocalBashExecutionTool, createLocalBashProgrammaticToolCallingTool, createLocalCodeExecutionTool, createLocalCodingToolBundle, createLocalCodingToolDefinitions, createLocalCodingToolRegistry, createLocalCodingTools, createLocalEditFileTool, createLocalFileCheckpointer, createLocalGlobSearchTool, createLocalGrepSearchTool, createLocalListDirectoryTool, createLocalProgrammaticToolCallingTool, createLocalReadFileTool, createLocalWriteFileTool, createMetadataAggregator, createProgrammaticToolCallingSchema, createProgrammaticToolCallingTool, createPruneMessages, createRemoveAllMessage, createRunHandlers, createSchemaOnlyTool, createSchemaOnlyTools, createSearchTool, createSubagentToolDefinition, createTokenCounter, createToolErrorOwnership, createToolPolicyHook, createToolSearch, createWorkspacePolicyHook, dateSchema, decodeFile, defaultOmitOptions, deserializeMessage, dispatchesChatModelStream, emptyOutputMessage, encodeFile, encodingForModel, enforceOriginalContentCap, ensureThinkingBlockInMessages, escapeRegexSpecialChars, estimateAnthropicImageTokens, estimateDocumentBlockTokens, estimateImageBlockTokens, estimateOpenAIImageTokens, estimateTimedMediaBlockTokens, execWithClientTimeout, executeCloudflareBash, executeCloudflareCode, executeHooks, executeLocalBash, executeLocalBashWithArgs, executeLocalCode, executeParallelSearches, executeTools, extractErrorMessage, extractImageDimensions, extractMcpServerName, extractTextFromContent, extractToolDiscoveries, extractUsedBashToolNames, extractUsedToolNames, fetchSessionFiles, filterBashToolsByUsage, filterGraphSubagentResult, filterSubagentResult, filterToolsByUsage, findLastIndex, foldToolBlocksForToollessAgent, formatAgentMessages, formatAnthropicArtifactContent, formatAnthropicMessage, formatArtifactPayload, formatCloudflareOutput, formatCompletedResponse, formatContentStrings, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, formatServerListing, formatSkillCatalog, getAvailableMcpServers, getBaseToolName, getBufferString, getChatModelClass, getChunkContent, getCloudflareWorkspaceRoot, getCodeBaseURL, getContextOverflowInfo, getConverseOverrideMessage, getDeferredToolsListing, getLocalCwd, getLocalSessionId, getMaxOutputTokensKey, getMessageId, getMessagesWithinTokenLimit, getReadRoots, getSpawn, getTokenCountForMessage, getWorkspaceFS, getWorkspaceRoots, getWriteRoots, handleServerToolResult, handleToolCallChunks, handleToolCalls, hasNestedQuantifier, hasNestedQuantifiers, hasToolSearchInCurrentTurn, hasUnsafeStructuredSerialization, imageAttachmentContent, imagesSchema, initializeModel, interrupt, isAIMessage, isAnthropicLike, isBaseMessage, isContextOverflowError, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, isGoogleLike, isGraphSubagentConfig, isIntentLabelProperty, isInterrupted, isLegacyConvertible, isLikelyContextOverflowError, isOpenAILike, isPresent, isSyntheticProviderContextMessage, isThinkingEnabled, isToolMessage, isZodSchema, joinKeys, labelContentByAgent, locateEdit, makeIsDeferred, makeRequest, maskConsumedToolResults, matchesQuery, messagesStateReducer, modifyDeltaProperties, newsSchema, normalizeBashToolResultsForReplay, normalizeCodeApiRequestError, normalizeServerFilter, normalizeSubagentConfigEntries, normalizeSubagentConfigs, normalizeToBashIdentifier, normalizeToPythonIdentifier, outcomeFieldsFromResult, parseBooleanEnv, partitionAndMarkAnthropicToolCache, performLocalSearch, preFlightTruncateToolCallInputs, preFlightTruncateToolResults, projectAgentContextUsage, projectAnthropicArtifactContent, projectArtifactPayload, projectCacheControlledToolOutputsToText, projectComputerCallOutputsToText, projectOpenAIChatToolMessageContent, projectOpenAIResponsesToolMessageContent, projectOpenAIToolMessageContent, projectOpenRouterToolMessageContent, projectSingleTextToolOutputsToText, projectStructuredToolOutputsToText, projectToolCallInputs, projectToolStreamContentForProvider, querySchema, readIntent, readOutcomeFields, removePredecessorHandoffCue, repairOrphanedToolMessages, resetIfNotEmpty, resolveBedrockPromptCacheTtl, resolveCloudflareSandbox, resolveCodeApiAuthHeaders, resolveContextPruningSettings, resolveFetchProxyAgent, resolveLocalExecutionConfig, resolveLocalExecutionTools, resolveLocalToolRegistry, resolveLocalToolsForBinding, resolvePromptCacheTtl, resolveSearchOutcome, resolveStreamDelay, resolveStreamLimits, resolveSubagentConfigEntries, resolveSubagentConfigs, resolveToolOutcome, resolveWorkspacePath, resolveWorkspacePathSafe, runBashAstChecks, runPostEditSyntaxCheck, sanitizeOrphanToolBlocks, sanitizeRegex, serializeMessage, serializeToolCallInput, shellQuote, shiftIndexTokenCountMap, shouldBypassProxy, shouldTriggerSummarization, sleep, smoothStream, spawnLocalProcess, splitAtRecencyBoundary, strictAlternationProviders, stripAnthropicCacheControl, stripBedrockCacheControl, stripCodeSessionFileSummary, stripIntent, summarizeEvent, supportsBedrockToolCache, syncBudgetDerivedFields, toJsonSchema, tool, toolResultTypes, toolsCondition, truncateLocalOutput, truncateToolInput, truncateToolResultContent, tryFallbackProviders, unescapeObject, unwrapToolResponse, validateBashCommand, validateCloudflareBashCommand, videosSchema, withClientTimeout, withIntent, withMessageRole, withoutIntent };
114
+ export { AIMessage, AIMessageChunk, ANTHROPIC_TOOL_TOKEN_MULTIPLIER, ASK_USER_QUESTION_ID_PATTERN, AgentSession, BASH_SHELL_GUIDANCE, BaseCheckpointSaver, BaseMessage, BaseMessageChunk, BashExecutionToolDefinition, BashExecutionToolDescription, BashExecutionToolName, BashExecutionToolSchema, BashProgrammaticToolCallingDefinition, BashProgrammaticToolCallingDescription, BashProgrammaticToolCallingName, BashProgrammaticToolCallingSchema, BashToolOutputReferencesGuide, CALIBRATION_RATIO_MAX, CALIBRATION_RATIO_MIN, CLOUDFLARE_BASH_CODING_TOOL_NAMES, CLOUDFLARE_CODING_TOOL_NAMES, CODE_API_AUTHORIZATION_ERROR_MESSAGE, CODE_API_EXECUTION_FAILED_ERROR_MESSAGE, CODE_API_INVALID_REQUEST_ERROR_MESSAGE, CODE_API_RATE_LIMITED_ERROR_MESSAGE, CODE_API_UNAVAILABLE_ERROR_MESSAGE, CODE_ARTIFACT_PATH_GUIDANCE, CODE_EXECUTION_TOOLS, Calculator, CalculatorSchema, CalculatorToolDefinition, CalculatorToolDescription, CalculatorToolName, Callback, ChatModelStreamHandler, ChatOpenRouter, CloudflareBashExecutionToolDescription, CloudflareCodeExecutionToolDescription, CodeApiRequestError, CodeExecutionToolDefinition, CodeExecutionToolDescription, CodeExecutionToolName, CodeExecutionToolSchema, Command, CommonEvents, CompileCheckToolName, Constants, ContentTypes, CustomChatMistralAI, CustomOpenAIClient, DATE_RANGE, DEFAULT_CONTEXT_PRUNING_SETTINGS, DEFAULT_COUNTRY_DESCRIPTION, DEFAULT_HOOK_TIMEOUT_MS, DEFAULT_MAX_SEALS, DEFAULT_MAX_TOOL_CALL_ARG_BYTES, DEFAULT_PROMPT_CACHE_TTL, DEFAULT_QUERY_DESCRIPTION, DEFAULT_RECURSION_LIMIT, DEFAULT_RESERVE_RATIO, DEFAULT_RETAIN_RECENT_TURNS, DEFAULT_STREAM_DELAY, DEFAULT_SUBAGENT_DESCRIPTION, DEFAULT_TOOL_TOKEN_MULTIPLIER, DynamicStructuredTool, EnvVar, FAILED_EXECUTION_FILE_REMINDER, FakeChatModel, Graph, GraphEvents, GraphNodeActions, GraphNodeKeys, HARD_MAX_TOOL_RESULT_CHARS, HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE, HOOK_EVENTS, HOOK_INJECTED_MESSAGES_CAPABLE, HOOK_PREEMPT_BOUNDARY_CAPABLE, HandlerRegistry, HookRegistry, HumanMessage, IMAGE_TOKEN_SAFETY_MARGIN, INTENT_ARG, INTENT_DESCRIPTION, INTENT_LABEL_MARKER, INTENT_PROPERTY, INTERRUPT, JsonlSessionStore, LLMStreamHandler, LOCAL_CODING_BUNDLE_NAMES, LOCAL_CODING_TOOL_NAMES, LOCAL_SPAWN_TIMEOUT_MS, LocalBashExecutionToolDescription, LocalCodeExecutionToolDescription, LocalEditFileToolName, LocalEditFileToolSchema, LocalFileCheckpointerImpl, LocalGlobSearchToolName, LocalGlobSearchToolSchema, LocalGrepSearchToolName, LocalGrepSearchToolSchema, LocalListDirectoryToolName, LocalListDirectoryToolSchema, LocalReadFileToolSchema, LocalWriteFileToolName, LocalWriteFileToolSchema, MAX_ASK_USER_QUESTIONS, MAX_CACHE_SIZE, MAX_PATTERN_LENGTH, MemorySaver, ModelEndHandler, MultiAgentGraph, OPENAI_RESPONSES_REPLAY_POSITIONS_KEY, ORIGINAL_CONTENT_MAX_CHARS, PREDECESSOR_HANDOFF_CUE, PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS, ProgrammaticToolCallingDefinition, ProgrammaticToolCallingDescription, ProgrammaticToolCallingName, ProgrammaticToolCallingSchema, PromptTemplate, Providers, REMOVE_ALL_MESSAGES, REPLY_PRIMER_TOKENS, ReadFileToolDefinition, ReadFileToolDescription, ReadFileToolName, ReadFileToolSchema, Run, Runnable, RunnableCallable, RunnableLambda, RunnableSequence, SDK_STREAM_DISPATCH, STATEFUL_BASH_NOTE, STATEFUL_ENV_NOTE, SessionManager, SkillToolDefinition, SkillToolDescription, SkillToolName, SkillToolSchema, StandardGraph, StatefulBashExecutionToolDescription, StatefulCodeExecutionToolDescription, StepTypes, StreamLimitExceededError, StructuredTool, SubagentExecutor, SubagentToolDefinition, SubagentToolDescription, SubagentToolName, SubagentToolSchema, SystemMessage, TMP_SCRATCH_OUTPUT_REMINDER, TOOL_APPROVAL_EXECUTION_SCOPE_CONFIG_KEY, TestChatStreamHandler, TestLLMStreamHandler, TitleMethod, TokenEncoderManager, Tool, ToolCallTypes, ToolEndHandler, ToolMessage, ToolNode, ToolSearchToolDefinition, ToolSearchToolDescription, ToolSearchToolName, ToolSearchToolSchema, UnsafeTokenMeasurementError, WebSearchToolDefinition, WebSearchToolDescription, WebSearchToolName, WebSearchToolSchema, _createBashProgramForTests, _resetLocalEngineWarningsForTests, _resetRipgrepCacheForTests, _resetSyntaxCheckProbeCacheForTests, _resetUnrecognizedTriggerWarnings, addBedrockCacheControl, addBedrockTailCacheControl, addCacheControl, addCacheControlToStablePrefixMessages, addTailCacheControl, appendCodeSessionFileSummary, appendFailedExecutionFileReminder, appendPredecessorHandoffCue, appendTmpScratchReminder, applyContextPruning, applyEdit, applyOutcome, applyPreToolUseHooksForBridge, apportionTokenCounts, askUserQuestion, askUserQuestions, attemptInvoke, bashAstFindingsToErrors, buildAnthropicCacheControl, buildBashExecutionToolDescription, buildBashExecutionToolSchema, buildBedrockCachePoint, buildChildInputs, buildCodeApiExecutionErrorMessage, buildCodeApiHttpErrorMessage, buildCodeExecutionToolDescription, buildCodeExecutionToolSchema, buildSandboxRuntimeConfig, buildSubagentToolParams, calculateMaxToolCallInputChars, calculateMaxToolResultChars, calculateMaxTotalToolOutputSize, calculateTotalTokens, canSealPreempt, checkValidNumber, clampCalibrationRatio, classifyAttachment, clientExecTimeoutMs, clientFsTimeoutMs, cloneMessage, coalesceAdjacentUserTurns, composeAbortSignals, composeEventHandlers, computeAdaptivePieceSize, convertInjectedMessages, convertMessagesToContent, countNestedGroups, countrySchema, createAgentSession, createBashExecutionTool, createBashProgrammaticToolCallingSchema, createBashProgrammaticToolCallingTool, createCloudflareBashExecutionTool, createCloudflareBashProgrammaticToolCallingTool, createCloudflareBridgeRuntime, createCloudflareCodeExecutionTool, createCloudflareCodingToolBundle, createCloudflareCodingTools, createCloudflareExecutionTool, createCloudflareLocalExecutionConfig, createCloudflareProgrammaticToolCallingTool, createCloudflareWorkspaceFS, createCodeExecutionTool, createCompileCheckTool, createCompileCheckToolDefinition, createContentAggregator, createFakeStreamingLLM, createGraph, createHandlers, createLocalBashExecutionTool, createLocalBashProgrammaticToolCallingTool, createLocalCodeExecutionTool, createLocalCodingToolBundle, createLocalCodingToolDefinitions, createLocalCodingToolRegistry, createLocalCodingTools, createLocalEditFileTool, createLocalFileCheckpointer, createLocalGlobSearchTool, createLocalGrepSearchTool, createLocalListDirectoryTool, createLocalProgrammaticToolCallingTool, createLocalReadFileTool, createLocalWriteFileTool, createMetadataAggregator, createProgrammaticToolCallingSchema, createProgrammaticToolCallingTool, createPruneMessages, createRemoveAllMessage, createRunHandlers, createSchemaOnlyTool, createSchemaOnlyTools, createSearchTool, createSubagentToolDefinition, createTokenCounter, createToolErrorOwnership, createToolPolicyHook, createToolSearch, createWorkspacePolicyHook, dateSchema, decodeFile, defaultOmitOptions, deserializeMessage, dispatchesChatModelStream, emptyOutputMessage, encodeFile, encodingForModel, enforceOriginalContentCap, ensureThinkingBlockInMessages, escapeRegexSpecialChars, estimateAnthropicImageTokens, estimateDocumentBlockTokens, estimateImageBlockTokens, estimateOpenAIImageTokens, estimateTimedMediaBlockTokens, execWithClientTimeout, executeCloudflareBash, executeCloudflareCode, executeHooks, executeLocalBash, executeLocalBashWithArgs, executeLocalCode, executeParallelSearches, executeTools, extractErrorMessage, extractImageDimensions, extractMcpServerName, extractTextFromContent, extractToolDiscoveries, extractUsedBashToolNames, extractUsedToolNames, fetchSessionFiles, filterBashToolsByUsage, filterGraphSubagentResult, filterSubagentResult, filterToolsByUsage, findLastIndex, foldToolBlocksForToollessAgent, formatAgentMessages, formatAnthropicArtifactContent, formatAnthropicMessage, formatArtifactPayload, formatCloudflareOutput, formatCompletedResponse, formatContentStrings, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, formatServerListing, formatSkillCatalog, getAssistantTextPhase, getAvailableMcpServers, getBaseToolName, getBufferString, getChatModelClass, getChunkContent, getCloudflareWorkspaceRoot, getCodeBaseURL, getContextOverflowInfo, getConverseOverrideMessage, getDeferredToolsListing, getLocalCwd, getLocalSessionId, getMaxOutputTokensKey, getMessageCreationContentMetadata, getMessageId, getMessagesWithinTokenLimit, getReadRoots, getSpawn, getTokenCountForMessage, getWorkspaceFS, getWorkspaceRoots, getWriteRoots, handleServerToolResult, handleToolCallChunks, handleToolCalls, hasNestedQuantifier, hasNestedQuantifiers, hasToolSearchInCurrentTurn, hasUnsafeStructuredSerialization, imageAttachmentContent, imagesSchema, initializeModel, interrupt, isAIMessage, isAnthropicLike, isAskUserQuestionsInterrupt, isBaseMessage, isContextOverflowError, isDangerousPattern, isFromAnyMcpServer, isFromMcpServer, isGoogleLike, isGraphSubagentConfig, isIntentLabelProperty, isInterrupted, isLegacyConvertible, isLikelyContextOverflowError, isOpenAILike, isPresent, isSyntheticProviderContextMessage, isThinkingEnabled, isToolMessage, isZodSchema, joinKeys, labelContentByAgent, locateEdit, makeIsDeferred, makeRequest, maskConsumedToolResults, matchesQuery, messagesStateReducer, modifyDeltaProperties, newsSchema, normalizeBashToolResultsForReplay, normalizeCodeApiRequestError, normalizeServerFilter, normalizeSubagentConfigEntries, normalizeSubagentConfigs, normalizeToBashIdentifier, normalizeToPythonIdentifier, outcomeFieldsFromResult, parseBooleanEnv, partitionAndMarkAnthropicToolCache, performLocalSearch, preFlightTruncateToolCallInputs, preFlightTruncateToolResults, projectAgentContextUsage, projectAnthropicArtifactContent, projectArtifactPayload, projectCacheControlledToolOutputsToText, projectComputerCallOutputsToText, projectOpenAIChatToolMessageContent, projectOpenAIResponsesToolMessageContent, projectOpenAIToolMessageContent, projectOpenRouterToolMessageContent, projectSingleTextToolOutputsToText, projectStructuredToolOutputsToText, projectToolCallInputs, projectToolStreamContentForProvider, querySchema, readIntent, readOutcomeFields, removePredecessorHandoffCue, repairOrphanedToolMessages, resetIfNotEmpty, resolveBedrockPromptCacheTtl, resolveCloudflareSandbox, resolveCodeApiAuthHeaders, resolveContextPruningSettings, resolveFetchProxyAgent, resolveLocalExecutionConfig, resolveLocalExecutionTools, resolveLocalToolRegistry, resolveLocalToolsForBinding, resolvePromptCacheTtl, resolveSearchOutcome, resolveStreamDelay, resolveStreamLimits, resolveSubagentConfigEntries, resolveSubagentConfigs, resolveToolOutcome, resolveWorkspacePath, resolveWorkspacePathSafe, runBashAstChecks, runPostEditSyntaxCheck, sanitizeOrphanToolBlocks, sanitizeRegex, serializeMessage, serializeToolCallInput, shellQuote, shiftIndexTokenCountMap, shouldBypassProxy, shouldTriggerSummarization, sleep, smoothStream, spawnLocalProcess, splitAssistantTextContentByPhase, splitAtRecencyBoundary, strictAlternationProviders, stripAnthropicCacheControl, stripBedrockCacheControl, stripCodeSessionFileSummary, stripIntent, summarizeEvent, supportsBedrockToolCache, syncBudgetDerivedFields, toJsonSchema, tool, toolResultTypes, toolsCondition, truncateLocalOutput, truncateToolInput, truncateToolResultContent, tryFallbackProviders, unescapeObject, unwrapToolResponse, validateBashCommand, validateCloudflareBashCommand, videosSchema, withClientTimeout, withIntent, withMessageRole, withoutIntent };
@@ -0,0 +1,57 @@
1
+ import "../common/enum.mjs";
2
+ import "../common/index.mjs";
3
+ //#region src/messages/assistantPhase.ts
4
+ function toAssistantTextPhase(value) {
5
+ return value === "commentary" || value === "final_answer" ? value : void 0;
6
+ }
7
+ /** Reads both provider-native and LangChain standard-content phase fields. */
8
+ function getAssistantTextPhase(contentPart) {
9
+ return toAssistantTextPhase(contentPart.phase) ?? toAssistantTextPhase(contentPart.extras?.phase);
10
+ }
11
+ /**
12
+ * Keeps provider-authored text phases in distinct message-creation steps.
13
+ * Open Responses may return commentary and final-answer blocks in one chunk;
14
+ * collapsing the array into one step would assign the first block's phase to
15
+ * every block and hide the boundary that closes an activity phase.
16
+ */
17
+ function splitAssistantTextContentByPhase(content) {
18
+ const groups = [];
19
+ for (const contentPart of content) {
20
+ const phase = getAssistantTextPhase(contentPart);
21
+ const currentGroup = groups.at(-1);
22
+ if (currentGroup == null || getAssistantTextPhase(currentGroup[0]) !== phase) {
23
+ groups.push([contentPart]);
24
+ continue;
25
+ }
26
+ currentGroup.push(contentPart);
27
+ }
28
+ return groups;
29
+ }
30
+ function isTextPart(contentPart) {
31
+ return contentPart.type?.startsWith("text") ?? false;
32
+ }
33
+ function isReasoningPart(contentPart) {
34
+ return contentPart.type === "think" || (contentPart.type?.startsWith("thinking") ?? false) || (contentPart.type?.startsWith("reasoning") ?? false) || (contentPart.type?.startsWith("reasoning_content") ?? false) || contentPart.type === "redacted_thinking";
35
+ }
36
+ /**
37
+ * Derives additive message-creation metadata before a content delta is
38
+ * dispatched. The fallback covers string-only providers whose semantic lane
39
+ * is tracked by the stream handler rather than represented on a block.
40
+ */
41
+ function getMessageCreationContentMetadata(content, fallbackContentType) {
42
+ if (!Array.isArray(content)) return fallbackContentType == null ? {} : { content_type: fallbackContentType };
43
+ const textPart = content.find(isTextPart);
44
+ if (textPart != null) {
45
+ const phase = getAssistantTextPhase(textPart);
46
+ return {
47
+ content_type: "text",
48
+ ...phase == null ? {} : { phase }
49
+ };
50
+ }
51
+ if (content.some(isReasoningPart)) return { content_type: "think" };
52
+ return fallbackContentType == null ? {} : { content_type: fallbackContentType };
53
+ }
54
+ //#endregion
55
+ export { getAssistantTextPhase, getMessageCreationContentMetadata, splitAssistantTextContentByPhase };
56
+
57
+ //# sourceMappingURL=assistantPhase.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistantPhase.mjs","names":[],"sources":["../../../src/messages/assistantPhase.ts"],"sourcesContent":["import type { AssistantTextPhase } from '@/types/assistantPhase';\nimport type { MessageContentComplex } from '@/types/stream';\nimport { ContentTypes } from '@/common';\n\nexport type MessageCreationContentMetadata = {\n content_type?: ContentTypes.TEXT | ContentTypes.THINK;\n phase?: AssistantTextPhase;\n};\n\nfunction toAssistantTextPhase(value: unknown): AssistantTextPhase | undefined {\n return value === 'commentary' || value === 'final_answer' ? value : undefined;\n}\n\n/** Reads both provider-native and LangChain standard-content phase fields. */\nexport function getAssistantTextPhase(\n contentPart: MessageContentComplex\n): AssistantTextPhase | undefined {\n return (\n toAssistantTextPhase(contentPart.phase) ??\n toAssistantTextPhase(contentPart.extras?.phase)\n );\n}\n\n/**\n * Keeps provider-authored text phases in distinct message-creation steps.\n * Open Responses may return commentary and final-answer blocks in one chunk;\n * collapsing the array into one step would assign the first block's phase to\n * every block and hide the boundary that closes an activity phase.\n */\nexport function splitAssistantTextContentByPhase(\n content: MessageContentComplex[]\n): MessageContentComplex[][] {\n const groups: MessageContentComplex[][] = [];\n for (const contentPart of content) {\n const phase = getAssistantTextPhase(contentPart);\n const currentGroup = groups.at(-1);\n if (\n currentGroup == null ||\n getAssistantTextPhase(currentGroup[0]) !== phase\n ) {\n groups.push([contentPart]);\n continue;\n }\n currentGroup.push(contentPart);\n }\n return groups;\n}\n\nfunction isTextPart(contentPart: MessageContentComplex): boolean {\n return contentPart.type?.startsWith(ContentTypes.TEXT) ?? false;\n}\n\nfunction isReasoningPart(contentPart: MessageContentComplex): boolean {\n return (\n contentPart.type === ContentTypes.THINK ||\n (contentPart.type?.startsWith(ContentTypes.THINKING) ?? false) ||\n (contentPart.type?.startsWith(ContentTypes.REASONING) ?? false) ||\n (contentPart.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false) ||\n contentPart.type === 'redacted_thinking'\n );\n}\n\n/**\n * Derives additive message-creation metadata before a content delta is\n * dispatched. The fallback covers string-only providers whose semantic lane\n * is tracked by the stream handler rather than represented on a block.\n */\nexport function getMessageCreationContentMetadata(\n content: string | MessageContentComplex[] | undefined,\n fallbackContentType?: ContentTypes.TEXT | ContentTypes.THINK\n): MessageCreationContentMetadata {\n if (!Array.isArray(content)) {\n return fallbackContentType == null\n ? {}\n : { content_type: fallbackContentType };\n }\n const textPart = content.find(isTextPart);\n if (textPart != null) {\n const phase = getAssistantTextPhase(textPart);\n return {\n content_type: ContentTypes.TEXT,\n ...(phase == null ? {} : { phase }),\n };\n }\n if (content.some(isReasoningPart)) {\n return { content_type: ContentTypes.THINK };\n }\n return fallbackContentType == null\n ? {}\n : { content_type: fallbackContentType };\n}\n"],"mappings":";;;AASA,SAAS,qBAAqB,OAAgD;CAC5E,OAAO,UAAU,gBAAgB,UAAU,iBAAiB,QAAQ,KAAA;AACtE;;AAGA,SAAgB,sBACd,aACgC;CAChC,OACE,qBAAqB,YAAY,KAAK,KACtC,qBAAqB,YAAY,QAAQ,KAAK;AAElD;;;;;;;AAQA,SAAgB,iCACd,SAC2B;CAC3B,MAAM,SAAoC,CAAC;CAC3C,KAAK,MAAM,eAAe,SAAS;EACjC,MAAM,QAAQ,sBAAsB,WAAW;EAC/C,MAAM,eAAe,OAAO,GAAG,EAAE;EACjC,IACE,gBAAgB,QAChB,sBAAsB,aAAa,EAAE,MAAM,OAC3C;GACA,OAAO,KAAK,CAAC,WAAW,CAAC;GACzB;EACF;EACA,aAAa,KAAK,WAAW;CAC/B;CACA,OAAO;AACT;AAEA,SAAS,WAAW,aAA6C;CAC/D,OAAO,YAAY,MAAM,WAAA,MAA4B,KAAK;AAC5D;AAEA,SAAS,gBAAgB,aAA6C;CACpE,OACE,YAAY,SAAA,YACX,YAAY,MAAM,WAAA,UAAgC,KAAK,WACvD,YAAY,MAAM,WAAA,WAAiC,KAAK,WACxD,YAAY,MAAM,WAAA,mBAAyC,KAAK,UACjE,YAAY,SAAS;AAEzB;;;;;;AAOA,SAAgB,kCACd,SACA,qBACgC;CAChC,IAAI,CAAC,MAAM,QAAQ,OAAO,GACxB,OAAO,uBAAuB,OAC1B,CAAC,IACD,EAAE,cAAc,oBAAoB;CAE1C,MAAM,WAAW,QAAQ,KAAK,UAAU;CACxC,IAAI,YAAY,MAAM;EACpB,MAAM,QAAQ,sBAAsB,QAAQ;EAC5C,OAAO;GACL,cAAA;GACA,GAAI,SAAS,OAAO,CAAC,IAAI,EAAE,MAAM;EACnC;CACF;CACA,IAAI,QAAQ,KAAK,eAAe,GAC9B,OAAO,EAAE,cAAA,QAAiC;CAE5C,OAAO,uBAAuB,OAC1B,CAAC,IACD,EAAE,cAAc,oBAAoB;AAC1C"}
@@ -14,4 +14,5 @@ import "./alternation.mjs";
14
14
  import "./handoffCue.mjs";
15
15
  import "./reducer.mjs";
16
16
  import "./recency.mjs";
17
+ import "./assistantPhase.mjs";
17
18
  export {};
@@ -22,6 +22,27 @@ Examples:
22
22
  - Fixed failing auth middleware tests
23
23
  - Read project config and dependency manifests
24
24
  - Attempted database migration, hit permission errors`;
25
+ /** Default system prompt for a run-wide parent activity phase. */
26
+ const ACTIVITY_PHASE_LABEL_PROMPT = `Summarize what this phase of an agent run accomplished. The result appears as the header of one collapsed parent group containing several activities.
27
+
28
+ Rules:
29
+ - One line, 8 to 18 words, past tense
30
+ - Lead with the concrete outcome and name the most distinctive subject
31
+ - Synthesize the phase; do not enumerate, count, or restate individual activities
32
+ - Describe failures plainly when they are the phase's material outcome
33
+ - Never mention tool names, calls, arguments, reasoning, commentary, or activity counts
34
+ - Output only the summary — no quotes, no trailing punctuation, no preamble
35
+
36
+ Examples:
37
+ - Reconciled authentication behavior and fixed the failing session refresh path
38
+ - Compared deployment options and documented the safest production rollout
39
+ - Investigated database latency but could not confirm the suspected index regression
40
+
41
+ Bad examples:
42
+ - Used three tools to inspect files and run tests
43
+ - Searched code, read configuration, and updated middleware`;
44
+ /** Hard ceiling across every activity/context section in one phase request. */
45
+ const ACTIVITY_PHASE_PROMPT_MAX_LENGTH = 12e3;
25
46
  /** Truncates a serialized value for the label prompt. */
26
47
  function truncateForLabel(value, maxLength) {
27
48
  if (value.length <= maxLength) return value;
@@ -81,6 +102,8 @@ const PREVIOUS_LABEL_LIMIT = 200;
81
102
  * produce one, and the cap keeps a 200-call programmatic batch from
82
103
  * building an enormous prompt out of per-field-bounded pieces. */
83
104
  const MAX_PROMPT_ENTRIES = 12;
105
+ const MAX_PHASE_ACTIVITIES = 12;
106
+ const MAX_PHASE_TOOL_ENTRIES = 6;
84
107
  /**
85
108
  * Builds the user prompt for a fast-model activity label. Pure — exported
86
109
  * for direct testing of redaction and truncation behavior.
@@ -134,7 +157,57 @@ function buildActivityLabelPrompt({ entries, charLimit, thinkingExcerpts, lastAs
134
157
  sections.push("Header:");
135
158
  return sections.join("\n\n");
136
159
  }
160
+ /**
161
+ * Builds bounded, redaction-aware evidence for a parent activity phase.
162
+ * Committed child labels are preferred; raw tool/reasoning evidence is only
163
+ * used when no child label exists.
164
+ */
165
+ function buildActivityPhaseLabelPrompt({ activities, totalActivityCount, charLimit, assistantContext, redaction }) {
166
+ const freeFormSuppressed = redaction != null && (redaction.enabled === false || redaction.redactedToolNames.size > 0);
167
+ const sections = [];
168
+ if (!freeFormSuppressed && assistantContext != null && assistantContext.length > 0) {
169
+ const context = assistantContext.slice(-3).map((text) => truncateForLabel(text.replace(/\s+/g, " ").trim(), charLimit)).filter((text) => text.length > 0);
170
+ if (context.length > 0) sections.push("Intermediate assistant context (do not quote or restate):\n" + context.map((text) => `- ${text}`).join("\n"));
171
+ }
172
+ let hasDescribableEvidence = false;
173
+ const activityLines = activities.slice(0, MAX_PHASE_ACTIVITIES).map((activity, index) => {
174
+ let status = "completed";
175
+ if (activity.status === "error") status = "failed";
176
+ else if (activity.status === "partial") status = "partial";
177
+ if (!freeFormSuppressed && activity.label != null && activity.label.trim() !== "") {
178
+ hasDescribableEvidence = true;
179
+ return `${index + 1}. ${status}: ${truncateForLabel(activity.label.replace(/\s+/g, " ").trim(), charLimit)}`;
180
+ }
181
+ const evidence = [];
182
+ if (!freeFormSuppressed && activity.thinkingExcerpts != null && activity.thinkingExcerpts.length > 0) evidence.push(...activity.thinkingExcerpts.slice(0, MAX_THINKING_EXCERPTS).map((excerpt) => truncateForLabel(excerpt.replace(/\s+/g, " ").trim(), charLimit)).filter((excerpt) => excerpt.length > 0).map((excerpt) => `context=${excerpt}`));
183
+ if (activity.entries != null && activity.entries.length > 0) evidence.push(...activity.entries.slice(0, MAX_PHASE_TOOL_ENTRIES).map((entry) => {
184
+ const entryRedacted = redaction != null && shouldRedactTool(entry.toolName, redaction);
185
+ const input = truncateForLabel(serializeForLabel(entry.toolInput, charLimit), charLimit);
186
+ let outcome;
187
+ if (entryRedacted) outcome = redaction.redactionText;
188
+ else if (entry.status === "error") outcome = `ERROR: ${truncateForLabel(entry.error ?? "unknown error", charLimit)}`;
189
+ else outcome = truncateForLabel(serializeForLabel(entry.toolOutput, charLimit), charLimit);
190
+ return `${entry.toolName}(${input}) → ${outcome}`;
191
+ }));
192
+ if (evidence.length > 0) hasDescribableEvidence = true;
193
+ return `${index + 1}. ${status}${evidence.length > 0 ? `: ${evidence.join("; ")}` : ""}`;
194
+ });
195
+ if (!hasDescribableEvidence) return "";
196
+ const activityCount = Math.max(activities.length, totalActivityCount ?? 0);
197
+ if (activityCount > MAX_PHASE_ACTIVITIES) activityLines.push(`13. …and ${activityCount - MAX_PHASE_ACTIVITIES} more activities`);
198
+ sections.push("Activities in this phase (synthesize; do not restate):\n" + activityLines.join("\n"));
199
+ const terminalCue = "\n\nPhase summary:";
200
+ const evidence = sections.join("\n\n");
201
+ const prompt = evidence + terminalCue;
202
+ if (prompt.length <= 12e3) return prompt;
203
+ const evidenceLimit = ACTIVITY_PHASE_PROMPT_MAX_LENGTH - 16 - 1;
204
+ return `${evidence.slice(0, evidenceLimit).trimEnd()}…${terminalCue}`;
205
+ }
206
+ /** Normalizes a model result for safe single-row persistence and display. */
207
+ function normalizeActivityPhaseLabel(label) {
208
+ return truncateForLabel(label.replace(/\s+/g, " ").trim().replace(/^["']|["']$/g, "").replace(/[.!?]+$/g, ""), 160);
209
+ }
137
210
  //#endregion
138
- export { ACTIVITY_LABEL_PROMPT, buildActivityLabelPrompt };
211
+ export { ACTIVITY_LABEL_PROMPT, ACTIVITY_PHASE_LABEL_PROMPT, buildActivityLabelPrompt, buildActivityPhaseLabelPrompt, normalizeActivityPhaseLabel };
139
212
 
140
213
  //# sourceMappingURL=activityLabel.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"activityLabel.mjs","names":[],"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\n/**\n * Reduces a committed label to bounded single-line data.\n *\n * Sections in this prompt are delimited by blank lines, so a label carrying\n * embedded newlines could otherwise forge an apparent entries section or\n * `Header:` cue. Unlike every other input here, previous labels re-enter\n * the prompt on EVERY later batch, so one malformed result — plain model\n * noncompliance, or injection surfacing through a tool result — would\n * persistently steer unrelated later labels rather than affecting one. The\n * clip bounds the same way `lastAssistantText` and reasoning excerpts are\n * bounded: oversized headers must not inflate later requests past the fast\n * model's window and starve the run of labels entirely.\n */\nfunction sanitizePreviousLabel(label: string): string {\n return truncateForLabel(\n label.replace(/\\s+/g, ' ').trim(),\n PREVIOUS_LABEL_LIMIT\n );\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;\nconst MAX_PREVIOUS_LABELS = 3;\n/** Per-label bound. A header is 5-9 words; anything past this is\n * noncompliance or payload, and previous labels are the one input that\n * RE-ENTERS the prompt on every later batch of the run. */\nconst PREVIOUS_LABEL_LIMIT = 200;\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 * Headers already committed for earlier batches in this run, in run order\n * with the most recent last. Rendered ahead of the block context so the\n * label continues the run's story instead of restating a line the user is\n * already reading. Capped at {@link MAX_PREVIOUS_LABELS}.\n */\n previousLabels?: 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 previousLabels,\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 /** Previous labels are free-form model prose too, and per-agent overlays\n * mean an earlier header may have been generated under ANOTHER agent's\n * weaker policy — so they share the excerpts' wholesale drop rather than\n * letting a handoff leak a looser agent's phrasing into this trace. */\n if (\n !excerptsRedacted &&\n previousLabels != null &&\n previousLabels.length > 0\n ) {\n const recent = previousLabels\n .slice(-MAX_PREVIOUS_LABELS)\n .map(sanitizePreviousLabel)\n /** A label that sanitizes to nothing carries no story to continue;\n * rendering it would leave a bare bullet implying a missing header. */\n .filter((label) => label.length > 0);\n if (recent.length > 0) {\n sections.push(\n 'Previous headers in this run (most recent last):\\n' +\n recent.map((label) => `- ${label}`).join('\\n')\n );\n }\n }\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 /** Frames the list as reference material, not the thing to\n * transcribe. Ported from LibreChat's fallback builder (its\n * runtime.ts documents that without this the model \"hands back a\n * transcription\" of the list) after the eval harness measured it\n * across three independent sweeps: fewer template-redundancy and\n * length violations than a bare `Tool calls:` heading, with no\n * per-case regressions (agents #360). */\n 'What it called, and what came back (do not restate these):\\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 /** The fallback builder's terminal cue, measured alongside the heading\n * (same sweeps). The default system prompt already describes the\n * output as \"the header of a collapsed activity group\". */\n sections.push('Header:');\n return sections.join('\\n\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"activityLabel.mjs","names":[],"sources":["../../../src/prompts/activityLabel.ts"],"sourcesContent":["import type {\n ActivityLabelToolEntry,\n ActivityPhaseEntry,\n} from '@/types/activityLabel';\nimport type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\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/** Default system prompt for a run-wide parent activity phase. */\nexport const ACTIVITY_PHASE_LABEL_PROMPT = `Summarize what this phase of an agent run accomplished. The result appears as the header of one collapsed parent group containing several activities.\n\nRules:\n- One line, 8 to 18 words, past tense\n- Lead with the concrete outcome and name the most distinctive subject\n- Synthesize the phase; do not enumerate, count, or restate individual activities\n- Describe failures plainly when they are the phase's material outcome\n- Never mention tool names, calls, arguments, reasoning, commentary, or activity counts\n- Output only the summary — no quotes, no trailing punctuation, no preamble\n\nExamples:\n- Reconciled authentication behavior and fixed the failing session refresh path\n- Compared deployment options and documented the safest production rollout\n- Investigated database latency but could not confirm the suspected index regression\n\nBad examples:\n- Used three tools to inspect files and run tests\n- Searched code, read configuration, and updated middleware`;\n\n/** Hard ceiling across every activity/context section in one phase request. */\nexport const ACTIVITY_PHASE_PROMPT_MAX_LENGTH = 12_000;\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\n/**\n * Reduces a committed label to bounded single-line data.\n *\n * Sections in this prompt are delimited by blank lines, so a label carrying\n * embedded newlines could otherwise forge an apparent entries section or\n * `Header:` cue. Unlike every other input here, previous labels re-enter\n * the prompt on EVERY later batch, so one malformed result — plain model\n * noncompliance, or injection surfacing through a tool result — would\n * persistently steer unrelated later labels rather than affecting one. The\n * clip bounds the same way `lastAssistantText` and reasoning excerpts are\n * bounded: oversized headers must not inflate later requests past the fast\n * model's window and starve the run of labels entirely.\n */\nfunction sanitizePreviousLabel(label: string): string {\n return truncateForLabel(\n label.replace(/\\s+/g, ' ').trim(),\n PREVIOUS_LABEL_LIMIT\n );\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;\nconst MAX_PREVIOUS_LABELS = 3;\n/** Per-label bound. A header is 5-9 words; anything past this is\n * noncompliance or payload, and previous labels are the one input that\n * RE-ENTERS the prompt on every later batch of the run. */\nconst PREVIOUS_LABEL_LIMIT = 200;\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;\nconst MAX_PHASE_ACTIVITIES = 12;\nconst MAX_PHASE_CONTEXT = 3;\nconst MAX_PHASE_TOOL_ENTRIES = 6;\nexport const ACTIVITY_PHASE_LABEL_MAX_LENGTH = 160;\n\nexport type BuildActivityLabelPromptParams = {\n entries: ActivityLabelToolEntry[];\n charLimit: number;\n thinkingExcerpts?: string[];\n lastAssistantText?: string;\n /**\n * Headers already committed for earlier batches in this run, in run order\n * with the most recent last. Rendered ahead of the block context so the\n * label continues the run's story instead of restating a line the user is\n * already reading. Capped at {@link MAX_PREVIOUS_LABELS}.\n */\n previousLabels?: 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 previousLabels,\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 /** Previous labels are free-form model prose too, and per-agent overlays\n * mean an earlier header may have been generated under ANOTHER agent's\n * weaker policy — so they share the excerpts' wholesale drop rather than\n * letting a handoff leak a looser agent's phrasing into this trace. */\n if (\n !excerptsRedacted &&\n previousLabels != null &&\n previousLabels.length > 0\n ) {\n const recent = previousLabels\n .slice(-MAX_PREVIOUS_LABELS)\n .map(sanitizePreviousLabel)\n /** A label that sanitizes to nothing carries no story to continue;\n * rendering it would leave a bare bullet implying a missing header. */\n .filter((label) => label.length > 0);\n if (recent.length > 0) {\n sections.push(\n 'Previous headers in this run (most recent last):\\n' +\n recent.map((label) => `- ${label}`).join('\\n')\n );\n }\n }\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 /** Frames the list as reference material, not the thing to\n * transcribe. Ported from LibreChat's fallback builder (its\n * runtime.ts documents that without this the model \"hands back a\n * transcription\" of the list) after the eval harness measured it\n * across three independent sweeps: fewer template-redundancy and\n * length violations than a bare `Tool calls:` heading, with no\n * per-case regressions (agents #360). */\n 'What it called, and what came back (do not restate these):\\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 /** The fallback builder's terminal cue, measured alongside the heading\n * (same sweeps). The default system prompt already describes the\n * output as \"the header of a collapsed activity group\". */\n sections.push('Header:');\n return sections.join('\\n\\n');\n}\n\nexport type BuildActivityPhaseLabelPromptParams = {\n activities: ActivityPhaseEntry[];\n totalActivityCount?: number;\n charLimit: number;\n assistantContext?: string[];\n redaction?: ResolvedLangfuseToolOutputTracingConfig;\n};\n\n/**\n * Builds bounded, redaction-aware evidence for a parent activity phase.\n * Committed child labels are preferred; raw tool/reasoning evidence is only\n * used when no child label exists.\n */\nexport function buildActivityPhaseLabelPrompt({\n activities,\n totalActivityCount,\n charLimit,\n assistantContext,\n redaction,\n}: BuildActivityPhaseLabelPromptParams): string {\n const freeFormSuppressed =\n redaction != null &&\n (redaction.enabled === false || redaction.redactedToolNames.size > 0);\n const sections: string[] = [];\n if (\n !freeFormSuppressed &&\n assistantContext != null &&\n assistantContext.length > 0\n ) {\n const context = assistantContext\n .slice(-MAX_PHASE_CONTEXT)\n .map((text) =>\n truncateForLabel(text.replace(/\\s+/g, ' ').trim(), charLimit)\n )\n .filter((text) => text.length > 0);\n if (context.length > 0) {\n sections.push(\n 'Intermediate assistant context (do not quote or restate):\\n' +\n context.map((text) => `- ${text}`).join('\\n')\n );\n }\n }\n\n let hasDescribableEvidence = false;\n const activityLines = activities\n .slice(0, MAX_PHASE_ACTIVITIES)\n .map((activity, index) => {\n let status = 'completed';\n if (activity.status === 'error') {\n status = 'failed';\n } else if (activity.status === 'partial') {\n status = 'partial';\n }\n if (\n !freeFormSuppressed &&\n activity.label != null &&\n activity.label.trim() !== ''\n ) {\n hasDescribableEvidence = true;\n return `${index + 1}. ${status}: ${truncateForLabel(activity.label.replace(/\\s+/g, ' ').trim(), charLimit)}`;\n }\n\n const evidence: string[] = [];\n if (\n !freeFormSuppressed &&\n activity.thinkingExcerpts != null &&\n activity.thinkingExcerpts.length > 0\n ) {\n evidence.push(\n ...activity.thinkingExcerpts\n .slice(0, MAX_THINKING_EXCERPTS)\n .map((excerpt) =>\n truncateForLabel(excerpt.replace(/\\s+/g, ' ').trim(), charLimit)\n )\n .filter((excerpt) => excerpt.length > 0)\n .map((excerpt) => `context=${excerpt}`)\n );\n }\n if (activity.entries != null && activity.entries.length > 0) {\n evidence.push(\n ...activity.entries.slice(0, MAX_PHASE_TOOL_ENTRIES).map((entry) => {\n const entryRedacted =\n redaction != null && shouldRedactTool(entry.toolName, redaction);\n const input = truncateForLabel(\n serializeForLabel(entry.toolInput, charLimit),\n charLimit\n );\n let outcome: string;\n if (entryRedacted) {\n outcome = redaction.redactionText;\n } else if (entry.status === 'error') {\n outcome = `ERROR: ${truncateForLabel(\n entry.error ?? 'unknown error',\n charLimit\n )}`;\n } else {\n outcome = truncateForLabel(\n serializeForLabel(entry.toolOutput, charLimit),\n charLimit\n );\n }\n return `${entry.toolName}(${input}) → ${outcome}`;\n })\n );\n }\n if (evidence.length > 0) {\n hasDescribableEvidence = true;\n }\n return `${index + 1}. ${status}${evidence.length > 0 ? `: ${evidence.join('; ')}` : ''}`;\n });\n\n if (!hasDescribableEvidence) {\n return '';\n }\n\n const activityCount = Math.max(activities.length, totalActivityCount ?? 0);\n if (activityCount > MAX_PHASE_ACTIVITIES) {\n activityLines.push(\n `${MAX_PHASE_ACTIVITIES + 1}. …and ${activityCount - MAX_PHASE_ACTIVITIES} more activities`\n );\n }\n sections.push(\n 'Activities in this phase (synthesize; do not restate):\\n' +\n activityLines.join('\\n')\n );\n const terminalCue = '\\n\\nPhase summary:';\n const evidence = sections.join('\\n\\n');\n const prompt = evidence + terminalCue;\n if (prompt.length <= ACTIVITY_PHASE_PROMPT_MAX_LENGTH) {\n return prompt;\n }\n const evidenceLimit =\n ACTIVITY_PHASE_PROMPT_MAX_LENGTH - terminalCue.length - 1;\n return `${evidence.slice(0, evidenceLimit).trimEnd()}…${terminalCue}`;\n}\n\n/** Normalizes a model result for safe single-row persistence and display. */\nexport function normalizeActivityPhaseLabel(label: string): string {\n const normalized = label\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^[\"']|[\"']$/g, '')\n .replace(/[.!?]+$/g, '');\n return truncateForLabel(normalized, ACTIVITY_PHASE_LABEL_MAX_LENGTH);\n}\n"],"mappings":"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"}