@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.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/.turbo/turbo-build.log +0 -23
- package/.turbo/turbo-check-types.log +0 -4
- package/.turbo/turbo-lint.log +0 -56
- package/.turbo/turbo-test$colon$coverage.log +0 -149
- package/.turbo/turbo-test.log +0 -108
- package/src/__tests__/get-runtime-info.test.ts +0 -117
- package/src/__tests__/handle-run.test.ts +0 -69
- package/src/__tests__/handle-transcribe.test.ts +0 -289
- package/src/__tests__/in-process-agent-runner-messages.test.ts +0 -599
- package/src/__tests__/in-process-agent-runner.test.ts +0 -726
- package/src/__tests__/middleware.test.ts +0 -432
- package/src/__tests__/routing.test.ts +0 -257
- package/src/endpoint.ts +0 -150
- package/src/handler.ts +0 -3
- package/src/handlers/get-runtime-info.ts +0 -50
- package/src/handlers/handle-connect.ts +0 -144
- package/src/handlers/handle-run.ts +0 -156
- package/src/handlers/handle-transcribe.ts +0 -126
- package/src/index.ts +0 -8
- package/src/middleware.ts +0 -232
- package/src/runner/__tests__/enterprise-runner.test.ts +0 -992
- package/src/runner/__tests__/event-compaction.test.ts +0 -253
- package/src/runner/__tests__/in-memory-runner.test.ts +0 -483
- package/src/runner/__tests__/sqlite-runner.test.ts +0 -975
- package/src/runner/agent-runner.ts +0 -27
- package/src/runner/enterprise.ts +0 -653
- package/src/runner/event-compaction.ts +0 -250
- package/src/runner/in-memory.ts +0 -328
- package/src/runner/index.ts +0 -0
- package/src/runner/sqlite.ts +0 -481
- package/src/runtime.ts +0 -53
- package/src/transcription-service/transcription-service-openai.ts +0 -29
- package/src/transcription-service/transcription-service.ts +0 -11
- package/tsconfig.json +0 -13
- 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
|
-
}
|
package/src/runner/in-memory.ts
DELETED
|
@@ -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
|
-
}
|
package/src/runner/index.ts
DELETED
|
File without changes
|