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