@copilotkitnext/runtime 0.0.3 → 0.0.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 (40) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +2 -2
  6. package/.turbo/turbo-build.log +0 -23
  7. package/.turbo/turbo-check-types.log +0 -4
  8. package/.turbo/turbo-lint.log +0 -56
  9. package/.turbo/turbo-test$colon$coverage.log +0 -149
  10. package/.turbo/turbo-test.log +0 -108
  11. package/src/__tests__/get-runtime-info.test.ts +0 -117
  12. package/src/__tests__/handle-run.test.ts +0 -69
  13. package/src/__tests__/handle-transcribe.test.ts +0 -289
  14. package/src/__tests__/in-process-agent-runner-messages.test.ts +0 -599
  15. package/src/__tests__/in-process-agent-runner.test.ts +0 -726
  16. package/src/__tests__/middleware.test.ts +0 -432
  17. package/src/__tests__/routing.test.ts +0 -257
  18. package/src/endpoint.ts +0 -150
  19. package/src/handler.ts +0 -3
  20. package/src/handlers/get-runtime-info.ts +0 -50
  21. package/src/handlers/handle-connect.ts +0 -144
  22. package/src/handlers/handle-run.ts +0 -156
  23. package/src/handlers/handle-transcribe.ts +0 -126
  24. package/src/index.ts +0 -8
  25. package/src/middleware.ts +0 -232
  26. package/src/runner/__tests__/enterprise-runner.test.ts +0 -992
  27. package/src/runner/__tests__/event-compaction.test.ts +0 -253
  28. package/src/runner/__tests__/in-memory-runner.test.ts +0 -483
  29. package/src/runner/__tests__/sqlite-runner.test.ts +0 -975
  30. package/src/runner/agent-runner.ts +0 -27
  31. package/src/runner/enterprise.ts +0 -653
  32. package/src/runner/event-compaction.ts +0 -250
  33. package/src/runner/in-memory.ts +0 -328
  34. package/src/runner/index.ts +0 -0
  35. package/src/runner/sqlite.ts +0 -481
  36. package/src/runtime.ts +0 -53
  37. package/src/transcription-service/transcription-service-openai.ts +0 -29
  38. package/src/transcription-service/transcription-service.ts +0 -11
  39. package/tsconfig.json +0 -13
  40. package/tsup.config.ts +0 -11
