@librechat/agents 3.1.85 → 3.1.87

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 (166) hide show
  1. package/README.md +69 -0
  2. package/dist/cjs/agents/AgentContext.cjs +7 -2
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/events.cjs +23 -0
  5. package/dist/cjs/events.cjs.map +1 -1
  6. package/dist/cjs/graphs/Graph.cjs +133 -18
  7. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  10. package/dist/cjs/llm/anthropic/index.cjs +251 -53
  11. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  12. package/dist/cjs/llm/init.cjs +1 -5
  13. package/dist/cjs/llm/init.cjs.map +1 -1
  14. package/dist/cjs/llm/openai/index.cjs +113 -24
  15. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  16. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openrouter/index.cjs +3 -1
  18. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  19. package/dist/cjs/main.cjs +18 -5
  20. package/dist/cjs/main.cjs.map +1 -1
  21. package/dist/cjs/openai/index.cjs +253 -0
  22. package/dist/cjs/openai/index.cjs.map +1 -0
  23. package/dist/cjs/responses/index.cjs +448 -0
  24. package/dist/cjs/responses/index.cjs.map +1 -0
  25. package/dist/cjs/run.cjs +108 -7
  26. package/dist/cjs/run.cjs.map +1 -1
  27. package/dist/cjs/session/AgentSession.cjs +1057 -0
  28. package/dist/cjs/session/AgentSession.cjs.map +1 -0
  29. package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
  30. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
  31. package/dist/cjs/session/handlers.cjs +221 -0
  32. package/dist/cjs/session/handlers.cjs.map +1 -0
  33. package/dist/cjs/session/ids.cjs +22 -0
  34. package/dist/cjs/session/ids.cjs.map +1 -0
  35. package/dist/cjs/session/messageSerialization.cjs +179 -0
  36. package/dist/cjs/session/messageSerialization.cjs.map +1 -0
  37. package/dist/cjs/stream.cjs +472 -11
  38. package/dist/cjs/stream.cjs.map +1 -1
  39. package/dist/cjs/summarization/node.cjs +1 -1
  40. package/dist/cjs/summarization/node.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolNode.cjs +177 -59
  42. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  43. package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
  44. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
  45. package/dist/cjs/tools/handlers.cjs +1 -1
  46. package/dist/cjs/tools/handlers.cjs.map +1 -1
  47. package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
  48. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
  49. package/dist/esm/agents/AgentContext.mjs +7 -2
  50. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  51. package/dist/esm/events.mjs +23 -1
  52. package/dist/esm/events.mjs.map +1 -1
  53. package/dist/esm/graphs/Graph.mjs +133 -18
  54. package/dist/esm/graphs/Graph.mjs.map +1 -1
  55. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  56. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  57. package/dist/esm/llm/anthropic/index.mjs +251 -53
  58. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  59. package/dist/esm/llm/init.mjs +1 -5
  60. package/dist/esm/llm/init.mjs.map +1 -1
  61. package/dist/esm/llm/openai/index.mjs +113 -25
  62. package/dist/esm/llm/openai/index.mjs.map +1 -1
  63. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  64. package/dist/esm/llm/openrouter/index.mjs +4 -2
  65. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  66. package/dist/esm/main.mjs +5 -1
  67. package/dist/esm/main.mjs.map +1 -1
  68. package/dist/esm/openai/index.mjs +246 -0
  69. package/dist/esm/openai/index.mjs.map +1 -0
  70. package/dist/esm/responses/index.mjs +440 -0
  71. package/dist/esm/responses/index.mjs.map +1 -0
  72. package/dist/esm/run.mjs +108 -7
  73. package/dist/esm/run.mjs.map +1 -1
  74. package/dist/esm/session/AgentSession.mjs +1054 -0
  75. package/dist/esm/session/AgentSession.mjs.map +1 -0
  76. package/dist/esm/session/JsonlSessionStore.mjs +422 -0
  77. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
  78. package/dist/esm/session/handlers.mjs +219 -0
  79. package/dist/esm/session/handlers.mjs.map +1 -0
  80. package/dist/esm/session/ids.mjs +17 -0
  81. package/dist/esm/session/ids.mjs.map +1 -0
  82. package/dist/esm/session/messageSerialization.mjs +173 -0
  83. package/dist/esm/session/messageSerialization.mjs.map +1 -0
  84. package/dist/esm/stream.mjs +473 -12
  85. package/dist/esm/stream.mjs.map +1 -1
  86. package/dist/esm/summarization/node.mjs +1 -1
  87. package/dist/esm/summarization/node.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +177 -59
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/eagerEventExecution.mjs +107 -0
  91. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
  92. package/dist/esm/tools/handlers.mjs +1 -1
  93. package/dist/esm/tools/handlers.mjs.map +1 -1
  94. package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
  95. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
  96. package/dist/types/events.d.ts +1 -0
  97. package/dist/types/graphs/Graph.d.ts +24 -9
  98. package/dist/types/index.d.ts +1 -0
  99. package/dist/types/llm/openai/index.d.ts +1 -0
  100. package/dist/types/openai/index.d.ts +75 -0
  101. package/dist/types/responses/index.d.ts +97 -0
  102. package/dist/types/run.d.ts +2 -0
  103. package/dist/types/session/AgentSession.d.ts +32 -0
  104. package/dist/types/session/JsonlSessionStore.d.ts +67 -0
  105. package/dist/types/session/handlers.d.ts +8 -0
  106. package/dist/types/session/ids.d.ts +4 -0
  107. package/dist/types/session/index.d.ts +5 -0
  108. package/dist/types/session/messageSerialization.d.ts +7 -0
  109. package/dist/types/session/types.d.ts +191 -0
  110. package/dist/types/tools/ToolNode.d.ts +12 -1
  111. package/dist/types/tools/eagerEventExecution.d.ts +23 -0
  112. package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
  113. package/dist/types/types/hitl.d.ts +4 -0
  114. package/dist/types/types/run.d.ts +11 -1
  115. package/dist/types/types/tools.d.ts +36 -0
  116. package/package.json +19 -2
  117. package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
  118. package/src/agents/AgentContext.ts +7 -2
  119. package/src/agents/__tests__/AgentContext.test.ts +254 -5
  120. package/src/events.ts +29 -0
  121. package/src/graphs/Graph.ts +224 -50
  122. package/src/graphs/MultiAgentGraph.ts +1 -1
  123. package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
  124. package/src/index.ts +3 -0
  125. package/src/llm/anthropic/index.ts +356 -84
  126. package/src/llm/anthropic/llm.spec.ts +64 -0
  127. package/src/llm/custom-chat-models.smoke.test.ts +175 -4
  128. package/src/llm/openai/contentBlocks.test.ts +35 -0
  129. package/src/llm/openai/deepseek.test.ts +201 -2
  130. package/src/llm/openai/index.ts +171 -26
  131. package/src/llm/openai/utils/index.ts +22 -0
  132. package/src/llm/openrouter/index.ts +4 -2
  133. package/src/openai/__tests__/openai.test.ts +337 -0
  134. package/src/openai/index.ts +404 -0
  135. package/src/responses/__tests__/responses.test.ts +652 -0
  136. package/src/responses/index.ts +677 -0
  137. package/src/run.ts +158 -8
  138. package/src/scripts/compare_pi_vs_ours.ts +592 -173
  139. package/src/scripts/session_live.ts +548 -0
  140. package/src/session/AgentSession.ts +1432 -0
  141. package/src/session/JsonlSessionStore.ts +572 -0
  142. package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
  143. package/src/session/__tests__/handlers.test.ts +161 -0
  144. package/src/session/handlers.ts +272 -0
  145. package/src/session/ids.ts +17 -0
  146. package/src/session/index.ts +44 -0
  147. package/src/session/messageSerialization.ts +207 -0
  148. package/src/session/types.ts +275 -0
  149. package/src/specs/custom-event-await.test.ts +89 -0
  150. package/src/specs/summarization.test.ts +1 -1
  151. package/src/stream.ts +755 -48
  152. package/src/summarization/node.ts +1 -1
  153. package/src/tools/ToolNode.ts +299 -126
  154. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
  155. package/src/tools/__tests__/handlers.test.ts +2 -1
  156. package/src/tools/__tests__/hitl.test.ts +206 -110
  157. package/src/tools/eagerEventExecution.ts +153 -0
  158. package/src/tools/handlers.ts +8 -4
  159. package/src/tools/streamedToolCallSeals.ts +57 -0
  160. package/src/types/hitl.ts +4 -0
  161. package/src/types/run.ts +11 -0
  162. package/src/types/tools.ts +36 -0
  163. package/dist/cjs/llm/text.cjs +0 -69
  164. package/dist/cjs/llm/text.cjs.map +0 -1
  165. package/dist/esm/llm/text.mjs +0 -67
  166. package/dist/esm/llm/text.mjs.map +0 -1
