@copilotkitnext/core 1.54.0 → 1.54.1-next.0
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.cjs +1287 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +482 -15
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +482 -15
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1280 -196
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1378 -204
- package/dist/index.umd.js.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,33 +1,511 @@
|
|
|
1
|
-
import { AbstractAgent, EventType, HttpAgent, runHttpRequest, transformHttpEventStream } from "@ag-ui/client";
|
|
2
|
-
import {
|
|
3
|
-
import { EMPTY, Observable } from "rxjs";
|
|
4
|
-
import { catchError } from "rxjs/operators";
|
|
5
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
1
|
+
import { AbstractAgent, EventType, HttpAgent, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
|
|
2
|
+
import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, schemaToJsonSchema } from "@copilotkitnext/shared";
|
|
3
|
+
import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, of, switchMap, throwError } from "rxjs";
|
|
4
|
+
import { catchError, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
|
|
6
5
|
import { Socket } from "phoenix";
|
|
6
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
7
|
+
import { fromFetch } from "rxjs/fetch";
|
|
7
8
|
|
|
8
|
-
//#region src/
|
|
9
|
+
//#region src/utils/phoenix-observable.ts
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Adapt Phoenix socket open/error callbacks into an observable signal stream.
|
|
12
|
+
*
|
|
13
|
+
* The returned observable is shared and replayable by the caller when needed,
|
|
14
|
+
* but this helper itself does not own socket connection teardown.
|
|
12
15
|
*/
|
|
13
|
-
function
|
|
14
|
-
return
|
|
16
|
+
function ɵcreatePhoenixSocketSignals$(socket) {
|
|
17
|
+
return new Observable((observer) => {
|
|
18
|
+
socket.onOpen(() => observer.next({ type: "open" }));
|
|
19
|
+
socket.onError((error) => observer.next({
|
|
20
|
+
type: "error",
|
|
21
|
+
error
|
|
22
|
+
}));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Adapt a Phoenix channel join attempt into a single-outcome observable.
|
|
27
|
+
*/
|
|
28
|
+
function ɵcreatePhoenixJoinOutcome$(channel) {
|
|
29
|
+
return new Observable((observer) => {
|
|
30
|
+
channel.join().receive("ok", () => {
|
|
31
|
+
observer.next({ type: "joined" });
|
|
32
|
+
observer.complete();
|
|
33
|
+
}).receive("error", (response) => {
|
|
34
|
+
observer.next({
|
|
35
|
+
type: "error",
|
|
36
|
+
response
|
|
37
|
+
});
|
|
38
|
+
observer.complete();
|
|
39
|
+
}).receive("timeout", () => {
|
|
40
|
+
observer.next({ type: "timeout" });
|
|
41
|
+
observer.complete();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a cold Phoenix socket session.
|
|
47
|
+
*
|
|
48
|
+
* The socket is constructed and connected on subscription, and disconnected on
|
|
49
|
+
* teardown. Each subscription creates an isolated socket instance.
|
|
50
|
+
*/
|
|
51
|
+
function ɵphoenixSocket$(options) {
|
|
52
|
+
return defer(() => {
|
|
53
|
+
const socket = new Socket(options.url, options.options);
|
|
54
|
+
const signals$ = ɵcreatePhoenixSocketSignals$(socket).pipe(shareReplay({
|
|
55
|
+
bufferSize: 1,
|
|
56
|
+
refCount: true
|
|
57
|
+
}));
|
|
58
|
+
socket.connect();
|
|
59
|
+
return concat(of({
|
|
60
|
+
socket,
|
|
61
|
+
signals$
|
|
62
|
+
}), NEVER).pipe(finalize(() => socket.disconnect()));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a cold Phoenix channel session from a socket session stream.
|
|
67
|
+
*
|
|
68
|
+
* A channel is created and joined for each active socket session. If the
|
|
69
|
+
* upstream socket session changes, the previous channel is left before the
|
|
70
|
+
* next one becomes active.
|
|
71
|
+
*/
|
|
72
|
+
function ɵphoenixChannel$(options) {
|
|
73
|
+
return options.socket$.pipe(switchMap$1(({ socket }) => defer(() => {
|
|
74
|
+
const channel = socket.channel(options.topic, options.params);
|
|
75
|
+
return concat(of({
|
|
76
|
+
channel,
|
|
77
|
+
joinOutcome$: ɵcreatePhoenixJoinOutcome$(channel).pipe(shareReplay({
|
|
78
|
+
bufferSize: 1,
|
|
79
|
+
refCount: true
|
|
80
|
+
}))
|
|
81
|
+
}), NEVER).pipe(finalize(() => {
|
|
82
|
+
if (options.leaveOnUnsubscribe !== false) channel.leave();
|
|
83
|
+
}));
|
|
84
|
+
})));
|
|
15
85
|
}
|
|
16
86
|
/**
|
|
17
|
-
*
|
|
18
|
-
* These errors are expected when the connection is cancelled mid-stream.
|
|
87
|
+
* Observe a named Phoenix channel event as an observable payload stream.
|
|
19
88
|
*/
|
|
89
|
+
function ɵobservePhoenixEvent$(channel, eventName) {
|
|
90
|
+
return new Observable((observer) => {
|
|
91
|
+
const ref = channel.on(eventName, (payload) => observer.next(payload));
|
|
92
|
+
return () => {
|
|
93
|
+
channel.off(eventName, ref);
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Flatten channel sessions into their join-outcome stream.
|
|
99
|
+
*/
|
|
100
|
+
function ɵobservePhoenixJoinOutcome$(channel$) {
|
|
101
|
+
return channel$.pipe(switchMap$1((session) => session.joinOutcome$));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Complete when a channel joins successfully, or error if the join fails.
|
|
105
|
+
*/
|
|
106
|
+
function ɵjoinPhoenixChannel$(channel$) {
|
|
107
|
+
return ɵobservePhoenixJoinOutcome$(channel$).pipe(take(1), mergeMap((outcome) => {
|
|
108
|
+
if (outcome.type === "joined") return EMPTY;
|
|
109
|
+
throw outcome.type === "timeout" ? /* @__PURE__ */ new Error("Timed out joining channel") : /* @__PURE__ */ new Error(`Failed to join channel: ${JSON.stringify(outcome.response)}`);
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Flatten socket sessions into their lifecycle signal stream.
|
|
114
|
+
*/
|
|
115
|
+
function ɵobservePhoenixSocketSignals$(socket$) {
|
|
116
|
+
return socket$.pipe(switchMap$1((session) => session.signals$));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Error after a socket emits the configured number of consecutive error
|
|
120
|
+
* signals, resetting the counter after each successful open signal.
|
|
121
|
+
*/
|
|
122
|
+
function ɵobservePhoenixSocketHealth$(socketSignals$, maxConsecutiveErrors) {
|
|
123
|
+
return socketSignals$.pipe(scan((consecutiveErrors, signal) => signal.type === "open" ? 0 : consecutiveErrors + 1, 0), filter((consecutiveErrors) => consecutiveErrors >= maxConsecutiveErrors), take(1), mergeMap((consecutiveErrors) => throwError(() => /* @__PURE__ */ new Error(`WebSocket connection failed after ${consecutiveErrors} consecutive errors`))));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/intelligence-agent.ts
|
|
128
|
+
const CLIENT_AG_UI_EVENT = "ag_ui_event";
|
|
129
|
+
const STOP_RUN_EVENT = "stop_run";
|
|
130
|
+
var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
131
|
+
config;
|
|
132
|
+
socket = null;
|
|
133
|
+
activeChannel = null;
|
|
134
|
+
runId = null;
|
|
135
|
+
sharedState;
|
|
136
|
+
constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
|
|
137
|
+
super();
|
|
138
|
+
this.config = config;
|
|
139
|
+
this.sharedState = sharedState;
|
|
140
|
+
}
|
|
141
|
+
clone() {
|
|
142
|
+
return new IntelligenceAgent(this.config, this.sharedState);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Override of AbstractAgent.connectAgent that removes the `verifyEvents` step.
|
|
146
|
+
*
|
|
147
|
+
* Background: AbstractAgent's connectAgent pipeline runs events through
|
|
148
|
+
* `verifyEvents`, which validates that the stream follows the AG-UI protocol
|
|
149
|
+
* lifecycle — specifically, it expects a RUN_STARTED event before any content
|
|
150
|
+
* events and a RUN_FINISHED/RUN_ERROR event to complete the stream.
|
|
151
|
+
*
|
|
152
|
+
* IntelligenceAgent uses long-lived WebSocket connections rather than
|
|
153
|
+
* request-scoped SSE streams. When connecting to replay historical messages
|
|
154
|
+
* for an existing thread, the connection semantics don't map to a single
|
|
155
|
+
* agent run start/stop cycle. The replayed events may not include
|
|
156
|
+
* RUN_STARTED/RUN_FINISHED bookends (or may contain events from multiple
|
|
157
|
+
* past runs), which causes verifyEvents to either never complete or to
|
|
158
|
+
* error out.
|
|
159
|
+
*
|
|
160
|
+
* This override replicates the base connectAgent implementation exactly,
|
|
161
|
+
* substituting only `transformChunks` (which is still needed for message
|
|
162
|
+
* reassembly) and omitting `verifyEvents`.
|
|
163
|
+
*
|
|
164
|
+
* TODO: Remove this override once AG-UI's AbstractAgent supports opting out
|
|
165
|
+
* of verifyEvents for transports with different connection life-cycles.
|
|
166
|
+
*/
|
|
167
|
+
async connectAgent(parameters, subscriber) {
|
|
168
|
+
const self = this;
|
|
169
|
+
try {
|
|
170
|
+
this.isRunning = true;
|
|
171
|
+
this.agentId = this.agentId ?? randomUUID();
|
|
172
|
+
const input = this.prepareRunAgentInput(parameters);
|
|
173
|
+
let result;
|
|
174
|
+
const previousMessageIds = new Set(this.messages.map((m) => m.id));
|
|
175
|
+
const subscribers = [
|
|
176
|
+
{ onRunFinishedEvent: (event) => {
|
|
177
|
+
result = event.result;
|
|
178
|
+
} },
|
|
179
|
+
...this.subscribers,
|
|
180
|
+
subscriber ?? {}
|
|
181
|
+
];
|
|
182
|
+
await this.onInitialize(input, subscribers);
|
|
183
|
+
self.activeRunDetach$ = new Subject();
|
|
184
|
+
let resolveCompletion;
|
|
185
|
+
self.activeRunCompletionPromise = new Promise((resolve) => {
|
|
186
|
+
resolveCompletion = resolve;
|
|
187
|
+
});
|
|
188
|
+
const source$ = defer(() => this.connect(input)).pipe(transformChunks(this.debug), takeUntil(self.activeRunDetach$));
|
|
189
|
+
const applied$ = this.apply(input, source$, subscribers);
|
|
190
|
+
await lastValueFrom(this.processApplyEvents(input, applied$, subscribers).pipe(catchError((error) => {
|
|
191
|
+
this.isRunning = false;
|
|
192
|
+
return this.onError(input, error, subscribers);
|
|
193
|
+
}), finalize(() => {
|
|
194
|
+
this.isRunning = false;
|
|
195
|
+
this.onFinalize(input, subscribers);
|
|
196
|
+
resolveCompletion?.();
|
|
197
|
+
resolveCompletion = void 0;
|
|
198
|
+
self.activeRunCompletionPromise = void 0;
|
|
199
|
+
self.activeRunDetach$ = void 0;
|
|
200
|
+
})), { defaultValue: void 0 });
|
|
201
|
+
const newMessages = structuredClone_(this.messages).filter((m) => !previousMessageIds.has(m.id));
|
|
202
|
+
return {
|
|
203
|
+
result,
|
|
204
|
+
newMessages
|
|
205
|
+
};
|
|
206
|
+
} finally {
|
|
207
|
+
this.isRunning = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
abortRun() {
|
|
211
|
+
if (this.activeChannel && this.runId) {
|
|
212
|
+
const fallback = setTimeout(() => clear(), 5e3);
|
|
213
|
+
const clear = () => {
|
|
214
|
+
clearTimeout(fallback);
|
|
215
|
+
this.detachActiveRun();
|
|
216
|
+
this.cleanup();
|
|
217
|
+
};
|
|
218
|
+
this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.runId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
|
|
219
|
+
} else {
|
|
220
|
+
this.detachActiveRun();
|
|
221
|
+
this.cleanup();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Trigger the run via REST, then join the realtime thread channel and relay
|
|
226
|
+
* server-pushed AG-UI events to the Observable subscriber.
|
|
227
|
+
*/
|
|
228
|
+
run(input) {
|
|
229
|
+
this.threadId = input.threadId;
|
|
230
|
+
this.runId = input.runId;
|
|
231
|
+
return defer(() => this.requestJoinCredentials$("run", input)).pipe(switchMap((credentials) => this.observeThread$(input, credentials, {
|
|
232
|
+
completeOnRunError: false,
|
|
233
|
+
streamMode: "run"
|
|
234
|
+
})));
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Reconnect to an existing thread by fetching websocket credentials and
|
|
238
|
+
* joining the realtime thread channel.
|
|
239
|
+
*/
|
|
240
|
+
connect(input) {
|
|
241
|
+
this.threadId = input.threadId;
|
|
242
|
+
this.runId = input.runId;
|
|
243
|
+
return defer(() => this.requestConnectPlan$(input)).pipe(switchMap((plan) => {
|
|
244
|
+
if (plan === null) return EMPTY;
|
|
245
|
+
if (plan.mode === "bootstrap") {
|
|
246
|
+
this.setLastSeenEventId(input.threadId, plan.latestEventId);
|
|
247
|
+
for (const event of plan.events) this.updateRunIdFromEvent(event);
|
|
248
|
+
return from(plan.events);
|
|
249
|
+
}
|
|
250
|
+
this.setLastSeenEventId(input.threadId, plan.joinFromEventId);
|
|
251
|
+
for (const event of plan.events) this.updateRunIdFromEvent(event);
|
|
252
|
+
return concat(from(plan.events), this.observeThread$(input, { joinToken: plan.joinToken }, {
|
|
253
|
+
completeOnRunError: true,
|
|
254
|
+
streamMode: "connect",
|
|
255
|
+
replayCursor: plan.joinFromEventId
|
|
256
|
+
}));
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Tear down a specific channel + socket pair that belongs to one pipeline.
|
|
261
|
+
* Only nulls instance references when they still point to the owned resource,
|
|
262
|
+
* so a concurrent pipeline's resources are never clobbered.
|
|
263
|
+
*/
|
|
264
|
+
cleanupOwned(ownChannel, ownSocket) {
|
|
265
|
+
if (ownChannel) {
|
|
266
|
+
ownChannel.leave();
|
|
267
|
+
if (this.activeChannel === ownChannel) this.activeChannel = null;
|
|
268
|
+
}
|
|
269
|
+
if (ownSocket) {
|
|
270
|
+
ownSocket.disconnect();
|
|
271
|
+
if (this.socket === ownSocket) this.socket = null;
|
|
272
|
+
}
|
|
273
|
+
if (this.threadId) this.sharedState.lastSeenEventIds.delete(this.threadId);
|
|
274
|
+
this.runId = null;
|
|
275
|
+
}
|
|
276
|
+
cleanup() {
|
|
277
|
+
this.cleanupOwned(this.activeChannel, this.socket);
|
|
278
|
+
}
|
|
279
|
+
requestJoinCredentials$(mode, input) {
|
|
280
|
+
return defer(async () => {
|
|
281
|
+
try {
|
|
282
|
+
const response = await fetch(this.buildRuntimeUrl(mode), {
|
|
283
|
+
method: "POST",
|
|
284
|
+
headers: {
|
|
285
|
+
"Content-Type": "application/json",
|
|
286
|
+
...this.config.headers
|
|
287
|
+
},
|
|
288
|
+
body: JSON.stringify({
|
|
289
|
+
threadId: input.threadId,
|
|
290
|
+
runId: input.runId,
|
|
291
|
+
messages: input.messages,
|
|
292
|
+
tools: input.tools,
|
|
293
|
+
context: input.context,
|
|
294
|
+
state: input.state,
|
|
295
|
+
forwardedProps: input.forwardedProps
|
|
296
|
+
}),
|
|
297
|
+
...this.config.credentials ? { credentials: this.config.credentials } : {}
|
|
298
|
+
});
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
const text = await response.text().catch(() => "");
|
|
301
|
+
throw new Error(text || response.statusText || String(response.status));
|
|
302
|
+
}
|
|
303
|
+
const payload = await response.json();
|
|
304
|
+
if (!payload.joinToken) throw new Error("missing joinToken");
|
|
305
|
+
return { joinToken: payload.joinToken };
|
|
306
|
+
} catch (error) {
|
|
307
|
+
throw new Error(`REST ${mode} request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
requestConnectPlan$(input) {
|
|
312
|
+
return defer(async () => {
|
|
313
|
+
try {
|
|
314
|
+
const response = await fetch(this.buildRuntimeUrl("connect"), {
|
|
315
|
+
method: "POST",
|
|
316
|
+
headers: {
|
|
317
|
+
"Content-Type": "application/json",
|
|
318
|
+
...this.config.headers
|
|
319
|
+
},
|
|
320
|
+
body: JSON.stringify({
|
|
321
|
+
threadId: input.threadId,
|
|
322
|
+
runId: input.runId,
|
|
323
|
+
messages: input.messages,
|
|
324
|
+
tools: input.tools,
|
|
325
|
+
context: input.context,
|
|
326
|
+
state: input.state,
|
|
327
|
+
forwardedProps: input.forwardedProps,
|
|
328
|
+
lastSeenEventId: this.getReconnectCursor(input)
|
|
329
|
+
}),
|
|
330
|
+
...this.config.credentials ? { credentials: this.config.credentials } : {}
|
|
331
|
+
});
|
|
332
|
+
if (response.status === 204) return null;
|
|
333
|
+
if (!response.ok) {
|
|
334
|
+
const text = await response.text().catch(() => "");
|
|
335
|
+
throw new Error(text || response.statusText || String(response.status));
|
|
336
|
+
}
|
|
337
|
+
return this.normalizeConnectPlan(await response.json());
|
|
338
|
+
} catch (error) {
|
|
339
|
+
throw new Error(`REST connect request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
normalizeConnectPlan(payload) {
|
|
344
|
+
const envelope = payload && typeof payload === "object" ? payload : null;
|
|
345
|
+
if (envelope?.mode === "bootstrap") return {
|
|
346
|
+
mode: "bootstrap",
|
|
347
|
+
latestEventId: typeof envelope.latestEventId === "string" ? envelope.latestEventId : null,
|
|
348
|
+
events: Array.isArray(envelope.events) ? envelope.events : []
|
|
349
|
+
};
|
|
350
|
+
if (envelope?.mode === "live") {
|
|
351
|
+
if (typeof envelope.joinToken !== "string" || envelope.joinToken.length === 0) throw new Error("missing joinToken");
|
|
352
|
+
return {
|
|
353
|
+
mode: "live",
|
|
354
|
+
joinToken: envelope.joinToken,
|
|
355
|
+
joinFromEventId: typeof envelope.joinFromEventId === "string" ? envelope.joinFromEventId : null,
|
|
356
|
+
events: Array.isArray(envelope.events) ? envelope.events : []
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
throw new Error("invalid connect plan");
|
|
360
|
+
}
|
|
361
|
+
observeThread$(input, credentials, options) {
|
|
362
|
+
return defer(() => {
|
|
363
|
+
let ownSocket = null;
|
|
364
|
+
let ownChannel = null;
|
|
365
|
+
const socket$ = ɵphoenixSocket$({
|
|
366
|
+
url: this.config.url,
|
|
367
|
+
options: {
|
|
368
|
+
params: {
|
|
369
|
+
...this.config.socketParams ?? {},
|
|
370
|
+
join_token: credentials.joinToken
|
|
371
|
+
},
|
|
372
|
+
reconnectAfterMs: phoenixExponentialBackoff(100, 1e4),
|
|
373
|
+
rejoinAfterMs: phoenixExponentialBackoff(1e3, 3e4)
|
|
374
|
+
}
|
|
375
|
+
}).pipe(tap(({ socket }) => {
|
|
376
|
+
ownSocket = socket;
|
|
377
|
+
this.socket = ownSocket;
|
|
378
|
+
}), shareReplay({
|
|
379
|
+
bufferSize: 1,
|
|
380
|
+
refCount: true
|
|
381
|
+
}));
|
|
382
|
+
const { topic, params } = this.createThreadChannelDescriptor(input, options.streamMode, options.replayCursor);
|
|
383
|
+
const channel$ = ɵphoenixChannel$({
|
|
384
|
+
socket$,
|
|
385
|
+
topic,
|
|
386
|
+
params
|
|
387
|
+
}).pipe(tap(({ channel }) => {
|
|
388
|
+
ownChannel = channel;
|
|
389
|
+
this.activeChannel = ownChannel;
|
|
390
|
+
}), shareReplay({
|
|
391
|
+
bufferSize: 1,
|
|
392
|
+
refCount: true
|
|
393
|
+
}));
|
|
394
|
+
const threadEvents$ = this.observeThreadEvents$(input.threadId, channel$, options).pipe(share());
|
|
395
|
+
const threadCompleted$ = threadEvents$.pipe(ignoreElements(), endWith(null), take(1));
|
|
396
|
+
return merge(this.joinThreadChannel$(channel$), this.observeSocketHealth$(socket$).pipe(takeUntil(threadCompleted$)), threadEvents$).pipe(finalize(() => this.cleanupOwned(ownChannel, ownSocket)));
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
joinThreadChannel$(channel$) {
|
|
400
|
+
return ɵjoinPhoenixChannel$(channel$);
|
|
401
|
+
}
|
|
402
|
+
observeSocketHealth$(socket$) {
|
|
403
|
+
return ɵobservePhoenixSocketHealth$(ɵobservePhoenixSocketSignals$(socket$), 5);
|
|
404
|
+
}
|
|
405
|
+
observeThreadEvents$(threadId, channel$, options) {
|
|
406
|
+
return channel$.pipe(switchMap$1(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), tap((payload) => {
|
|
407
|
+
this.updateLastSeenEventId(threadId, payload);
|
|
408
|
+
this.updateRunIdFromEvent(payload);
|
|
409
|
+
}), mergeMap((payload) => from(this.createThreadNotifications(payload, options.completeOnRunError))), dematerialize());
|
|
410
|
+
}
|
|
411
|
+
observeChannelEvent$(channel, eventName) {
|
|
412
|
+
return ɵobservePhoenixEvent$(channel, eventName);
|
|
413
|
+
}
|
|
414
|
+
createThreadNotifications(payload, completeOnRunError) {
|
|
415
|
+
if (payload.type === EventType.RUN_FINISHED) return [Notification.createNext(payload), Notification.createComplete()];
|
|
416
|
+
if (payload.type === EventType.RUN_ERROR) {
|
|
417
|
+
const errorMessage = payload.message ?? "Run error";
|
|
418
|
+
return completeOnRunError ? [Notification.createNext(payload), Notification.createComplete()] : [Notification.createNext(payload), Notification.createError(new Error(errorMessage))];
|
|
419
|
+
}
|
|
420
|
+
return [Notification.createNext(payload)];
|
|
421
|
+
}
|
|
422
|
+
buildRuntimeUrl(mode) {
|
|
423
|
+
const path = `${this.config.runtimeUrl}/agent/${encodeURIComponent(this.config.agentId)}/${mode}`;
|
|
424
|
+
const origin = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
|
|
425
|
+
return new URL(path, new URL(this.config.runtimeUrl, origin)).toString();
|
|
426
|
+
}
|
|
427
|
+
createThreadChannelDescriptor(input, streamMode, replayCursor) {
|
|
428
|
+
const params = streamMode === "run" ? {
|
|
429
|
+
stream_mode: "run",
|
|
430
|
+
run_id: input.runId
|
|
431
|
+
} : {
|
|
432
|
+
stream_mode: "connect",
|
|
433
|
+
last_seen_event_id: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor
|
|
434
|
+
};
|
|
435
|
+
return {
|
|
436
|
+
topic: `thread:${input.threadId}`,
|
|
437
|
+
params
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
getLastSeenEventId(threadId) {
|
|
441
|
+
return this.sharedState.lastSeenEventIds.get(threadId) ?? null;
|
|
442
|
+
}
|
|
443
|
+
getReconnectCursor(input) {
|
|
444
|
+
return this.hasLocalThreadMessages(input) ? this.getLastSeenEventId(input.threadId) : null;
|
|
445
|
+
}
|
|
446
|
+
hasLocalThreadMessages(input) {
|
|
447
|
+
return Array.isArray(input.messages) && input.messages.length > 0;
|
|
448
|
+
}
|
|
449
|
+
updateLastSeenEventId(threadId, payload) {
|
|
450
|
+
const eventId = this.readEventId(payload);
|
|
451
|
+
if (!eventId) return;
|
|
452
|
+
this.sharedState.lastSeenEventIds.set(threadId, eventId);
|
|
453
|
+
}
|
|
454
|
+
setLastSeenEventId(threadId, eventId) {
|
|
455
|
+
if (!eventId) return;
|
|
456
|
+
this.sharedState.lastSeenEventIds.set(threadId, eventId);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Keep `this.runId` in sync with the backend's actual run ID.
|
|
460
|
+
*
|
|
461
|
+
* During a `connect` (resume) flow the client generates a fresh `runId`
|
|
462
|
+
* via `prepareRunAgentInput`, but the backend is running under its own
|
|
463
|
+
* run ID. If the client later sends `STOP_RUN_EVENT` with the wrong
|
|
464
|
+
* `runId`, the gateway's runner channel will not match it and the agent
|
|
465
|
+
* keeps running. Extracting the run ID from live events fixes this.
|
|
466
|
+
*
|
|
467
|
+
* The runner normalises events to `run_id` (snake_case) before pushing
|
|
468
|
+
* to the gateway, so we check both `runId` and `run_id`.
|
|
469
|
+
*/
|
|
470
|
+
updateRunIdFromEvent(payload) {
|
|
471
|
+
const record = payload;
|
|
472
|
+
const eventRunId = record.runId ?? record.run_id;
|
|
473
|
+
if (typeof eventRunId === "string" && eventRunId.length > 0) this.runId = eventRunId;
|
|
474
|
+
}
|
|
475
|
+
readEventId(payload) {
|
|
476
|
+
const metadata = payload.metadata;
|
|
477
|
+
if (!metadata || typeof metadata !== "object") return null;
|
|
478
|
+
const runnerEventId = metadata.cpki_event_id;
|
|
479
|
+
return typeof runnerEventId === "string" ? runnerEventId : null;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/agent.ts
|
|
485
|
+
function hasHeaders(agent) {
|
|
486
|
+
return "headers" in agent;
|
|
487
|
+
}
|
|
488
|
+
function hasCredentials(agent) {
|
|
489
|
+
return "credentials" in agent;
|
|
490
|
+
}
|
|
491
|
+
function isZodError(error) {
|
|
492
|
+
return error !== null && typeof error === "object" && "name" in error && error.name === "ZodError";
|
|
493
|
+
}
|
|
20
494
|
function withAbortErrorHandling(observable) {
|
|
21
495
|
return observable.pipe(catchError((error) => {
|
|
22
496
|
if (isZodError(error)) return EMPTY;
|
|
23
497
|
throw error;
|
|
24
498
|
}));
|
|
25
499
|
}
|
|
26
|
-
var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
500
|
+
var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAgent {
|
|
27
501
|
runtimeUrl;
|
|
28
502
|
credentials;
|
|
29
503
|
transport;
|
|
30
504
|
singleEndpointUrl;
|
|
505
|
+
runtimeMode;
|
|
506
|
+
intelligence;
|
|
507
|
+
delegate;
|
|
508
|
+
runtimeInfoPromise;
|
|
31
509
|
constructor(config) {
|
|
32
510
|
const normalizedRuntimeUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/$/, "") : void 0;
|
|
33
511
|
const transport = config.transport ?? "rest";
|
|
@@ -40,9 +518,21 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
40
518
|
this.runtimeUrl = normalizedRuntimeUrl ?? config.runtimeUrl;
|
|
41
519
|
this.credentials = config.credentials;
|
|
42
520
|
this.transport = transport;
|
|
521
|
+
this.runtimeMode = config.runtimeMode ?? RUNTIME_MODE_SSE;
|
|
522
|
+
this.intelligence = config.intelligence;
|
|
43
523
|
if (this.transport === "single") this.singleEndpointUrl = this.runtimeUrl;
|
|
44
524
|
}
|
|
525
|
+
async detachActiveRun() {
|
|
526
|
+
if (this.delegate) await this.delegate.detachActiveRun();
|
|
527
|
+
await super.detachActiveRun();
|
|
528
|
+
}
|
|
45
529
|
abortRun() {
|
|
530
|
+
if (this.delegate) {
|
|
531
|
+
this.syncDelegate(this.delegate);
|
|
532
|
+
this.delegate.abortRun();
|
|
533
|
+
this.detachActiveRun();
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
46
536
|
if (!this.agentId || !this.threadId) return;
|
|
47
537
|
if (typeof fetch === "undefined") return;
|
|
48
538
|
if (this.transport === "single") {
|
|
@@ -83,7 +573,52 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
83
573
|
console.error("ProxiedCopilotRuntimeAgent: stop request failed", error);
|
|
84
574
|
});
|
|
85
575
|
}
|
|
576
|
+
async connectAgent(parameters, subscriber) {
|
|
577
|
+
if (this.runtimeMode !== RUNTIME_MODE_INTELLIGENCE) return super.connectAgent(parameters, subscriber);
|
|
578
|
+
if (this.delegate) await this.delegate.detachActiveRun();
|
|
579
|
+
await this.resolveDelegate();
|
|
580
|
+
const delegate = this.delegate;
|
|
581
|
+
const bridgeSub = delegate.subscribe({
|
|
582
|
+
onMessagesChanged: () => {
|
|
583
|
+
this.setMessages([...delegate.messages]);
|
|
584
|
+
},
|
|
585
|
+
onStateChanged: () => {
|
|
586
|
+
this.setState({ ...delegate.state });
|
|
587
|
+
},
|
|
588
|
+
onRunInitialized: () => {
|
|
589
|
+
this.isRunning = true;
|
|
590
|
+
},
|
|
591
|
+
onRunFinalized: () => {
|
|
592
|
+
this.isRunning = false;
|
|
593
|
+
},
|
|
594
|
+
onRunFailed: () => {
|
|
595
|
+
this.isRunning = false;
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
const forwardedSubs = this.subscribers.map((s) => delegate.subscribe(s));
|
|
599
|
+
try {
|
|
600
|
+
const result = await delegate.connectAgent(parameters, subscriber);
|
|
601
|
+
this.setMessages([...delegate.messages]);
|
|
602
|
+
this.setState({ ...delegate.state });
|
|
603
|
+
return result;
|
|
604
|
+
} finally {
|
|
605
|
+
this.isRunning = false;
|
|
606
|
+
bridgeSub.unsubscribe();
|
|
607
|
+
for (const sub of forwardedSubs) sub.unsubscribe();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
86
610
|
connect(input) {
|
|
611
|
+
if (this.runtimeMode === RUNTIME_MODE_INTELLIGENCE) return this.#connectViaDelegate(input);
|
|
612
|
+
return this.#connectViaHttp(input);
|
|
613
|
+
}
|
|
614
|
+
run(input) {
|
|
615
|
+
if (this.runtimeMode === RUNTIME_MODE_INTELLIGENCE) return this.#runViaDelegate(input);
|
|
616
|
+
return this.#runViaHttp(input);
|
|
617
|
+
}
|
|
618
|
+
#connectViaDelegate(input) {
|
|
619
|
+
return defer(() => from(this.resolveDelegate())).pipe(switchMap$1((delegate) => withAbortErrorHandling(delegate.connect(input))));
|
|
620
|
+
}
|
|
621
|
+
#connectViaHttp(input) {
|
|
87
622
|
if (this.transport === "single") {
|
|
88
623
|
if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
|
|
89
624
|
const requestInit = this.createSingleRouteRequestInit(input, "agent/connect", { agentId: this.agentId });
|
|
@@ -91,7 +626,10 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
91
626
|
}
|
|
92
627
|
return withAbortErrorHandling(transformHttpEventStream(runHttpRequest(`${this.runtimeUrl}/agent/${this.agentId}/connect`, this.requestInit(input))));
|
|
93
628
|
}
|
|
94
|
-
|
|
629
|
+
#runViaDelegate(input) {
|
|
630
|
+
return defer(() => from(this.resolveDelegate())).pipe(switchMap$1((delegate) => withAbortErrorHandling(delegate.run(input))));
|
|
631
|
+
}
|
|
632
|
+
#runViaHttp(input) {
|
|
95
633
|
if (this.transport === "single") {
|
|
96
634
|
if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
|
|
97
635
|
const requestInit = this.createSingleRouteRequestInit(input, "agent/run", { agentId: this.agentId });
|
|
@@ -100,13 +638,67 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
100
638
|
return withAbortErrorHandling(super.run(input));
|
|
101
639
|
}
|
|
102
640
|
clone() {
|
|
103
|
-
const cloned =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
641
|
+
const cloned = new ProxiedCopilotRuntimeAgent({
|
|
642
|
+
runtimeUrl: this.runtimeUrl,
|
|
643
|
+
agentId: this.agentId,
|
|
644
|
+
description: this.description,
|
|
645
|
+
headers: { ...this.headers },
|
|
646
|
+
credentials: this.credentials,
|
|
647
|
+
transport: this.transport,
|
|
648
|
+
runtimeMode: this.runtimeMode,
|
|
649
|
+
intelligence: this.intelligence
|
|
650
|
+
});
|
|
651
|
+
cloned.threadId = this.threadId;
|
|
652
|
+
cloned.setState(this.state);
|
|
653
|
+
cloned.setMessages(this.messages);
|
|
654
|
+
if (this.delegate) {
|
|
655
|
+
cloned.delegate = this.delegate.clone();
|
|
656
|
+
cloned.syncDelegate(cloned.delegate);
|
|
657
|
+
}
|
|
108
658
|
return cloned;
|
|
109
659
|
}
|
|
660
|
+
async resolveDelegate() {
|
|
661
|
+
await this.ensureRuntimeMode();
|
|
662
|
+
if (!this.delegate) {
|
|
663
|
+
if (this.runtimeMode !== RUNTIME_MODE_INTELLIGENCE) throw new Error("A delegate is only created for Intelligence mode");
|
|
664
|
+
this.delegate = this.createIntelligenceDelegate();
|
|
665
|
+
}
|
|
666
|
+
this.syncDelegate(this.delegate);
|
|
667
|
+
return this.delegate;
|
|
668
|
+
}
|
|
669
|
+
async ensureRuntimeMode() {
|
|
670
|
+
if (this.runtimeMode !== "pending") return;
|
|
671
|
+
if (!this.runtimeUrl) throw new Error("Runtime URL is not set");
|
|
672
|
+
this.runtimeInfoPromise ??= this.fetchRuntimeInfo().then((runtimeInfo) => {
|
|
673
|
+
this.runtimeMode = runtimeInfo.mode ?? RUNTIME_MODE_SSE;
|
|
674
|
+
this.intelligence = runtimeInfo.intelligence;
|
|
675
|
+
});
|
|
676
|
+
await this.runtimeInfoPromise;
|
|
677
|
+
}
|
|
678
|
+
async fetchRuntimeInfo() {
|
|
679
|
+
const headers = { ...this.headers };
|
|
680
|
+
let init;
|
|
681
|
+
let url;
|
|
682
|
+
if (this.transport === "single") {
|
|
683
|
+
if (!this.singleEndpointUrl) throw new Error("Single endpoint transport requires a runtimeUrl");
|
|
684
|
+
if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
|
685
|
+
url = this.runtimeUrl;
|
|
686
|
+
init = {
|
|
687
|
+
method: "POST",
|
|
688
|
+
body: JSON.stringify({ method: "info" })
|
|
689
|
+
};
|
|
690
|
+
} else {
|
|
691
|
+
url = `${this.runtimeUrl}/info`;
|
|
692
|
+
init = {};
|
|
693
|
+
}
|
|
694
|
+
const response = await fetch(url, {
|
|
695
|
+
...init,
|
|
696
|
+
headers,
|
|
697
|
+
...this.credentials ? { credentials: this.credentials } : {}
|
|
698
|
+
});
|
|
699
|
+
if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
|
|
700
|
+
return await response.json();
|
|
701
|
+
}
|
|
110
702
|
createSingleRouteRequestInit(input, method, params) {
|
|
111
703
|
if (!this.agentId) throw new Error("ProxiedCopilotRuntimeAgent requires agentId to make runtime requests");
|
|
112
704
|
const baseInit = super.requestInit(input);
|
|
@@ -118,7 +710,6 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
118
710
|
originalBody = JSON.parse(baseInit.body);
|
|
119
711
|
} catch (error) {
|
|
120
712
|
console.warn("ProxiedCopilotRuntimeAgent: failed to parse request body for single route transport", error);
|
|
121
|
-
originalBody = void 0;
|
|
122
713
|
}
|
|
123
714
|
const envelope = { method };
|
|
124
715
|
if (params && Object.keys(params).length > 0) envelope.params = params;
|
|
@@ -130,6 +721,25 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
|
|
|
130
721
|
...this.credentials ? { credentials: this.credentials } : {}
|
|
131
722
|
};
|
|
132
723
|
}
|
|
724
|
+
createIntelligenceDelegate() {
|
|
725
|
+
if (!this.runtimeUrl || !this.agentId || !this.intelligence?.wsUrl) throw new Error("Intelligence mode requires runtimeUrl, agentId, and intelligence websocket metadata");
|
|
726
|
+
return new IntelligenceAgent({
|
|
727
|
+
url: this.intelligence.wsUrl,
|
|
728
|
+
runtimeUrl: this.runtimeUrl,
|
|
729
|
+
agentId: this.agentId,
|
|
730
|
+
headers: { ...this.headers },
|
|
731
|
+
credentials: this.credentials
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
syncDelegate(delegate) {
|
|
735
|
+
delegate.agentId = this.agentId;
|
|
736
|
+
delegate.description = this.description;
|
|
737
|
+
delegate.threadId = this.threadId;
|
|
738
|
+
delegate.setMessages(this.messages);
|
|
739
|
+
delegate.setState(this.state);
|
|
740
|
+
if (hasHeaders(delegate)) delegate.headers = { ...this.headers };
|
|
741
|
+
if (hasCredentials(delegate)) delegate.credentials = this.credentials;
|
|
742
|
+
}
|
|
133
743
|
};
|
|
134
744
|
|
|
135
745
|
//#endregion
|
|
@@ -147,6 +757,8 @@ var AgentRegistry = class {
|
|
|
147
757
|
_runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
|
|
148
758
|
_runtimeTransport = "rest";
|
|
149
759
|
_audioFileTranscriptionEnabled = false;
|
|
760
|
+
_runtimeMode = RUNTIME_MODE_SSE;
|
|
761
|
+
_intelligence;
|
|
150
762
|
_a2uiEnabled = false;
|
|
151
763
|
constructor(core) {
|
|
152
764
|
this.core = core;
|
|
@@ -172,6 +784,12 @@ var AgentRegistry = class {
|
|
|
172
784
|
get audioFileTranscriptionEnabled() {
|
|
173
785
|
return this._audioFileTranscriptionEnabled;
|
|
174
786
|
}
|
|
787
|
+
get runtimeMode() {
|
|
788
|
+
return this._runtimeMode;
|
|
789
|
+
}
|
|
790
|
+
get intelligence() {
|
|
791
|
+
return this._intelligence;
|
|
792
|
+
}
|
|
175
793
|
get a2uiEnabled() {
|
|
176
794
|
return this._a2uiEnabled;
|
|
177
795
|
}
|
|
@@ -281,6 +899,8 @@ var AgentRegistry = class {
|
|
|
281
899
|
this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
|
|
282
900
|
this._runtimeVersion = void 0;
|
|
283
901
|
this._audioFileTranscriptionEnabled = false;
|
|
902
|
+
this._runtimeMode = RUNTIME_MODE_SSE;
|
|
903
|
+
this._intelligence = void 0;
|
|
284
904
|
this._a2uiEnabled = false;
|
|
285
905
|
this.remoteAgents = {};
|
|
286
906
|
this._agents = this.localAgents;
|
|
@@ -300,7 +920,9 @@ var AgentRegistry = class {
|
|
|
300
920
|
agentId: id,
|
|
301
921
|
description,
|
|
302
922
|
transport: this._runtimeTransport,
|
|
303
|
-
credentials
|
|
923
|
+
credentials,
|
|
924
|
+
runtimeMode: runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE,
|
|
925
|
+
intelligence: runtimeInfoResponse.intelligence
|
|
304
926
|
});
|
|
305
927
|
this.applyHeadersToAgent(agent);
|
|
306
928
|
return [id, agent];
|
|
@@ -312,6 +934,8 @@ var AgentRegistry = class {
|
|
|
312
934
|
this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connected;
|
|
313
935
|
this._runtimeVersion = version;
|
|
314
936
|
this._audioFileTranscriptionEnabled = runtimeInfoResponse.audioFileTranscriptionEnabled ?? false;
|
|
937
|
+
this._runtimeMode = runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE;
|
|
938
|
+
this._intelligence = runtimeInfoResponse.intelligence;
|
|
315
939
|
this._a2uiEnabled = runtimeInfoResponse.a2uiEnabled ?? false;
|
|
316
940
|
await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connected);
|
|
317
941
|
await this.notifyAgentsChanged();
|
|
@@ -319,6 +943,8 @@ var AgentRegistry = class {
|
|
|
319
943
|
this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Error;
|
|
320
944
|
this._runtimeVersion = void 0;
|
|
321
945
|
this._audioFileTranscriptionEnabled = false;
|
|
946
|
+
this._runtimeMode = RUNTIME_MODE_SSE;
|
|
947
|
+
this._intelligence = void 0;
|
|
322
948
|
this._a2uiEnabled = false;
|
|
323
949
|
this.remoteAgents = {};
|
|
324
950
|
this._agents = this.localAgents;
|
|
@@ -415,7 +1041,7 @@ var ContextStore = class {
|
|
|
415
1041
|
* @returns The ID of the created context entry
|
|
416
1042
|
*/
|
|
417
1043
|
addContext({ description, value }) {
|
|
418
|
-
const id = randomUUID();
|
|
1044
|
+
const id = randomUUID$1();
|
|
419
1045
|
this._context[id] = {
|
|
420
1046
|
description,
|
|
421
1047
|
value
|
|
@@ -458,14 +1084,14 @@ var SuggestionEngine = class {
|
|
|
458
1084
|
* Initialize with suggestion configs
|
|
459
1085
|
*/
|
|
460
1086
|
initialize(suggestionsConfig) {
|
|
461
|
-
for (const config of suggestionsConfig) this._suggestionsConfig[randomUUID()] = config;
|
|
1087
|
+
for (const config of suggestionsConfig) this._suggestionsConfig[randomUUID$1()] = config;
|
|
462
1088
|
}
|
|
463
1089
|
/**
|
|
464
1090
|
* Add a suggestion configuration
|
|
465
1091
|
* @returns The ID of the created config
|
|
466
1092
|
*/
|
|
467
1093
|
addSuggestionsConfig(config) {
|
|
468
|
-
const id = randomUUID();
|
|
1094
|
+
const id = randomUUID$1();
|
|
469
1095
|
this._suggestionsConfig[id] = config;
|
|
470
1096
|
this.notifySuggestionsConfigChanged();
|
|
471
1097
|
return id;
|
|
@@ -490,7 +1116,7 @@ var SuggestionEngine = class {
|
|
|
490
1116
|
for (const config of Object.values(this._suggestionsConfig)) {
|
|
491
1117
|
if (config.consumerAgentId !== void 0 && config.consumerAgentId !== "*" && config.consumerAgentId !== agentId) continue;
|
|
492
1118
|
if (!this.shouldShowSuggestions(config, messageCount)) continue;
|
|
493
|
-
const suggestionId = randomUUID();
|
|
1119
|
+
const suggestionId = randomUUID$1();
|
|
494
1120
|
if (isDynamicSuggestionsConfig(config)) {
|
|
495
1121
|
if (!hasAnySuggestions) {
|
|
496
1122
|
hasAnySuggestions = true;
|
|
@@ -840,6 +1466,7 @@ var RunHandler = class {
|
|
|
840
1466
|
async runAgent({ agent, forwardedProps }) {
|
|
841
1467
|
if (agent.agentId) this._internal.suggestionEngine.clearSuggestions(agent.agentId);
|
|
842
1468
|
if (agent instanceof HttpAgent) agent.headers = { ...this._internal.headers };
|
|
1469
|
+
if (agent.detachActiveRun) agent.detachActiveRun();
|
|
843
1470
|
try {
|
|
844
1471
|
const runAgentResult = await agent.runAgent({
|
|
845
1472
|
forwardedProps: {
|
|
@@ -996,7 +1623,7 @@ var RunHandler = class {
|
|
|
996
1623
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
997
1624
|
if (messageIndex === -1) return false;
|
|
998
1625
|
const toolMessage = {
|
|
999
|
-
id: randomUUID(),
|
|
1626
|
+
id: randomUUID$1(),
|
|
1000
1627
|
role: "tool",
|
|
1001
1628
|
toolCallId: toolCall.id,
|
|
1002
1629
|
content: handlerResult.result
|
|
@@ -1085,7 +1712,7 @@ var RunHandler = class {
|
|
|
1085
1712
|
const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
|
|
1086
1713
|
if (messageIndex === -1) return false;
|
|
1087
1714
|
const toolMessage = {
|
|
1088
|
-
id: randomUUID(),
|
|
1715
|
+
id: randomUUID$1(),
|
|
1089
1716
|
role: "tool",
|
|
1090
1717
|
toolCallId: toolCall.id,
|
|
1091
1718
|
content: toolCallResult
|
|
@@ -1129,9 +1756,9 @@ var RunHandler = class {
|
|
|
1129
1756
|
});
|
|
1130
1757
|
throw error;
|
|
1131
1758
|
}
|
|
1132
|
-
const toolCallId = randomUUID();
|
|
1759
|
+
const toolCallId = randomUUID$1();
|
|
1133
1760
|
const assistantMessage = {
|
|
1134
|
-
id: randomUUID(),
|
|
1761
|
+
id: randomUUID$1(),
|
|
1135
1762
|
role: "assistant",
|
|
1136
1763
|
content: "",
|
|
1137
1764
|
toolCalls: [{
|
|
@@ -1158,7 +1785,7 @@ var RunHandler = class {
|
|
|
1158
1785
|
toolType: "runTool"
|
|
1159
1786
|
});
|
|
1160
1787
|
const toolResultMessage = {
|
|
1161
|
-
id: randomUUID(),
|
|
1788
|
+
id: randomUUID$1(),
|
|
1162
1789
|
role: "tool",
|
|
1163
1790
|
toolCallId,
|
|
1164
1791
|
content: handlerResult.result
|
|
@@ -1169,7 +1796,7 @@ var RunHandler = class {
|
|
|
1169
1796
|
if (!handlerResult.error && followUp !== false) {
|
|
1170
1797
|
if (typeof followUp === "string" && followUp !== "generate") {
|
|
1171
1798
|
const userMessage = {
|
|
1172
|
-
id: randomUUID(),
|
|
1799
|
+
id: randomUUID$1(),
|
|
1173
1800
|
role: "user",
|
|
1174
1801
|
content: followUp
|
|
1175
1802
|
};
|
|
@@ -1560,6 +2187,12 @@ var CopilotKitCore = class {
|
|
|
1560
2187
|
get audioFileTranscriptionEnabled() {
|
|
1561
2188
|
return this.agentRegistry.audioFileTranscriptionEnabled;
|
|
1562
2189
|
}
|
|
2190
|
+
get runtimeMode() {
|
|
2191
|
+
return this.agentRegistry.runtimeMode;
|
|
2192
|
+
}
|
|
2193
|
+
get intelligence() {
|
|
2194
|
+
return this.agentRegistry.intelligence;
|
|
2195
|
+
}
|
|
1563
2196
|
get a2uiEnabled() {
|
|
1564
2197
|
return this.agentRegistry.a2uiEnabled;
|
|
1565
2198
|
}
|
|
@@ -1890,180 +2523,631 @@ function completePartialMarkdown(input) {
|
|
|
1890
2523
|
}
|
|
1891
2524
|
|
|
1892
2525
|
//#endregion
|
|
1893
|
-
//#region src/
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2526
|
+
//#region src/utils/micro-redux.ts
|
|
2527
|
+
const INTERNAL_ACTION_TYPES = {
|
|
2528
|
+
boot: "@@micro-redux/boot",
|
|
2529
|
+
init: "@@micro-redux/init",
|
|
2530
|
+
stop: "@@micro-redux/stop"
|
|
2531
|
+
};
|
|
2532
|
+
const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
|
|
2533
|
+
/**
|
|
2534
|
+
* Builds a typed action creator from a type string and payload factory.
|
|
2535
|
+
*/
|
|
2536
|
+
function createTypedActionCreator(type, factory) {
|
|
2537
|
+
const creator = ((...args) => ({
|
|
2538
|
+
...factory(...args),
|
|
2539
|
+
type
|
|
2540
|
+
}));
|
|
2541
|
+
creator.type = type;
|
|
2542
|
+
creator.match = (action) => action.type === type;
|
|
2543
|
+
return creator;
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Declares a payload-based action config for `createActionGroup`.
|
|
2547
|
+
*
|
|
2548
|
+
* @example
|
|
2549
|
+
* ```ts
|
|
2550
|
+
* const actions = createActionGroup("User", {
|
|
2551
|
+
* loaded: props<{ id: string }>(),
|
|
2552
|
+
* });
|
|
2553
|
+
* ```
|
|
2554
|
+
*/
|
|
2555
|
+
function props() {
|
|
2556
|
+
return { kind: "props" };
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Declares a no-payload action config for `createActionGroup`.
|
|
2560
|
+
*
|
|
2561
|
+
* @example
|
|
2562
|
+
* ```ts
|
|
2563
|
+
* const actions = createActionGroup("User", {
|
|
2564
|
+
* reset: empty(),
|
|
2565
|
+
* });
|
|
2566
|
+
* ```
|
|
2567
|
+
*/
|
|
2568
|
+
function empty() {
|
|
2569
|
+
return { kind: "empty" };
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* Creates a namespaced group of typed action creators.
|
|
2573
|
+
*
|
|
2574
|
+
* Action types are formatted as: `[Source] actionName`.
|
|
2575
|
+
*/
|
|
2576
|
+
function createActionGroup(source, config) {
|
|
2577
|
+
const group = {};
|
|
2578
|
+
for (const eventName of Object.keys(config)) {
|
|
2579
|
+
const eventConfig = config[eventName];
|
|
2580
|
+
if (!eventConfig) continue;
|
|
2581
|
+
const actionType = `[${source}] ${eventName}`;
|
|
2582
|
+
if (eventConfig.kind === "props") {
|
|
2583
|
+
group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2586
|
+
group[eventName] = createTypedActionCreator(actionType, () => ({}));
|
|
1905
2587
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2588
|
+
return group;
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Registers one reducer handler for one or more action creators.
|
|
2592
|
+
*
|
|
2593
|
+
* @throws Error when called without at least one action creator and reducer.
|
|
2594
|
+
*/
|
|
2595
|
+
function on(...args) {
|
|
2596
|
+
if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
|
|
2597
|
+
const reducer = args[args.length - 1];
|
|
2598
|
+
return {
|
|
2599
|
+
creators: args.slice(0, -1),
|
|
2600
|
+
reducer
|
|
2601
|
+
};
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Creates a reducer from an initial state and `on(...)` handler entries.
|
|
2605
|
+
*
|
|
2606
|
+
* Unknown action types return the current state unchanged.
|
|
2607
|
+
*/
|
|
2608
|
+
function createReducer(initialState, ...entries) {
|
|
2609
|
+
const reducerMap = /* @__PURE__ */ new Map();
|
|
2610
|
+
for (const entry of entries) for (const creator of entry.creators) {
|
|
2611
|
+
const handlers = reducerMap.get(creator.type) ?? [];
|
|
2612
|
+
handlers.push(entry.reducer);
|
|
2613
|
+
reducerMap.set(creator.type, handlers);
|
|
2614
|
+
}
|
|
2615
|
+
return (state, action) => {
|
|
2616
|
+
const currentState = state ?? initialState;
|
|
2617
|
+
const handlers = reducerMap.get(action.type);
|
|
2618
|
+
if (!handlers || handlers.length === 0) return currentState;
|
|
2619
|
+
let nextState = currentState;
|
|
2620
|
+
for (const handler of handlers) nextState = handler(nextState, action);
|
|
2621
|
+
return nextState;
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* Creates a selector that caches and reuses the last computed result
|
|
2626
|
+
* when all input references are unchanged.
|
|
2627
|
+
*/
|
|
2628
|
+
function createSelector(...args) {
|
|
2629
|
+
if (args.length === 1) {
|
|
2630
|
+
const projector = args[0];
|
|
2631
|
+
let hasCached = false;
|
|
2632
|
+
let lastState;
|
|
2633
|
+
let lastResult;
|
|
2634
|
+
return (state) => {
|
|
2635
|
+
if (hasCached && state === lastState) return lastResult;
|
|
2636
|
+
lastState = state;
|
|
2637
|
+
lastResult = projector(state);
|
|
2638
|
+
hasCached = true;
|
|
2639
|
+
return lastResult;
|
|
2640
|
+
};
|
|
1919
2641
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2642
|
+
const projector = args[args.length - 1];
|
|
2643
|
+
const selectors = args.slice(0, -1);
|
|
2644
|
+
let hasCached = false;
|
|
2645
|
+
let lastInputs = [];
|
|
2646
|
+
let lastResult;
|
|
2647
|
+
return (state) => {
|
|
2648
|
+
const inputs = selectors.map((selector) => selector(state));
|
|
2649
|
+
if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
|
|
2650
|
+
lastInputs = inputs;
|
|
2651
|
+
lastResult = projector(...inputs);
|
|
2652
|
+
hasCached = true;
|
|
2653
|
+
return lastResult;
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* RxJS operator that maps state emissions through a selector and suppresses
|
|
2658
|
+
* unchanged projected values via reference equality.
|
|
2659
|
+
*/
|
|
2660
|
+
function select(selector) {
|
|
2661
|
+
return (source$) => source$.pipe(map(selector), distinctUntilChanged());
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* RxJS operator that filters an action stream by action creators and narrows
|
|
2665
|
+
* the output action type to the matched creator union.
|
|
2666
|
+
*
|
|
2667
|
+
* @throws Error when called without at least one action creator.
|
|
2668
|
+
*/
|
|
2669
|
+
function ofType(...creators) {
|
|
2670
|
+
if (creators.length === 0) throw new Error("ofType requires at least one action creator");
|
|
2671
|
+
const actionTypes = new Set(creators.map((creator) => creator.type));
|
|
2672
|
+
return (source$) => {
|
|
2673
|
+
return source$.pipe(filter((action) => {
|
|
2674
|
+
return actionTypes.has(action.type);
|
|
2675
|
+
}));
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Creates an effect descriptor consumed by `createStore`.
|
|
2680
|
+
*/
|
|
2681
|
+
function createEffect(factory, options = {}) {
|
|
2682
|
+
if (options.dispatch === false) return {
|
|
2683
|
+
run: factory,
|
|
2684
|
+
dispatch: false
|
|
2685
|
+
};
|
|
2686
|
+
return {
|
|
2687
|
+
run: factory,
|
|
2688
|
+
dispatch: true
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Creates a small observable store with reducer + effects.
|
|
2693
|
+
*
|
|
2694
|
+
* Behavior:
|
|
2695
|
+
* - `init()` starts effects and dispatches `@@micro-redux/init`.
|
|
2696
|
+
* - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
|
|
2697
|
+
* - Effect action observation is scheduled on `asapScheduler` to avoid
|
|
2698
|
+
* synchronous re-entrancy in the effect loop.
|
|
2699
|
+
* - Any effect error triggers fail-fast teardown and errors both `actions$`
|
|
2700
|
+
* and `state$`.
|
|
2701
|
+
*/
|
|
2702
|
+
function createStore(options) {
|
|
2703
|
+
const reducer = options.reducer;
|
|
2704
|
+
const effects = options.effects ?? [];
|
|
2705
|
+
let hasFatalError = false;
|
|
2706
|
+
let isRunning = false;
|
|
2707
|
+
let effectSubscriptions = new Subscription();
|
|
2708
|
+
let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
|
|
2709
|
+
const stateSubject = new BehaviorSubject(currentState);
|
|
2710
|
+
const actionsSubject = new Subject();
|
|
2711
|
+
const dispatchInternal = (action) => {
|
|
2712
|
+
if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
|
|
2713
|
+
currentState = reducer(currentState, action);
|
|
2714
|
+
stateSubject.next(currentState);
|
|
2715
|
+
actionsSubject.next(action);
|
|
2716
|
+
};
|
|
2717
|
+
const failFast = (error) => {
|
|
2718
|
+
if (hasFatalError) return;
|
|
2719
|
+
hasFatalError = true;
|
|
2720
|
+
isRunning = false;
|
|
2721
|
+
effectSubscriptions.unsubscribe();
|
|
2722
|
+
effectSubscriptions = new Subscription();
|
|
2723
|
+
actionsSubject.error(error);
|
|
2724
|
+
stateSubject.error(error);
|
|
2725
|
+
};
|
|
2726
|
+
const startEffects = () => {
|
|
2727
|
+
for (const effect of effects) {
|
|
2728
|
+
const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn(asapScheduler));
|
|
2729
|
+
const state$ = stateSubject.asObservable();
|
|
2730
|
+
if (effect.dispatch) {
|
|
2731
|
+
const subscription = effect.run(scheduledActions$, state$).subscribe({
|
|
2732
|
+
next: (effectAction) => {
|
|
2733
|
+
if (hasFatalError) return;
|
|
2734
|
+
dispatchInternal(effectAction);
|
|
1974
2735
|
},
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
messages: input.messages,
|
|
1979
|
-
tools: input.tools,
|
|
1980
|
-
context: input.context,
|
|
1981
|
-
state: input.state,
|
|
1982
|
-
forwardedProps: input.forwardedProps
|
|
1983
|
-
}),
|
|
1984
|
-
...credentials ? { credentials } : {}
|
|
1985
|
-
}).catch((error) => {
|
|
1986
|
-
observer.error(/* @__PURE__ */ new Error(`REST run request failed: ${error.message ?? error}`));
|
|
1987
|
-
this.cleanup();
|
|
2736
|
+
error: (error) => {
|
|
2737
|
+
failFast(error);
|
|
2738
|
+
}
|
|
1988
2739
|
});
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2740
|
+
effectSubscriptions.add(subscription);
|
|
2741
|
+
continue;
|
|
2742
|
+
}
|
|
2743
|
+
const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
|
|
2744
|
+
failFast(error);
|
|
2745
|
+
} });
|
|
2746
|
+
effectSubscriptions.add(subscription);
|
|
2747
|
+
}
|
|
2748
|
+
};
|
|
2749
|
+
return {
|
|
2750
|
+
dispatch(action) {
|
|
2751
|
+
dispatchInternal(action);
|
|
2752
|
+
},
|
|
2753
|
+
getState() {
|
|
2754
|
+
return currentState;
|
|
2755
|
+
},
|
|
2756
|
+
get state$() {
|
|
2757
|
+
return stateSubject.asObservable();
|
|
2758
|
+
},
|
|
2759
|
+
get actions$() {
|
|
2760
|
+
return actionsSubject.asObservable();
|
|
2761
|
+
},
|
|
2762
|
+
select(selector) {
|
|
2763
|
+
return stateSubject.asObservable().pipe(select(selector));
|
|
2764
|
+
},
|
|
2765
|
+
init() {
|
|
2766
|
+
if (hasFatalError || isRunning) return;
|
|
2767
|
+
isRunning = true;
|
|
2768
|
+
startEffects();
|
|
2769
|
+
if (hasFatalError) return;
|
|
2770
|
+
dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
|
|
2771
|
+
},
|
|
2772
|
+
stop() {
|
|
2773
|
+
if (hasFatalError || !isRunning) return;
|
|
2774
|
+
dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
|
|
2775
|
+
effectSubscriptions.unsubscribe();
|
|
2776
|
+
effectSubscriptions = new Subscription();
|
|
2777
|
+
isRunning = false;
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
//#endregion
|
|
2783
|
+
//#region src/threads.ts
|
|
2784
|
+
const THREADS_CHANNEL_EVENT = "thread_metadata";
|
|
2785
|
+
const THREAD_SUBSCRIBE_PATH = "/threads/subscribe";
|
|
2786
|
+
const MAX_SOCKET_RETRIES = 5;
|
|
2787
|
+
const REQUEST_TIMEOUT_MS = 15e3;
|
|
2788
|
+
const initialThreadState = {
|
|
2789
|
+
threads: [],
|
|
2790
|
+
isLoading: false,
|
|
2791
|
+
error: null,
|
|
2792
|
+
context: null,
|
|
2793
|
+
sessionId: 0,
|
|
2794
|
+
metadataCredentialsRequested: false
|
|
2795
|
+
};
|
|
2796
|
+
const threadAdapterEvents = createActionGroup("Thread Adapter", {
|
|
2797
|
+
started: empty(),
|
|
2798
|
+
stopped: empty(),
|
|
2799
|
+
contextChanged: props(),
|
|
2800
|
+
renameRequested: props(),
|
|
2801
|
+
archiveRequested: props(),
|
|
2802
|
+
deleteRequested: props()
|
|
2803
|
+
});
|
|
2804
|
+
const threadRestEvents = createActionGroup("Thread REST", {
|
|
2805
|
+
listRequested: props(),
|
|
2806
|
+
listSucceeded: props(),
|
|
2807
|
+
listFailed: props(),
|
|
2808
|
+
metadataCredentialsRequested: props(),
|
|
2809
|
+
metadataCredentialsSucceeded: props(),
|
|
2810
|
+
metadataCredentialsFailed: props(),
|
|
2811
|
+
mutationFinished: props()
|
|
2812
|
+
});
|
|
2813
|
+
const threadSocketEvents = createActionGroup("Thread Socket", {
|
|
2814
|
+
opened: props(),
|
|
2815
|
+
errored: props(),
|
|
2816
|
+
joinFailed: props(),
|
|
2817
|
+
joinTimedOut: props(),
|
|
2818
|
+
metadataReceived: props()
|
|
2819
|
+
});
|
|
2820
|
+
const threadDomainEvents = createActionGroup("Thread Domain", {
|
|
2821
|
+
threadUpserted: props(),
|
|
2822
|
+
threadDeleted: props()
|
|
2823
|
+
});
|
|
2824
|
+
function sortThreadsByUpdatedAt(threads) {
|
|
2825
|
+
return [...threads].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
2826
|
+
}
|
|
2827
|
+
function upsertThread(threads, thread) {
|
|
2828
|
+
const existingIndex = threads.findIndex((item) => item.id === thread.id);
|
|
2829
|
+
if (existingIndex === -1) return sortThreadsByUpdatedAt([...threads, thread]);
|
|
2830
|
+
const next = [...threads];
|
|
2831
|
+
next[existingIndex] = thread;
|
|
2832
|
+
return sortThreadsByUpdatedAt(next);
|
|
2833
|
+
}
|
|
2834
|
+
const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
|
|
2835
|
+
...state,
|
|
2836
|
+
context,
|
|
2837
|
+
sessionId: state.sessionId + 1,
|
|
2838
|
+
threads: [],
|
|
2839
|
+
isLoading: Boolean(context),
|
|
2840
|
+
error: null,
|
|
2841
|
+
metadataCredentialsRequested: false
|
|
2842
|
+
})), on(threadAdapterEvents.stopped, (state) => ({
|
|
2843
|
+
...state,
|
|
2844
|
+
threads: [],
|
|
2845
|
+
isLoading: false,
|
|
2846
|
+
error: null,
|
|
2847
|
+
metadataCredentialsRequested: false
|
|
2848
|
+
})), on(threadRestEvents.listRequested, (state, { sessionId }) => {
|
|
2849
|
+
if (sessionId !== state.sessionId || !state.context) return state;
|
|
2850
|
+
return {
|
|
2851
|
+
...state,
|
|
2852
|
+
isLoading: true,
|
|
2853
|
+
error: null
|
|
2854
|
+
};
|
|
2855
|
+
}), on(threadRestEvents.listSucceeded, (state, { sessionId, threads }) => {
|
|
2856
|
+
if (sessionId !== state.sessionId) return state;
|
|
2857
|
+
return {
|
|
2858
|
+
...state,
|
|
2859
|
+
threads: sortThreadsByUpdatedAt(threads),
|
|
2860
|
+
isLoading: false,
|
|
2861
|
+
error: null
|
|
2862
|
+
};
|
|
2863
|
+
}), on(threadRestEvents.listFailed, (state, { sessionId, error }) => {
|
|
2864
|
+
if (sessionId !== state.sessionId) return state;
|
|
2865
|
+
return {
|
|
2866
|
+
...state,
|
|
2867
|
+
isLoading: false,
|
|
2868
|
+
error
|
|
2869
|
+
};
|
|
2870
|
+
}), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId, error }) => {
|
|
2871
|
+
if (sessionId !== state.sessionId) return state;
|
|
2872
|
+
return {
|
|
2873
|
+
...state,
|
|
2874
|
+
error
|
|
2875
|
+
};
|
|
2876
|
+
}), on(threadRestEvents.metadataCredentialsRequested, (state, { sessionId }) => {
|
|
2877
|
+
if (sessionId !== state.sessionId) return state;
|
|
2878
|
+
return {
|
|
2879
|
+
...state,
|
|
2880
|
+
metadataCredentialsRequested: true
|
|
2881
|
+
};
|
|
2882
|
+
}), on(threadRestEvents.mutationFinished, (state, { outcome }) => ({
|
|
2883
|
+
...state,
|
|
2884
|
+
error: outcome.ok ? state.error : outcome.error
|
|
2885
|
+
})), on(threadDomainEvents.threadUpserted, (state, { sessionId, thread }) => {
|
|
2886
|
+
if (sessionId !== state.sessionId) return state;
|
|
2887
|
+
return {
|
|
2888
|
+
...state,
|
|
2889
|
+
threads: upsertThread(state.threads, thread)
|
|
2890
|
+
};
|
|
2891
|
+
}), on(threadDomainEvents.threadDeleted, (state, { sessionId, threadId }) => {
|
|
2892
|
+
if (sessionId !== state.sessionId) return state;
|
|
2893
|
+
return {
|
|
2894
|
+
...state,
|
|
2895
|
+
threads: state.threads.filter((thread) => thread.id !== threadId)
|
|
2896
|
+
};
|
|
2897
|
+
}));
|
|
2898
|
+
const selectThreads = createSelector((state) => state.threads);
|
|
2899
|
+
const selectThreadsIsLoading = createSelector((state) => state.isLoading);
|
|
2900
|
+
const selectThreadsError = createSelector((state) => state.error);
|
|
2901
|
+
let threadRequestId = 0;
|
|
2902
|
+
function createThreadRequestId() {
|
|
2903
|
+
threadRequestId += 1;
|
|
2904
|
+
return `thread-request-${threadRequestId}`;
|
|
2905
|
+
}
|
|
2906
|
+
function createThreadFetchObservable(environment, context, sessionId) {
|
|
2907
|
+
return defer(() => {
|
|
2908
|
+
const params = new URLSearchParams({
|
|
2909
|
+
userId: context.userId,
|
|
2910
|
+
agentId: context.agentId
|
|
1999
2911
|
});
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2912
|
+
return fromFetch(`${context.runtimeUrl}/threads?${params.toString()}`, {
|
|
2913
|
+
selector: (response) => {
|
|
2914
|
+
if (!response.ok) throw new Error(`Failed to fetch threads: ${response.status}`);
|
|
2915
|
+
return response.json();
|
|
2916
|
+
},
|
|
2917
|
+
fetch: environment.fetch,
|
|
2918
|
+
method: "GET",
|
|
2919
|
+
headers: { ...context.headers }
|
|
2920
|
+
}).pipe(timeout({
|
|
2921
|
+
first: REQUEST_TIMEOUT_MS,
|
|
2922
|
+
with: () => {
|
|
2923
|
+
throw new Error("Request timed out");
|
|
2924
|
+
}
|
|
2925
|
+
}), map((data) => threadRestEvents.listSucceeded({
|
|
2926
|
+
sessionId,
|
|
2927
|
+
threads: data.threads
|
|
2928
|
+
})), catchError((error) => {
|
|
2929
|
+
return of(threadRestEvents.listFailed({
|
|
2930
|
+
sessionId,
|
|
2931
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
2932
|
+
}));
|
|
2933
|
+
}));
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
function createThreadMetadataCredentialsObservable(environment, context, sessionId) {
|
|
2937
|
+
return defer(() => {
|
|
2938
|
+
return fromFetch(`${context.runtimeUrl}${THREAD_SUBSCRIBE_PATH}`, {
|
|
2939
|
+
selector: async (response) => {
|
|
2940
|
+
if (!response.ok) throw new Error(`Failed to fetch thread metadata credentials: ${response.status}`);
|
|
2941
|
+
return response.json();
|
|
2942
|
+
},
|
|
2943
|
+
fetch: environment.fetch,
|
|
2944
|
+
method: "POST",
|
|
2945
|
+
headers: {
|
|
2946
|
+
...context.headers,
|
|
2947
|
+
"Content-Type": "application/json"
|
|
2948
|
+
},
|
|
2949
|
+
body: JSON.stringify({ userId: context.userId })
|
|
2950
|
+
}).pipe(timeout({
|
|
2951
|
+
first: REQUEST_TIMEOUT_MS,
|
|
2952
|
+
with: () => {
|
|
2953
|
+
throw new Error("Request timed out");
|
|
2954
|
+
}
|
|
2955
|
+
}), map((data) => {
|
|
2956
|
+
if (typeof data.joinToken !== "string" || data.joinToken.length === 0) throw new Error("missing joinToken");
|
|
2957
|
+
return threadRestEvents.metadataCredentialsSucceeded({
|
|
2958
|
+
sessionId,
|
|
2959
|
+
joinToken: data.joinToken
|
|
2023
2960
|
});
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2961
|
+
}), catchError((error) => {
|
|
2962
|
+
return of(threadRestEvents.metadataCredentialsFailed({
|
|
2963
|
+
sessionId,
|
|
2964
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
2965
|
+
}));
|
|
2966
|
+
}));
|
|
2967
|
+
});
|
|
2968
|
+
}
|
|
2969
|
+
function createThreadMutationObservable(environment, context, request) {
|
|
2970
|
+
return defer(() => {
|
|
2971
|
+
return fromFetch(`${context.runtimeUrl}${request.path}`, {
|
|
2972
|
+
selector: async (response) => {
|
|
2973
|
+
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
|
|
2974
|
+
return null;
|
|
2975
|
+
},
|
|
2976
|
+
fetch: environment.fetch,
|
|
2977
|
+
method: request.method,
|
|
2978
|
+
headers: {
|
|
2979
|
+
...context.headers,
|
|
2980
|
+
"Content-Type": "application/json"
|
|
2981
|
+
},
|
|
2982
|
+
body: JSON.stringify(request.body)
|
|
2983
|
+
}).pipe(map(() => threadRestEvents.mutationFinished({ outcome: {
|
|
2984
|
+
requestId: request.requestId,
|
|
2985
|
+
ok: true
|
|
2986
|
+
} })), catchError((error) => {
|
|
2987
|
+
return of(threadRestEvents.mutationFinished({ outcome: {
|
|
2988
|
+
requestId: request.requestId,
|
|
2989
|
+
ok: false,
|
|
2990
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
2991
|
+
} }));
|
|
2992
|
+
}));
|
|
2993
|
+
});
|
|
2994
|
+
}
|
|
2995
|
+
function createThreadStore(environment) {
|
|
2996
|
+
const store = createStore({
|
|
2997
|
+
reducer: threadReducer,
|
|
2998
|
+
effects: [
|
|
2999
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => threadRestEvents.listRequested({ sessionId: state.sessionId })))),
|
|
3000
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.listRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
|
|
3001
|
+
action,
|
|
3002
|
+
context
|
|
3003
|
+
})), takeUntil(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createThreadFetchObservable(environment, context, currentAction.sessionId)))))),
|
|
3004
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.listSucceeded), withLatestFrom(state$), filter(([action, state]) => {
|
|
3005
|
+
return action.sessionId === state.sessionId && !state.metadataCredentialsRequested && Boolean(state.context?.wsUrl);
|
|
3006
|
+
}), map(([action]) => threadRestEvents.metadataCredentialsRequested({ sessionId: action.sessionId })))),
|
|
3007
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.metadataCredentialsRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
|
|
3008
|
+
action,
|
|
3009
|
+
context
|
|
3010
|
+
})), takeUntil(actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createThreadMetadataCredentialsObservable(environment, context, currentAction.sessionId)))))),
|
|
3011
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadRestEvents.metadataCredentialsSucceeded), withLatestFrom(state$), filter(([action, state]) => {
|
|
3012
|
+
return action.sessionId === state.sessionId && Boolean(state.context?.wsUrl);
|
|
3013
|
+
}), switchMap$1(([action, state]) => {
|
|
3014
|
+
const context = state.context;
|
|
3015
|
+
const joinToken = action.joinToken;
|
|
3016
|
+
const shutdown$ = actions$.pipe(ofType(threadAdapterEvents.contextChanged, threadAdapterEvents.stopped));
|
|
3017
|
+
return defer(() => {
|
|
3018
|
+
const socket$ = ɵphoenixSocket$({
|
|
3019
|
+
url: context.wsUrl,
|
|
3020
|
+
options: {
|
|
3021
|
+
params: { join_token: joinToken },
|
|
3022
|
+
reconnectAfterMs: phoenixExponentialBackoff(100, 1e4),
|
|
3023
|
+
rejoinAfterMs: phoenixExponentialBackoff(1e3, 3e4)
|
|
3024
|
+
}
|
|
3025
|
+
}).pipe(shareReplay({
|
|
3026
|
+
bufferSize: 1,
|
|
3027
|
+
refCount: true
|
|
3028
|
+
}));
|
|
3029
|
+
const channel$ = ɵphoenixChannel$({
|
|
3030
|
+
socket$,
|
|
3031
|
+
topic: `user_meta:${context.userId}`
|
|
3032
|
+
}).pipe(shareReplay({
|
|
3033
|
+
bufferSize: 1,
|
|
3034
|
+
refCount: true
|
|
3035
|
+
}));
|
|
3036
|
+
const socketSignals$ = ɵobservePhoenixSocketSignals$(socket$).pipe(share());
|
|
3037
|
+
const fatalSocketShutdown$ = ɵobservePhoenixSocketHealth$(socketSignals$, MAX_SOCKET_RETRIES).pipe(catchError(() => {
|
|
3038
|
+
console.warn(`[threads] WebSocket failed after ${MAX_SOCKET_RETRIES} attempts, giving up`);
|
|
3039
|
+
return of(void 0);
|
|
3040
|
+
}), share());
|
|
3041
|
+
return merge(socketSignals$.pipe(map((signal) => signal.type === "open" ? threadSocketEvents.opened({ sessionId: action.sessionId }) : threadSocketEvents.errored({ sessionId: action.sessionId }))), channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, THREADS_CHANNEL_EVENT)), map((payload) => threadSocketEvents.metadataReceived({
|
|
3042
|
+
sessionId: action.sessionId,
|
|
3043
|
+
payload
|
|
3044
|
+
}))), ɵobservePhoenixJoinOutcome$(channel$).pipe(filter((outcome) => outcome.type !== "joined"), map((outcome) => outcome.type === "timeout" ? threadSocketEvents.joinTimedOut({ sessionId: action.sessionId }) : threadSocketEvents.joinFailed({ sessionId: action.sessionId })))).pipe(takeUntil(merge(shutdown$, fatalSocketShutdown$)));
|
|
3045
|
+
});
|
|
3046
|
+
}))),
|
|
3047
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadSocketEvents.metadataReceived), withLatestFrom(state$), filter(([action, state]) => {
|
|
3048
|
+
return action.sessionId === state.sessionId && action.payload.userId === state.context?.userId;
|
|
3049
|
+
}), map(([action]) => {
|
|
3050
|
+
if (action.payload.operation === "deleted") return threadDomainEvents.threadDeleted({
|
|
3051
|
+
sessionId: action.sessionId,
|
|
3052
|
+
threadId: action.payload.deleted.id
|
|
3053
|
+
});
|
|
3054
|
+
return threadDomainEvents.threadUpserted({
|
|
3055
|
+
sessionId: action.sessionId,
|
|
3056
|
+
thread: action.payload.thread
|
|
3057
|
+
});
|
|
3058
|
+
}))),
|
|
3059
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(threadAdapterEvents.renameRequested, threadAdapterEvents.archiveRequested, threadAdapterEvents.deleteRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
|
|
3060
|
+
const context = state.context;
|
|
3061
|
+
if (!context?.runtimeUrl) {
|
|
3062
|
+
const requestId = action.requestId;
|
|
3063
|
+
return of(threadRestEvents.mutationFinished({ outcome: {
|
|
3064
|
+
requestId,
|
|
3065
|
+
ok: false,
|
|
3066
|
+
error: /* @__PURE__ */ new Error("Runtime URL is not configured")
|
|
3067
|
+
} }));
|
|
2031
3068
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
3069
|
+
const commonBody = {
|
|
3070
|
+
userId: context.userId,
|
|
3071
|
+
agentId: context.agentId
|
|
3072
|
+
};
|
|
3073
|
+
if (threadAdapterEvents.renameRequested.match(action)) return createThreadMutationObservable(environment, context, {
|
|
3074
|
+
requestId: action.requestId,
|
|
3075
|
+
method: "PATCH",
|
|
3076
|
+
path: `/threads/${encodeURIComponent(action.threadId)}`,
|
|
3077
|
+
body: {
|
|
3078
|
+
...commonBody,
|
|
3079
|
+
name: action.name
|
|
3080
|
+
}
|
|
2042
3081
|
});
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
3082
|
+
if (threadAdapterEvents.archiveRequested.match(action)) return createThreadMutationObservable(environment, context, {
|
|
3083
|
+
requestId: action.requestId,
|
|
3084
|
+
method: "POST",
|
|
3085
|
+
path: `/threads/${encodeURIComponent(action.threadId)}/archive`,
|
|
3086
|
+
body: commonBody
|
|
3087
|
+
});
|
|
3088
|
+
return createThreadMutationObservable(environment, context, {
|
|
3089
|
+
requestId: action.requestId,
|
|
3090
|
+
method: "DELETE",
|
|
3091
|
+
path: `/threads/${encodeURIComponent(action.threadId)}`,
|
|
3092
|
+
body: commonBody
|
|
3093
|
+
});
|
|
3094
|
+
})))
|
|
3095
|
+
]
|
|
3096
|
+
});
|
|
3097
|
+
function trackMutation(dispatchAction) {
|
|
3098
|
+
const resultPromise = firstValueFrom(merge(store.actions$.pipe(ofType(threadRestEvents.mutationFinished), filter((action) => action.outcome.requestId === dispatchAction.requestId), map((action) => action.outcome)), store.actions$.pipe(ofType(threadAdapterEvents.stopped), map(() => ({
|
|
3099
|
+
requestId: dispatchAction.requestId,
|
|
3100
|
+
ok: false,
|
|
3101
|
+
error: /* @__PURE__ */ new Error("Thread store stopped before mutation completed")
|
|
3102
|
+
})))).pipe(take(1))).then((outcome) => {
|
|
3103
|
+
if (outcome.ok) return;
|
|
3104
|
+
throw outcome.error;
|
|
2053
3105
|
});
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
}
|
|
3106
|
+
store.dispatch(dispatchAction);
|
|
3107
|
+
return resultPromise;
|
|
3108
|
+
}
|
|
3109
|
+
return {
|
|
3110
|
+
start() {
|
|
3111
|
+
store.init();
|
|
3112
|
+
store.dispatch(threadAdapterEvents.started());
|
|
3113
|
+
},
|
|
3114
|
+
stop() {
|
|
3115
|
+
store.dispatch(threadAdapterEvents.stopped());
|
|
3116
|
+
store.stop();
|
|
3117
|
+
},
|
|
3118
|
+
setContext(context) {
|
|
3119
|
+
store.dispatch(threadAdapterEvents.contextChanged({ context }));
|
|
3120
|
+
},
|
|
3121
|
+
renameThread(threadId, name) {
|
|
3122
|
+
return trackMutation(threadAdapterEvents.renameRequested({
|
|
3123
|
+
requestId: createThreadRequestId(),
|
|
3124
|
+
threadId,
|
|
3125
|
+
name
|
|
3126
|
+
}));
|
|
3127
|
+
},
|
|
3128
|
+
archiveThread(threadId) {
|
|
3129
|
+
return trackMutation(threadAdapterEvents.archiveRequested({
|
|
3130
|
+
requestId: createThreadRequestId(),
|
|
3131
|
+
threadId
|
|
3132
|
+
}));
|
|
3133
|
+
},
|
|
3134
|
+
deleteThread(threadId) {
|
|
3135
|
+
return trackMutation(threadAdapterEvents.deleteRequested({
|
|
3136
|
+
requestId: createThreadRequestId(),
|
|
3137
|
+
threadId
|
|
3138
|
+
}));
|
|
3139
|
+
},
|
|
3140
|
+
getState() {
|
|
3141
|
+
return store.getState();
|
|
3142
|
+
},
|
|
3143
|
+
select: store.select.bind(store)
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
const ɵthreadAdapterEvents = threadAdapterEvents;
|
|
3147
|
+
const ɵselectThreads = selectThreads;
|
|
3148
|
+
const ɵselectThreadsIsLoading = selectThreadsIsLoading;
|
|
3149
|
+
const ɵselectThreadsError = selectThreadsError;
|
|
2066
3150
|
|
|
2067
3151
|
//#endregion
|
|
2068
|
-
export { AgentRegistry, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, ensureObjectArgs };
|
|
3152
|
+
export { AgentRegistry, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, ofType, on, props, select, createThreadStore as ɵcreateThreadStore, ɵjoinPhoenixChannel$, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
|
|
2069
3153
|
//# sourceMappingURL=index.mjs.map
|