@@ -1,250 +0,0 @@
1
- import {
2
- BaseEvent,
3
- EventType,
4
- TextMessageStartEvent,
5
- TextMessageContentEvent,
6
- TextMessageEndEvent,
7
- ToolCallStartEvent,
8
- ToolCallArgsEvent,
9
- ToolCallEndEvent,
10
- } from "@ag-ui/client";
11
-
12
- /**
13
- * Compacts streaming events by consolidating multiple deltas into single events.
14
- * For text messages: multiple content deltas become one concatenated delta.
15
- * For tool calls: multiple args deltas become one concatenated delta.
16
- * Events between related streaming events are reordered to keep streaming events together.
17
- *
18
- * @param events - Array of events to compact
19
- * @returns Compacted array of events
20
- */
21
- export function compactEvents(events: BaseEvent[]): BaseEvent[] {
22
- const compacted: BaseEvent[] = [];
23
- const pendingTextMessages = new Map<string, {
24
- start?: TextMessageStartEvent;
25
- contents: TextMessageContentEvent[];
26
- end?: TextMessageEndEvent;
27
- otherEvents: BaseEvent[];
28
- }>();
29
- const pendingToolCalls = new Map<string, {
30
- start?: ToolCallStartEvent;
31
- args: ToolCallArgsEvent[];
32
- end?: ToolCallEndEvent;
33
- otherEvents: BaseEvent[];
34
- }>();
35
-
36
- for (const event of events) {
37
- // Handle text message streaming events
38
- if (event.type === EventType.TEXT_MESSAGE_START) {
39
- const startEvent = event as TextMessageStartEvent;
40
- const messageId = startEvent.messageId;
41
-
42
- if (!pendingTextMessages.has(messageId)) {
43
- pendingTextMessages.set(messageId, {
44
- contents: [],
45
- otherEvents: []
46
- });
47
- }
48
-
49
- const pending = pendingTextMessages.get(messageId)!;
50
- pending.start = startEvent;
51
- } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
52
- const contentEvent = event as TextMessageContentEvent;
53
- const messageId = contentEvent.messageId;
54
-
55
- if (!pendingTextMessages.has(messageId)) {
56
- pendingTextMessages.set(messageId, {
57
- contents: [],
58
- otherEvents: []
59
- });
60
- }
61
-
62
- const pending = pendingTextMessages.get(messageId)!;
63
- pending.contents.push(contentEvent);
64
- } else if (event.type === EventType.TEXT_MESSAGE_END) {
65
- const endEvent = event as TextMessageEndEvent;
66
- const messageId = endEvent.messageId;
67
-
68
- if (!pendingTextMessages.has(messageId)) {
69
- pendingTextMessages.set(messageId, {
70
- contents: [],
71
- otherEvents: []
72
- });
73
- }
74
-
75
- const pending = pendingTextMessages.get(messageId)!;
76
- pending.end = endEvent;
77
-
78
- // Flush this message's events
79
- flushTextMessage(messageId, pending, compacted);
80
- pendingTextMessages.delete(messageId);
81
- } else if (event.type === EventType.TOOL_CALL_START) {
82
- const startEvent = event as ToolCallStartEvent;
83
- const toolCallId = startEvent.toolCallId;
84
-
85
- if (!pendingToolCalls.has(toolCallId)) {
86
- pendingToolCalls.set(toolCallId, {
87
- args: [],
88
- otherEvents: []
89
- });
90
- }
91
-
92
- const pending = pendingToolCalls.get(toolCallId)!;
93
- pending.start = startEvent;
94
- } else if (event.type === EventType.TOOL_CALL_ARGS) {
95
- const argsEvent = event as ToolCallArgsEvent;
96
- const toolCallId = argsEvent.toolCallId;
97
-
98
- if (!pendingToolCalls.has(toolCallId)) {
99
- pendingToolCalls.set(toolCallId, {
100
- args: [],
101
- otherEvents: []
102
- });
103
- }
104
-
105
- const pending = pendingToolCalls.get(toolCallId)!;
106
- pending.args.push(argsEvent);
107
- } else if (event.type === EventType.TOOL_CALL_END) {
108
- const endEvent = event as ToolCallEndEvent;
109
- const toolCallId = endEvent.toolCallId;
110
-
111
- if (!pendingToolCalls.has(toolCallId)) {
112
- pendingToolCalls.set(toolCallId, {
113
- args: [],
114
- otherEvents: []
115
- });
116
- }
117
-
118
- const pending = pendingToolCalls.get(toolCallId)!;
119
- pending.end = endEvent;
120
-
121
- // Flush this tool call's events
122
- flushToolCall(toolCallId, pending, compacted);
123
- pendingToolCalls.delete(toolCallId);
124
- } else {
125
- // For non-streaming events, check if we're in the middle of any streaming sequences
126
- let addedToBuffer = false;
127
-
128
- // Check text messages
129
- for (const [messageId, pending] of pendingTextMessages) {
130
- // If we have a start but no end yet, this event is "in between"
131
- if (pending.start && !pending.end) {
132
- pending.otherEvents.push(event);
133
- addedToBuffer = true;
134
- break;
135
- }
136
- }
137
-
138
- // Check tool calls if not already buffered
139
- if (!addedToBuffer) {
140
- for (const [toolCallId, pending] of pendingToolCalls) {
141
- // If we have a start but no end yet, this event is "in between"
142
- if (pending.start && !pending.end) {
143
- pending.otherEvents.push(event);
144
- addedToBuffer = true;
145
- break;
146
- }
147
- }
148
- }
149
-
150
- // If not in the middle of any streaming sequence, add directly to compacted
151
- if (!addedToBuffer) {
152
- compacted.push(event);
153
- }
154
- }
155
- }
156
-
157
- // Flush any remaining incomplete messages
158
- for (const [messageId, pending] of pendingTextMessages) {
159
- flushTextMessage(messageId, pending, compacted);
160
- }
161
-
162
- // Flush any remaining incomplete tool calls
163
- for (const [toolCallId, pending] of pendingToolCalls) {
164
- flushToolCall(toolCallId, pending, compacted);
165
- }
166
-
167
- return compacted;
168
- }
169
-
170
- function flushTextMessage(
171
- messageId: string,
172
- pending: {
173
- start?: TextMessageStartEvent;
174
- contents: TextMessageContentEvent[];
175
- end?: TextMessageEndEvent;
176
- otherEvents: BaseEvent[];
177
- },
178
- compacted: BaseEvent[]
179
- ): void {
180
- // Add start event if present
181
- if (pending.start) {
182
- compacted.push(pending.start);
183
- }
184
-
185
- // Compact all content events into one
186
- if (pending.contents.length > 0) {
187
- const concatenatedDelta = pending.contents
188
- .map(c => c.delta)
189
- .join('');
190
-
191
- const compactedContent: TextMessageContentEvent = {
192
- type: EventType.TEXT_MESSAGE_CONTENT,
193
- messageId: messageId,
194
- delta: concatenatedDelta,
195
- };
196
-
197
- compacted.push(compactedContent);
198
- }
199
-
200
- // Add end event if present
201
- if (pending.end) {
202
- compacted.push(pending.end);
203
- }
204
-
205
- // Add any events that were in between
206
- for (const otherEvent of pending.otherEvents) {
207
- compacted.push(otherEvent);
208
- }
209
- }
210
-
211
- function flushToolCall(
212
- toolCallId: string,
213
- pending: {
214
- start?: ToolCallStartEvent;
215
- args: ToolCallArgsEvent[];
216
- end?: ToolCallEndEvent;
217
- otherEvents: BaseEvent[];
218
- },
219
- compacted: BaseEvent[]
220
- ): void {
221
- // Add start event if present
222
- if (pending.start) {
223
- compacted.push(pending.start);
224
- }
225
-
226
- // Compact all args events into one
227
- if (pending.args.length > 0) {
228
- const concatenatedArgs = pending.args
229
- .map(a => a.delta)
230
- .join('');
231
-
232
- const compactedArgs: ToolCallArgsEvent = {
233
- type: EventType.TOOL_CALL_ARGS,
234
- toolCallId: toolCallId,
235
- delta: concatenatedArgs,
236
- };
237
-
238
- compacted.push(compactedArgs);
239
- }
240
-
241
- // Add end event if present
242
- if (pending.end) {
243
- compacted.push(pending.end);
244
- }
245
-
246
- // Add any events that were in between
247
- for (const otherEvent of pending.otherEvents) {
248
- compacted.push(otherEvent);
249
- }
250
- }
@@ -1,328 +0,0 @@
1
- import {
2
- AgentRunner,
3
- AgentRunnerConnectRequest,
4
- AgentRunnerIsRunningRequest,
5
- AgentRunnerRunRequest,
6
- type AgentRunnerStopRequest,
7
- } from "./agent-runner";
8
- import { Observable, ReplaySubject } from "rxjs";
9
- import {
10
- BaseEvent,
11
- Message,
12
- EventType,
13
- TextMessageStartEvent,
14
- TextMessageContentEvent,
15
- TextMessageEndEvent,
16
- ToolCallStartEvent,
17
- ToolCallArgsEvent,
18
- ToolCallEndEvent,
19
- ToolCallResultEvent,
20
- MessagesSnapshotEvent,
21
- } from "@ag-ui/client";
22
- import { compactEvents } from "./event-compaction";
23
-
24
- interface HistoricRun {
25
- threadId: string;
26
- runId: string;
27
- parentRunId: string | null;
28
- events: BaseEvent[];
29
- createdAt: number;
30
- }
31
-
32
- class InMemoryEventStore {
33
- constructor(public threadId: string) {}
34
-
35
- /** The subject that current consumers subscribe to. */
36
- subject: ReplaySubject<BaseEvent> | null = null;
37
-
38
- /** True while a run is actively producing events. */
39
- isRunning = false;
40
-
41
- /** Lets stop() cancel the current producer. */
42
- abortController = new AbortController();
43
-
44
- /** Current run ID */
45
- currentRunId: string | null = null;
46
-
47
- /** Historic completed runs */
48
- historicRuns: HistoricRun[] = [];
49
- }
50
-
51
- const GLOBAL_STORE = new Map<string, InMemoryEventStore>();
52
-
53
- export class InMemoryAgentRunner extends AgentRunner {
54
- private convertMessageToEvents(message: Message): BaseEvent[] {
55
- const events: BaseEvent[] = [];
56
-
57
- if (
58
- (message.role === "assistant" ||
59
- message.role === "user" ||
60
- message.role === "developer" ||
61
- message.role === "system") &&
62
- message.content
63
- ) {
64
- const textStartEvent: TextMessageStartEvent = {
65
- type: EventType.TEXT_MESSAGE_START,
66
- messageId: message.id,
67
- role: message.role,
68
- };
69
- events.push(textStartEvent);
70
-
71
- const textContentEvent: TextMessageContentEvent = {
72
- type: EventType.TEXT_MESSAGE_CONTENT,
73
- messageId: message.id,
74
- delta: message.content,
75
- };
76
- events.push(textContentEvent);
77
-
78
- const textEndEvent: TextMessageEndEvent = {
79
- type: EventType.TEXT_MESSAGE_END,
80
- messageId: message.id,
81
- };
82
- events.push(textEndEvent);
83
- }
84
-
85
- if (message.role === "assistant" && message.toolCalls) {
86
- for (const toolCall of message.toolCalls) {
87
- const toolStartEvent: ToolCallStartEvent = {
88
- type: EventType.TOOL_CALL_START,
89
- toolCallId: toolCall.id,
90
- toolCallName: toolCall.function.name,
91
- parentMessageId: message.id,
92
- };
93
- events.push(toolStartEvent);
94
-
95
- const toolArgsEvent: ToolCallArgsEvent = {
96
- type: EventType.TOOL_CALL_ARGS,
97
- toolCallId: toolCall.id,
98
- delta: toolCall.function.arguments,
99
- };
100
- events.push(toolArgsEvent);
101
-
102
- const toolEndEvent: ToolCallEndEvent = {
103
- type: EventType.TOOL_CALL_END,
104
- toolCallId: toolCall.id,
105
- };
106
- events.push(toolEndEvent);
107
- }
108
- }
109
-
110
- if (message.role === "tool" && message.toolCallId) {
111
- const toolResultEvent: ToolCallResultEvent = {
112
- type: EventType.TOOL_CALL_RESULT,
113
- messageId: message.id,
114
- toolCallId: message.toolCallId,
115
- content: message.content,
116
- role: "tool",
117
- };
118
- events.push(toolResultEvent);
119
- }
120
-
121
- return events;
122
- }
123
-
124
- run(request: AgentRunnerRunRequest): Observable<BaseEvent> {
125
- let existingStore = GLOBAL_STORE.get(request.threadId);
126
- if (!existingStore) {
127
- existingStore = new InMemoryEventStore(request.threadId);
128
- GLOBAL_STORE.set(request.threadId, existingStore);
129
- }
130
- const store = existingStore; // Now store is const and non-null
131
-
132
- if (store.isRunning) {
133
- throw new Error("Thread already running");
134
- }
135
- store.isRunning = true;
136
- store.currentRunId = request.input.runId;
137
-
138
- // Track seen message IDs and current run events for this run
139
- const seenMessageIds = new Set<string>();
140
- const currentRunEvents: BaseEvent[] = [];
141
-
142
- // Get all previously seen message IDs from historic runs
143
- const historicMessageIds = new Set<string>();
144
- for (const run of store.historicRuns) {
145
- for (const event of run.events) {
146
- if ("messageId" in event && typeof event.messageId === "string") {
147
- historicMessageIds.add(event.messageId);
148
- }
149
- }
150
- }
151
-
152
- const nextSubject = new ReplaySubject<BaseEvent>(Infinity);
153
- const prevSubject = store.subject;
154
-
155
- // Update the store's subject immediately
156
- store.subject = nextSubject;
157
- store.abortController = new AbortController();
158
-
159
- // Create a subject for run() return value
160
- const runSubject = new ReplaySubject<BaseEvent>(Infinity);
161
-
162
- // Helper function to run the agent and handle errors
163
- const runAgent = async () => {
164
- // Get parent run ID for chaining
165
- const lastRun = store.historicRuns[store.historicRuns.length - 1];
166
- const parentRunId = lastRun?.runId ?? null;
167
-
168
- try {
169
- await request.agent.runAgent(request.input, {
170
- onEvent: ({ event }) => {
171
- runSubject.next(event); // For run() return - only agent events
172
- nextSubject.next(event); // For connect() / store - all events
173
- currentRunEvents.push(event); // Accumulate for storage
174
- },
175
- onNewMessage: ({ message }) => {
176
- // Called for each new message
177
- if (!seenMessageIds.has(message.id)) {
178
- seenMessageIds.add(message.id);
179
- }
180
- },
181
- onRunStartedEvent: () => {
182
- // Process input messages (same logic as SQLite)
183
- if (request.input.messages) {
184
- for (const message of request.input.messages) {
185
- if (!seenMessageIds.has(message.id)) {
186
- seenMessageIds.add(message.id);
187
- const events = this.convertMessageToEvents(message);
188
-
189
- // Check if this message is NEW (not in historic runs)
190
- const isNewMessage = !historicMessageIds.has(message.id);
191
-
192
- for (const event of events) {
193
- // Always emit to stream for context
194
- nextSubject.next(event);
195
-
196
- // Store if this is a NEW message for this run
197
- if (isNewMessage) {
198
- currentRunEvents.push(event);
199
- }
200
- }
201
- }
202
- }
203
- }
204
- },
205
- });
206
-
207
- // Store the completed run in memory with ONLY its events
208
- if (store.currentRunId) {
209
- // Compact the events before storing (like SQLite does)
210
- const compactedEvents = compactEvents(currentRunEvents);
211
-
212
- store.historicRuns.push({
213
- threadId: request.threadId,
214
- runId: store.currentRunId,
215
- parentRunId,
216
- events: compactedEvents,
217
- createdAt: Date.now(),
218
- });
219
- }
220
-
221
- // Complete the run
222
- store.isRunning = false;
223
- store.currentRunId = null;
224
- runSubject.complete();
225
- nextSubject.complete();
226
- } catch {
227
- // Store the run even if it failed (partial events)
228
- if (store.currentRunId && currentRunEvents.length > 0) {
229
- // Compact the events before storing (like SQLite does)
230
- const compactedEvents = compactEvents(currentRunEvents);
231
- store.historicRuns.push({
232
- threadId: request.threadId,
233
- runId: store.currentRunId,
234
- parentRunId,
235
- events: compactedEvents,
236
- createdAt: Date.now(),
237
- });
238
- }
239
-
240
- // Complete the run
241
- store.isRunning = false;
242
- store.currentRunId = null;
243
- runSubject.complete();
244
- nextSubject.complete();
245
- }
246
- };
247
-
248
- // Bridge previous events if they exist
249
- if (prevSubject) {
250
- prevSubject.subscribe({
251
- next: (e) => nextSubject.next(e),
252
- error: (err) => nextSubject.error(err),
253
- complete: () => {
254
- // Don't complete nextSubject here - it needs to stay open for new events
255
- },
256
- });
257
- }
258
-
259
- // Start the agent execution immediately (not lazily)
260
- runAgent();
261
-
262
- // Return the run subject (only agent events, no injected messages)
263
- return runSubject.asObservable();
264
- }
265
-
266
- connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {
267
- const store = GLOBAL_STORE.get(request.threadId);
268
- const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);
269
-
270
- if (!store) {
271
- // No store means no events
272
- connectionSubject.complete();
273
- return connectionSubject.asObservable();
274
- }
275
-
276
- // Collect all historic events from memory
277
- const allHistoricEvents: BaseEvent[] = [];
278
- for (const run of store.historicRuns) {
279
- allHistoricEvents.push(...run.events);
280
- }
281
-
282
- // Apply compaction to all historic events together (like SQLite)
283
- const compactedEvents = compactEvents(allHistoricEvents);
284
-
285
- // Emit compacted events and track message IDs
286
- const emittedMessageIds = new Set<string>();
287
- for (const event of compactedEvents) {
288
- connectionSubject.next(event);
289
- if ("messageId" in event && typeof event.messageId === "string") {
290
- emittedMessageIds.add(event.messageId);
291
- }
292
- }
293
-
294
- // Bridge active run to connection if exists
295
- if (store.subject && store.isRunning) {
296
- store.subject.subscribe({
297
- next: (event) => {
298
- // Skip message events that we've already emitted from historic
299
- if (
300
- "messageId" in event &&
301
- typeof event.messageId === "string" &&
302
- emittedMessageIds.has(event.messageId)
303
- ) {
304
- return;
305
- }
306
- connectionSubject.next(event);
307
- },
308
- complete: () => connectionSubject.complete(),
309
- error: (err) => connectionSubject.error(err),
310
- });
311
- } else {
312
- // No active run, complete after historic events
313
- connectionSubject.complete();
314
- }
315
-
316
- return connectionSubject.asObservable();
317
- }
318
-
319
- isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {
320
- const store = GLOBAL_STORE.get(request.threadId);
321
- return Promise.resolve(store?.isRunning ?? false);
322
- }
323
-
324
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
325
- stop(_request: AgentRunnerStopRequest): Promise<boolean | undefined> {
326
- throw new Error("Method not implemented.");
327
- }
328
- }
File without changes