@@ -0,0 +1,161 @@
1
+ import { ContentTypes, GraphEvents, StepTypes } from '@/common';
2
+ import { composeEventHandlers } from '@/events';
3
+ import { createRunHandlers } from '@/session';
4
+ import type { AgentSessionStreamEvent } from '@/session';
5
+ import type * as t from '@/types';
6
+
7
+ describe('createRunHandlers', () => {
8
+ it('emits live session events through the same graph handlers before user handlers', async () => {
9
+ const liveEvents: AgentSessionStreamEvent[] = [];
10
+ let liveEventCountSeenByUserHandler = 0;
11
+ const handlerResult = createRunHandlers({
12
+ runId: 'run_live',
13
+ threadId: 'thread_live',
14
+ onEvent: (event) => {
15
+ liveEvents.push(event);
16
+ },
17
+ userHandlers: {
18
+ [GraphEvents.ON_MESSAGE_DELTA]: {
19
+ handle: (): void => {
20
+ liveEventCountSeenByUserHandler = liveEvents.length;
21
+ },
22
+ },
23
+ },
24
+ });
25
+
26
+ await handlerResult.handlers[GraphEvents.ON_RUN_STEP].handle(
27
+ GraphEvents.ON_RUN_STEP,
28
+ {
29
+ stepIndex: 0,
30
+ id: 'message_step',
31
+ type: StepTypes.MESSAGE_CREATION,
32
+ index: 0,
33
+ stepDetails: {
34
+ type: StepTypes.MESSAGE_CREATION,
35
+ message_creation: { message_id: 'message_step' },
36
+ },
37
+ usage: null,
38
+ } satisfies t.RunStep
39
+ );
40
+ await handlerResult.handlers[GraphEvents.ON_MESSAGE_DELTA].handle(
41
+ GraphEvents.ON_MESSAGE_DELTA,
42
+ {
43
+ id: 'message_step',
44
+ delta: { content: [{ type: 'text', text: 'hello' }] },
45
+ } satisfies t.MessageDeltaEvent
46
+ );
47
+
48
+ expect(liveEvents.map((event) => event.type)).toEqual([
49
+ 'run.started',
50
+ 'message.delta',
51
+ ]);
52
+ expect(liveEventCountSeenByUserHandler).toBe(2);
53
+ expect(handlerResult.contentParts[0]).toEqual({
54
+ type: 'text',
55
+ text: 'hello',
56
+ });
57
+ });
58
+
59
+ it('composes adapter and host handlers in order for the same graph event', async () => {
60
+ const calls: string[] = [];
61
+ const handlers = composeEventHandlers(
62
+ {
63
+ [GraphEvents.ON_MESSAGE_DELTA]: {
64
+ handle: (): void => {
65
+ calls.push('adapter');
66
+ },
67
+ },
68
+ },
69
+ {
70
+ [GraphEvents.ON_MESSAGE_DELTA]: {
71
+ handle: (): void => {
72
+ calls.push('host');
73
+ },
74
+ },
75
+ }
76
+ );
77
+
78
+ await handlers[GraphEvents.ON_MESSAGE_DELTA].handle(
79
+ GraphEvents.ON_MESSAGE_DELTA,
80
+ {
81
+ id: 'message_step',
82
+ delta: { content: [{ type: 'text', text: 'hello' }] },
83
+ } satisfies t.MessageDeltaEvent
84
+ );
85
+
86
+ expect(calls).toEqual(['adapter', 'host']);
87
+ });
88
+
89
+ it('accumulates partial usage metadata without corrupting totals', async () => {
90
+ jest.spyOn(console, 'warn').mockImplementation(() => undefined);
91
+ const handlerResult = createRunHandlers({
92
+ runId: 'run_usage',
93
+ threadId: 'thread_usage',
94
+ });
95
+
96
+ await handlerResult.handlers[GraphEvents.CHAT_MODEL_END].handle(
97
+ GraphEvents.CHAT_MODEL_END,
98
+ {
99
+ output: { usage_metadata: { input_tokens: 4 } },
100
+ } as t.ModelEndData
101
+ );
102
+ await handlerResult.handlers[GraphEvents.CHAT_MODEL_END].handle(
103
+ GraphEvents.CHAT_MODEL_END,
104
+ {
105
+ output: { usage_metadata: { output_tokens: 6 } },
106
+ } as t.ModelEndData
107
+ );
108
+
109
+ expect(handlerResult.usage).toEqual({
110
+ inputTokens: 4,
111
+ outputTokens: 6,
112
+ totalTokens: 10,
113
+ });
114
+ });
115
+
116
+ it('does not emit tool completion events for summary completions', async () => {
117
+ const liveEvents: AgentSessionStreamEvent[] = [];
118
+ const handlerResult = createRunHandlers({
119
+ runId: 'run_summary',
120
+ threadId: 'thread_summary',
121
+ onEvent: (event) => {
122
+ liveEvents.push(event);
123
+ },
124
+ });
125
+ const summary: t.SummaryContentBlock = {
126
+ type: ContentTypes.SUMMARY,
127
+ content: [{ type: ContentTypes.TEXT, text: 'summary text' }],
128
+ tokenCount: 4,
129
+ };
130
+
131
+ await handlerResult.handlers[GraphEvents.ON_RUN_STEP].handle(
132
+ GraphEvents.ON_RUN_STEP,
133
+ {
134
+ stepIndex: 0,
135
+ id: 'summary_step',
136
+ type: StepTypes.MESSAGE_CREATION,
137
+ index: 0,
138
+ stepDetails: {
139
+ type: StepTypes.MESSAGE_CREATION,
140
+ message_creation: { message_id: 'summary_step' },
141
+ },
142
+ summary,
143
+ usage: null,
144
+ } satisfies t.RunStep
145
+ );
146
+ await handlerResult.handlers[GraphEvents.ON_RUN_STEP_COMPLETED].handle(
147
+ GraphEvents.ON_RUN_STEP_COMPLETED,
148
+ {
149
+ result: {
150
+ id: 'summary_step',
151
+ index: 0,
152
+ type: 'summary',
153
+ summary,
154
+ },
155
+ }
156
+ );
157
+
158
+ expect(liveEvents.map((event) => event.type)).toEqual(['run.started']);
159
+ expect(handlerResult.contentParts[0]).toBe(summary);
160
+ });
161
+ });
@@ -0,0 +1,272 @@
1
+ import type { UsageMetadata } from '@langchain/core/messages';
2
+ import { GraphEvents } from '@/common';
3
+ import { ModelEndHandler, ToolEndHandler } from '@/events';
4
+ import { createContentAggregator } from '@/stream';
5
+ import type * as t from '@/types';
6
+ import type {
7
+ AgentSessionHandlersResult,
8
+ AgentSessionStreamEvent,
9
+ AgentSessionUsage,
10
+ } from './types';
11
+ import { createTimestamp } from './ids';
12
+ import { toJsonValue } from './messageSerialization';
13
+
14
+ type CompletedRunStepResult =
15
+ | t.ToolEndEvent
16
+ | (t.SummaryCompleted & { id: string; index: number });
17
+
18
+ function isToolCompletion(
19
+ result: CompletedRunStepResult
20
+ ): result is t.ToolEndEvent {
21
+ return 'tool_call' in result;
22
+ }
23
+
24
+ function createEventFactory(params: {
25
+ runId: string;
26
+ threadId: string;
27
+ }): (
28
+ type: AgentSessionStreamEvent['type'],
29
+ data?: unknown
30
+ ) => AgentSessionStreamEvent {
31
+ let sequence = 0;
32
+ return (type, data) => ({
33
+ type,
34
+ sequence: sequence++,
35
+ runId: params.runId,
36
+ threadId: params.threadId,
37
+ timestamp: createTimestamp(),
38
+ ...(typeof data !== 'undefined' ? { data: toJsonValue(data) } : {}),
39
+ });
40
+ }
41
+
42
+ function getTokenCount(value: number | null | undefined): number {
43
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
44
+ }
45
+
46
+ function updateUsage(usage: AgentSessionUsage, data: t.ModelEndData): void {
47
+ const metadata = data?.output?.usage_metadata as
48
+ | Partial<UsageMetadata>
49
+ | undefined;
50
+ if (!metadata) {
51
+ return;
52
+ }
53
+ const inputTokens = getTokenCount(metadata.input_tokens);
54
+ const outputTokens = getTokenCount(metadata.output_tokens);
55
+ const totalTokens =
56
+ metadata.total_tokens == null
57
+ ? inputTokens + outputTokens
58
+ : getTokenCount(metadata.total_tokens);
59
+ usage.inputTokens += inputTokens;
60
+ usage.outputTokens += outputTokens;
61
+ usage.totalTokens += totalTokens;
62
+ }
63
+
64
+ async function callUserHandler(params: {
65
+ userHandlers?: Record<string, t.EventHandler>;
66
+ event: string;
67
+ data: Parameters<t.EventHandler['handle']>[1];
68
+ metadata?: Record<string, unknown>;
69
+ graph?: Parameters<t.EventHandler['handle']>[3];
70
+ }): Promise<void> {
71
+ const handler = params.userHandlers?.[params.event];
72
+ if (!handler) {
73
+ return;
74
+ }
75
+ await handler.handle(
76
+ params.event,
77
+ params.data,
78
+ params.metadata,
79
+ params.graph
80
+ );
81
+ }
82
+
83
+ export function createRunHandlers(params: {
84
+ runId: string;
85
+ threadId: string;
86
+ userHandlers?: Record<string, t.EventHandler>;
87
+ onEvent?: (event: AgentSessionStreamEvent) => void;
88
+ }): AgentSessionHandlersResult {
89
+ const { contentParts, aggregateContent } = createContentAggregator();
90
+ const steps: t.RunStep[] = [];
91
+ const usage: AgentSessionUsage = {
92
+ inputTokens: 0,
93
+ outputTokens: 0,
94
+ totalTokens: 0,
95
+ };
96
+ const events: AgentSessionStreamEvent[] = [];
97
+ const createEvent = createEventFactory({
98
+ runId: params.runId,
99
+ threadId: params.threadId,
100
+ });
101
+ const emitEvent = (event: AgentSessionStreamEvent): void => {
102
+ events.push(event);
103
+ params.onEvent?.(event);
104
+ };
105
+ const toolEndHandler = new ToolEndHandler();
106
+ const modelEndHandler = new ModelEndHandler();
107
+
108
+ emitEvent(createEvent('run.started'));
109
+
110
+ const handlers: Record<string, t.EventHandler> = {
111
+ [GraphEvents.CHAT_MODEL_STREAM]: {
112
+ handle: async (event, data, metadata, graph): Promise<void> => {
113
+ await callUserHandler({
114
+ userHandlers: params.userHandlers,
115
+ event,
116
+ data,
117
+ metadata,
118
+ graph,
119
+ });
120
+ },
121
+ },
122
+ [GraphEvents.CHAT_MODEL_END]: {
123
+ handle: async (event, data, metadata, graph): Promise<void> => {
124
+ await modelEndHandler.handle(
125
+ event,
126
+ data as t.ModelEndData,
127
+ metadata,
128
+ graph
129
+ );
130
+ updateUsage(usage, data as t.ModelEndData);
131
+ emitEvent(createEvent('usage.updated', usage));
132
+ await callUserHandler({
133
+ userHandlers: params.userHandlers,
134
+ event,
135
+ data,
136
+ metadata,
137
+ graph,
138
+ });
139
+ },
140
+ },
141
+ [GraphEvents.TOOL_END]: {
142
+ handle: async (event, data, metadata, graph): Promise<void> => {
143
+ await toolEndHandler.handle(
144
+ event,
145
+ data as t.StreamEventData,
146
+ metadata,
147
+ graph
148
+ );
149
+ await callUserHandler({
150
+ userHandlers: params.userHandlers,
151
+ event,
152
+ data,
153
+ metadata,
154
+ graph,
155
+ });
156
+ },
157
+ },
158
+ [GraphEvents.ON_RUN_STEP]: {
159
+ handle: async (event, data, metadata, graph): Promise<void> => {
160
+ const runStep = data as t.RunStep;
161
+ steps.push(runStep);
162
+ aggregateContent({ event: GraphEvents.ON_RUN_STEP, data: runStep });
163
+ if (runStep.stepDetails.type === 'tool_calls') {
164
+ emitEvent(createEvent('tool.started', runStep));
165
+ }
166
+ await callUserHandler({
167
+ userHandlers: params.userHandlers,
168
+ event,
169
+ data,
170
+ metadata,
171
+ graph,
172
+ });
173
+ },
174
+ },
175
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
176
+ handle: async (event, data, metadata, graph): Promise<void> => {
177
+ const delta = data as t.RunStepDeltaEvent;
178
+ aggregateContent({ event: GraphEvents.ON_RUN_STEP_DELTA, data: delta });
179
+ emitEvent(createEvent('tool.delta', delta));
180
+ await callUserHandler({
181
+ userHandlers: params.userHandlers,
182
+ event,
183
+ data,
184
+ metadata,
185
+ graph,
186
+ });
187
+ },
188
+ },
189
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
190
+ handle: async (event, data, metadata, graph): Promise<void> => {
191
+ const completed = data as unknown as { result: CompletedRunStepResult };
192
+ aggregateContent({
193
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
194
+ data: completed as { result: t.ToolEndEvent },
195
+ });
196
+ if (isToolCompletion(completed.result)) {
197
+ emitEvent(createEvent('tool.completed', completed));
198
+ }
199
+ await callUserHandler({
200
+ userHandlers: params.userHandlers,
201
+ event,
202
+ data,
203
+ metadata,
204
+ graph,
205
+ });
206
+ },
207
+ },
208
+ [GraphEvents.ON_MESSAGE_DELTA]: {
209
+ handle: async (event, data, metadata, graph): Promise<void> => {
210
+ const delta = data as t.MessageDeltaEvent;
211
+ aggregateContent({ event: GraphEvents.ON_MESSAGE_DELTA, data: delta });
212
+ emitEvent(createEvent('message.delta', delta));
213
+ await callUserHandler({
214
+ userHandlers: params.userHandlers,
215
+ event,
216
+ data,
217
+ metadata,
218
+ graph,
219
+ });
220
+ },
221
+ },
222
+ [GraphEvents.ON_REASONING_DELTA]: {
223
+ handle: async (event, data, metadata, graph): Promise<void> => {
224
+ const delta = data as t.ReasoningDeltaEvent;
225
+ aggregateContent({
226
+ event: GraphEvents.ON_REASONING_DELTA,
227
+ data: delta,
228
+ });
229
+ emitEvent(createEvent('reasoning.delta', delta));
230
+ await callUserHandler({
231
+ userHandlers: params.userHandlers,
232
+ event,
233
+ data,
234
+ metadata,
235
+ graph,
236
+ });
237
+ },
238
+ },
239
+ [GraphEvents.ON_SUMMARIZE_DELTA]: {
240
+ handle: async (event, data, metadata, graph): Promise<void> => {
241
+ aggregateContent({
242
+ event: GraphEvents.ON_SUMMARIZE_DELTA,
243
+ data: data as t.SummarizeDeltaData,
244
+ });
245
+ await callUserHandler({
246
+ userHandlers: params.userHandlers,
247
+ event,
248
+ data,
249
+ metadata,
250
+ graph,
251
+ });
252
+ },
253
+ },
254
+ [GraphEvents.ON_SUMMARIZE_COMPLETE]: {
255
+ handle: async (event, data, metadata, graph): Promise<void> => {
256
+ aggregateContent({
257
+ event: GraphEvents.ON_SUMMARIZE_COMPLETE,
258
+ data: data as t.SummarizeCompleteEvent,
259
+ });
260
+ await callUserHandler({
261
+ userHandlers: params.userHandlers,
262
+ event,
263
+ data,
264
+ metadata,
265
+ graph,
266
+ });
267
+ },
268
+ },
269
+ };
270
+
271
+ return { contentParts, steps, usage, events, handlers };
272
+ }
@@ -0,0 +1,17 @@
1
+ import { randomUUID } from 'crypto';
2
+
3
+ export function createSessionId(): string {
4
+ return randomUUID();
5
+ }
6
+
7
+ export function createEntryId(): string {
8
+ return randomUUID().replace(/-/g, '').slice(0, 12);
9
+ }
10
+
11
+ export function createRunId(): string {
12
+ return randomUUID();
13
+ }
14
+
15
+ export function createTimestamp(): string {
16
+ return new Date().toISOString();
17
+ }
@@ -0,0 +1,44 @@
1
+ export { AgentSession, createAgentSession } from './AgentSession';
2
+ export { JsonlSessionStore, SessionManager } from './JsonlSessionStore';
3
+ export { createRunHandlers } from './handlers';
4
+ export {
5
+ serializeMessage,
6
+ deserializeMessage,
7
+ extractTextFromContent,
8
+ } from './messageSerialization';
9
+ export type {
10
+ AgentSessionConfig,
11
+ AgentSessionCheckpointLookupOptions,
12
+ AgentSessionCheckpointReference,
13
+ AgentSessionCheckpointing,
14
+ AgentSessionCheckpointingOptions,
15
+ AgentSessionHandlersResult,
16
+ AgentSessionInput,
17
+ AgentSessionRunOptions,
18
+ AgentSessionRunResult,
19
+ AgentSessionStream,
20
+ AgentSessionStreamEvent,
21
+ AgentSessionUsage,
22
+ CreateSessionFileOptions,
23
+ JsonObject,
24
+ JsonPrimitive,
25
+ JsonValue,
26
+ SerializedSessionMessage,
27
+ SessionBranchOptions,
28
+ SessionCompactOptions,
29
+ SessionCheckpointEntry,
30
+ SessionCompactionEntry,
31
+ SessionEntry,
32
+ SessionEntryBase,
33
+ SessionEntryType,
34
+ SessionForkOptions,
35
+ SessionHeader,
36
+ SessionLabelEntry,
37
+ SessionListItem,
38
+ SessionMessageEntry,
39
+ SessionPosition,
40
+ SessionRunEventEntry,
41
+ SessionStateEntry,
42
+ SessionSummaryEntry,
43
+ SessionTreeNode,
44
+ } from './types';
@@ -0,0 +1,207 @@
1
+ import {
2
+ AIMessage,
3
+ HumanMessage,
4
+ RemoveMessage,
5
+ SystemMessage,
6
+ ToolMessage,
7
+ BaseMessage,
8
+ } from '@langchain/core/messages';
9
+ import type { ToolCall } from '@langchain/core/messages/tool';
10
+ import type { UsageMetadata } from '@langchain/core/messages';
11
+ import type { JsonObject, JsonValue, SerializedSessionMessage } from './types';
12
+
13
+ type MessageExtras = {
14
+ id?: string;
15
+ name?: string;
16
+ tool_call_id?: string;
17
+ tool_calls?: ToolCall[];
18
+ usage_metadata?: UsageMetadata;
19
+ additional_kwargs?: unknown;
20
+ response_metadata?: unknown;
21
+ };
22
+
23
+ const CIRCULAR_REFERENCE = '[Circular]';
24
+
25
+ function isJsonPrimitive(
26
+ value: unknown
27
+ ): value is string | number | boolean | null {
28
+ return (
29
+ value == null ||
30
+ typeof value === 'string' ||
31
+ typeof value === 'number' ||
32
+ typeof value === 'boolean'
33
+ );
34
+ }
35
+
36
+ function toJsonValueInternal(value: unknown, seen: WeakSet<object>): JsonValue {
37
+ if (isJsonPrimitive(value)) {
38
+ return Number.isNaN(value) ? null : value;
39
+ }
40
+ if (Array.isArray(value)) {
41
+ if (seen.has(value)) {
42
+ return CIRCULAR_REFERENCE;
43
+ }
44
+ seen.add(value);
45
+ const result = value.map((item) => toJsonValueInternal(item, seen));
46
+ seen.delete(value);
47
+ return result;
48
+ }
49
+ if (value instanceof Error) {
50
+ if (seen.has(value)) {
51
+ return CIRCULAR_REFERENCE;
52
+ }
53
+ seen.add(value);
54
+ const result: JsonObject = {
55
+ name: value.name,
56
+ message: value.message,
57
+ };
58
+ if (value.stack != null && value.stack !== '') {
59
+ result.stack = value.stack;
60
+ }
61
+ if ('cause' in value && typeof value.cause !== 'undefined') {
62
+ result.cause = toJsonValueInternal(value.cause, seen);
63
+ }
64
+ for (const [key, nested] of Object.entries(value)) {
65
+ if (typeof nested !== 'undefined') {
66
+ result[key] = toJsonValueInternal(nested, seen);
67
+ }
68
+ }
69
+ seen.delete(value);
70
+ return result;
71
+ }
72
+ if (value !== null && typeof value === 'object') {
73
+ if (seen.has(value)) {
74
+ return CIRCULAR_REFERENCE;
75
+ }
76
+ seen.add(value);
77
+ const result: JsonObject = {};
78
+ for (const [key, nested] of Object.entries(value)) {
79
+ if (typeof nested !== 'undefined') {
80
+ result[key] = toJsonValueInternal(nested, seen);
81
+ }
82
+ }
83
+ seen.delete(value);
84
+ return result;
85
+ }
86
+ return String(value);
87
+ }
88
+
89
+ export function toJsonValue(value: unknown): JsonValue {
90
+ return toJsonValueInternal(value, new WeakSet<object>());
91
+ }
92
+
93
+ function toJsonObject(value: unknown): JsonObject | undefined {
94
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
95
+ return undefined;
96
+ }
97
+ return toJsonValue(value) as JsonObject;
98
+ }
99
+
100
+ function fromJsonValue(value: JsonValue): unknown {
101
+ return value;
102
+ }
103
+
104
+ export function serializeMessage(
105
+ message: BaseMessage
106
+ ): SerializedSessionMessage {
107
+ const extras = message as BaseMessage & MessageExtras;
108
+ const serialized: SerializedSessionMessage = {
109
+ messageType: message._getType(),
110
+ content: toJsonValue(message.content),
111
+ };
112
+ const additionalKwargs = toJsonObject(extras.additional_kwargs);
113
+ const responseMetadata = toJsonObject(extras.response_metadata);
114
+ const usageMetadata = toJsonObject(extras.usage_metadata);
115
+ if (additionalKwargs) {
116
+ serialized.additionalKwargs = additionalKwargs;
117
+ }
118
+ if (responseMetadata) {
119
+ serialized.responseMetadata = responseMetadata;
120
+ }
121
+ if (usageMetadata) {
122
+ serialized.usageMetadata = usageMetadata;
123
+ }
124
+ if (extras.id != null && extras.id !== '') {
125
+ serialized.id = extras.id;
126
+ }
127
+ if (extras.name != null && extras.name !== '') {
128
+ serialized.name = extras.name;
129
+ }
130
+ if (extras.tool_call_id != null && extras.tool_call_id !== '') {
131
+ serialized.toolCallId = extras.tool_call_id;
132
+ }
133
+ if (extras.tool_calls) {
134
+ serialized.toolCalls = toJsonValue(extras.tool_calls);
135
+ }
136
+ return serialized;
137
+ }
138
+
139
+ export function deserializeMessage(
140
+ serialized: SerializedSessionMessage
141
+ ): BaseMessage {
142
+ const common = {
143
+ content: fromJsonValue(serialized.content) as BaseMessage['content'],
144
+ additional_kwargs: serialized.additionalKwargs,
145
+ response_metadata: serialized.responseMetadata,
146
+ id: serialized.id,
147
+ name: serialized.name,
148
+ };
149
+ if (serialized.messageType === 'human') {
150
+ return new HumanMessage(common);
151
+ }
152
+ if (serialized.messageType === 'ai') {
153
+ return new AIMessage({
154
+ ...common,
155
+ tool_calls: serialized.toolCalls as ToolCall[] | undefined,
156
+ usage_metadata: serialized.usageMetadata as UsageMetadata | undefined,
157
+ });
158
+ }
159
+ if (serialized.messageType === 'system') {
160
+ return new SystemMessage(common);
161
+ }
162
+ if (serialized.messageType === 'tool') {
163
+ return new ToolMessage({
164
+ ...common,
165
+ tool_call_id: serialized.toolCallId ?? '',
166
+ });
167
+ }
168
+ if (serialized.messageType === 'remove') {
169
+ return new RemoveMessage({
170
+ additional_kwargs: serialized.additionalKwargs,
171
+ response_metadata: serialized.responseMetadata,
172
+ id: serialized.id ?? '',
173
+ name: serialized.name,
174
+ });
175
+ }
176
+ return new HumanMessage(common);
177
+ }
178
+
179
+ export function getMessageRole(message: BaseMessage): string {
180
+ const type = message._getType();
181
+ if (type === 'human') {
182
+ return 'user';
183
+ }
184
+ if (type === 'ai') {
185
+ return 'assistant';
186
+ }
187
+ return type;
188
+ }
189
+
190
+ export function extractTextFromContent(content: JsonValue): string {
191
+ if (typeof content === 'string') {
192
+ return content;
193
+ }
194
+ if (!Array.isArray(content)) {
195
+ return '';
196
+ }
197
+ const chunks: string[] = [];
198
+ for (const part of content) {
199
+ if (part != null && typeof part === 'object' && !Array.isArray(part)) {
200
+ const text = part.text;
201
+ if (typeof text === 'string') {
202
+ chunks.push(text);
203
+ }
204
+ }
205
+ }
206
+ return chunks.join('');
207
+ }