@copilotkit/core 1.56.0 → 1.56.2-canary.pin-to-send
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 +196 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -20
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +84 -20
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +196 -38
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +201 -43
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Throttler } from "@tanstack/pacer";
|
|
1
2
|
import { AbstractAgent, EventType, HttpAgent, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
|
|
2
3
|
import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema } from "@copilotkit/shared";
|
|
3
4
|
import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, of, switchMap, throwError } from "rxjs";
|
|
@@ -137,7 +138,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
137
138
|
config;
|
|
138
139
|
socket = null;
|
|
139
140
|
activeChannel = null;
|
|
140
|
-
|
|
141
|
+
canonicalRunId = null;
|
|
141
142
|
sharedState;
|
|
142
143
|
constructor(config, sharedState = { lastSeenEventIds: /* @__PURE__ */ new Map() }) {
|
|
143
144
|
super();
|
|
@@ -175,7 +176,11 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
175
176
|
try {
|
|
176
177
|
this.isRunning = true;
|
|
177
178
|
this.agentId = this.agentId ?? randomUUID();
|
|
178
|
-
const
|
|
179
|
+
const effectiveParameters = parameters?.runId || !this.canonicalRunId ? parameters : {
|
|
180
|
+
...parameters,
|
|
181
|
+
runId: this.canonicalRunId
|
|
182
|
+
};
|
|
183
|
+
const input = this.prepareRunAgentInput(effectiveParameters);
|
|
179
184
|
let result;
|
|
180
185
|
const previousMessageIds = new Set(this.messages.map((m) => m.id));
|
|
181
186
|
const subscribers = [
|
|
@@ -214,14 +219,14 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
221
|
abortRun() {
|
|
217
|
-
if (this.activeChannel && this.
|
|
222
|
+
if (this.activeChannel && this.canonicalRunId) {
|
|
218
223
|
const fallback = setTimeout(() => clear(), 5e3);
|
|
219
224
|
const clear = () => {
|
|
220
225
|
clearTimeout(fallback);
|
|
221
226
|
this.detachActiveRun();
|
|
222
227
|
this.cleanup();
|
|
223
228
|
};
|
|
224
|
-
this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.
|
|
229
|
+
this.activeChannel.push(STOP_RUN_EVENT, { run_id: this.canonicalRunId }).receive("ok", clear).receive("error", clear).receive("timeout", clear);
|
|
225
230
|
} else {
|
|
226
231
|
this.detachActiveRun();
|
|
227
232
|
this.cleanup();
|
|
@@ -233,7 +238,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
233
238
|
*/
|
|
234
239
|
run(input) {
|
|
235
240
|
this.threadId = input.threadId;
|
|
236
|
-
this.
|
|
241
|
+
this.canonicalRunId = input.runId;
|
|
237
242
|
return defer(() => this.requestJoinCredentials$("run", input)).pipe(switchMap((credentials) => this.observeThread$(input, credentials, {
|
|
238
243
|
completeOnRunError: false,
|
|
239
244
|
streamMode: "run"
|
|
@@ -245,16 +250,14 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
245
250
|
*/
|
|
246
251
|
connect(input) {
|
|
247
252
|
this.threadId = input.threadId;
|
|
248
|
-
this.
|
|
253
|
+
this.canonicalRunId = input.runId;
|
|
249
254
|
return defer(() => this.requestConnectPlan$(input)).pipe(switchMap((plan) => {
|
|
250
255
|
if (plan === null) return EMPTY;
|
|
251
256
|
if (plan.mode === "bootstrap") {
|
|
252
257
|
this.setLastSeenEventId(input.threadId, plan.latestEventId);
|
|
253
|
-
for (const event of plan.events) this.updateRunIdFromEvent(event);
|
|
254
258
|
return from(plan.events);
|
|
255
259
|
}
|
|
256
260
|
this.setLastSeenEventId(input.threadId, plan.joinFromEventId);
|
|
257
|
-
for (const event of plan.events) this.updateRunIdFromEvent(event);
|
|
258
261
|
return concat(from(plan.events), this.observeThread$(input, { joinToken: plan.joinToken }, {
|
|
259
262
|
completeOnRunError: true,
|
|
260
263
|
streamMode: "connect",
|
|
@@ -277,7 +280,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
277
280
|
if (this.socket === ownSocket) this.socket = null;
|
|
278
281
|
}
|
|
279
282
|
if (this.threadId) this.sharedState.lastSeenEventIds.delete(this.threadId);
|
|
280
|
-
this.
|
|
283
|
+
this.canonicalRunId = null;
|
|
281
284
|
}
|
|
282
285
|
cleanup() {
|
|
283
286
|
this.cleanupOwned(this.activeChannel, this.socket);
|
|
@@ -413,7 +416,6 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
413
416
|
observeThreadEvents$(threadId, channel$, options) {
|
|
414
417
|
return channel$.pipe(switchMap$1(({ channel }) => this.observeChannelEvent$(channel, CLIENT_AG_UI_EVENT)), tap((payload) => {
|
|
415
418
|
this.updateLastSeenEventId(threadId, payload);
|
|
416
|
-
this.updateRunIdFromEvent(payload);
|
|
417
419
|
}), mergeMap((payload) => from(this.createThreadNotifications(payload, options.completeOnRunError))), dematerialize());
|
|
418
420
|
}
|
|
419
421
|
observeChannelEvent$(channel, eventName) {
|
|
@@ -463,23 +465,6 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
|
|
|
463
465
|
if (!eventId) return;
|
|
464
466
|
this.sharedState.lastSeenEventIds.set(threadId, eventId);
|
|
465
467
|
}
|
|
466
|
-
/**
|
|
467
|
-
* Keep `this.runId` in sync with the backend's actual run ID.
|
|
468
|
-
*
|
|
469
|
-
* During a `connect` (resume) flow the client generates a fresh `runId`
|
|
470
|
-
* via `prepareRunAgentInput`, but the backend is running under its own
|
|
471
|
-
* run ID. If the client later sends `STOP_RUN_EVENT` with the wrong
|
|
472
|
-
* `runId`, the gateway's runner channel will not match it and the agent
|
|
473
|
-
* keeps running. Extracting the run ID from live events fixes this.
|
|
474
|
-
*
|
|
475
|
-
* The runner normalises events to `run_id` (snake_case) before pushing
|
|
476
|
-
* to the gateway, so we check both `runId` and `run_id`.
|
|
477
|
-
*/
|
|
478
|
-
updateRunIdFromEvent(payload) {
|
|
479
|
-
const record = payload;
|
|
480
|
-
const eventRunId = record.runId ?? record.run_id;
|
|
481
|
-
if (typeof eventRunId === "string" && eventRunId.length > 0) this.runId = eventRunId;
|
|
482
|
-
}
|
|
483
468
|
readEventId(payload) {
|
|
484
469
|
const metadata = payload.metadata;
|
|
485
470
|
if (!metadata || typeof metadata !== "object") return null;
|
|
@@ -857,6 +842,7 @@ var AgentRegistry = class {
|
|
|
857
842
|
initialize(agents) {
|
|
858
843
|
this.localAgents = this.assignAgentIds(agents);
|
|
859
844
|
this.applyHeadersToAgents(this.localAgents);
|
|
845
|
+
this.applyCredentialsToAgents(this.localAgents);
|
|
860
846
|
this._agents = this.localAgents;
|
|
861
847
|
}
|
|
862
848
|
/**
|
|
@@ -886,6 +872,7 @@ var AgentRegistry = class {
|
|
|
886
872
|
...this.remoteAgents
|
|
887
873
|
};
|
|
888
874
|
this.applyHeadersToAgents(this._agents);
|
|
875
|
+
this.applyCredentialsToAgents(this._agents);
|
|
889
876
|
this.notifyAgentsChanged();
|
|
890
877
|
}
|
|
891
878
|
/**
|
|
@@ -895,6 +882,7 @@ var AgentRegistry = class {
|
|
|
895
882
|
this.validateAndAssignAgentId(id, agent);
|
|
896
883
|
this.localAgents[id] = agent;
|
|
897
884
|
this.applyHeadersToAgent(agent);
|
|
885
|
+
this.applyCredentialsToAgent(agent);
|
|
898
886
|
this._agents = {
|
|
899
887
|
...this.localAgents,
|
|
900
888
|
...this.remoteAgents
|
|
@@ -1955,7 +1943,7 @@ var RunHandler = class {
|
|
|
1955
1943
|
* Build frontend tools for an agent
|
|
1956
1944
|
*/
|
|
1957
1945
|
buildFrontendTools(agentId) {
|
|
1958
|
-
return this._tools.filter((tool) => tool.available !== false && (!tool.agentId || tool.agentId === agentId)).map((tool) => ({
|
|
1946
|
+
return this._tools.filter((tool) => tool.available !== false && tool.available !== "disabled" && (!tool.agentId || tool.agentId === agentId)).map((tool) => ({
|
|
1959
1947
|
name: tool.name,
|
|
1960
1948
|
description: tool.description ?? "",
|
|
1961
1949
|
parameters: createToolSchema(tool)
|
|
@@ -2305,8 +2293,21 @@ let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode)
|
|
|
2305
2293
|
CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
|
|
2306
2294
|
CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
|
|
2307
2295
|
CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
|
|
2296
|
+
CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
|
|
2308
2297
|
return CopilotKitCoreErrorCode;
|
|
2309
2298
|
}({});
|
|
2299
|
+
/**
|
|
2300
|
+
* Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
|
|
2301
|
+
* to module scope so the Set is allocated once, not per-subscription.
|
|
2302
|
+
*/
|
|
2303
|
+
const ALLOWED_KEYS = new Set([
|
|
2304
|
+
"onMessagesChanged",
|
|
2305
|
+
"onStateChanged",
|
|
2306
|
+
"onRunInitialized",
|
|
2307
|
+
"onRunFinalized",
|
|
2308
|
+
"onRunFailed",
|
|
2309
|
+
"onRunErrorEvent"
|
|
2310
|
+
]);
|
|
2310
2311
|
let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
|
|
2311
2312
|
CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
|
|
2312
2313
|
CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
|
|
@@ -2372,6 +2373,16 @@ var CopilotKitCore = class {
|
|
|
2372
2373
|
}), "Subscriber onError error:");
|
|
2373
2374
|
}
|
|
2374
2375
|
/**
|
|
2376
|
+
* Log a message to the console and emit an error to subscribers.
|
|
2377
|
+
* Catches failures from `emitError` itself to prevent unhandled rejections.
|
|
2378
|
+
*/
|
|
2379
|
+
logAndEmitError(message, params, logLevel = "error") {
|
|
2380
|
+
console[logLevel](message, params.error);
|
|
2381
|
+
this.emitError(params).catch((emitErr) => {
|
|
2382
|
+
console.error(message + " — emitError itself failed:", emitErr);
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
/**
|
|
2375
2386
|
* Snapshot accessors
|
|
2376
2387
|
*/
|
|
2377
2388
|
get context() {
|
|
@@ -2408,17 +2419,30 @@ var CopilotKitCore = class {
|
|
|
2408
2419
|
return this._properties;
|
|
2409
2420
|
}
|
|
2410
2421
|
/**
|
|
2411
|
-
* Default throttle interval (ms)
|
|
2412
|
-
*
|
|
2413
|
-
* `
|
|
2414
|
-
* or `<CopilotChat>` overrides this default and disables throttling.
|
|
2422
|
+
* Default throttle interval (ms) used by `subscribeToAgentWithOptions()`
|
|
2423
|
+
* when the caller does not specify an explicit `throttleMs`.
|
|
2424
|
+
* `undefined` means no default is configured; `0` means no throttling.
|
|
2415
2425
|
*/
|
|
2416
2426
|
get defaultThrottleMs() {
|
|
2417
2427
|
return this._defaultThrottleMs;
|
|
2418
2428
|
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Set the default throttle interval (ms) for `subscribeToAgentWithOptions()`.
|
|
2431
|
+
*
|
|
2432
|
+
* Accepts a non-negative finite number or `undefined` (to clear the
|
|
2433
|
+
* default). Invalid values (NaN, Infinity, negative) are logged as
|
|
2434
|
+
* errors and ignored — the previous valid value is preserved.
|
|
2435
|
+
*/
|
|
2419
2436
|
setDefaultThrottleMs(value) {
|
|
2420
2437
|
if (value !== void 0 && (!Number.isFinite(value) || value < 0)) {
|
|
2421
|
-
this.
|
|
2438
|
+
this.logAndEmitError(`CopilotKitCore.setDefaultThrottleMs: value must be a non-negative finite number or undefined, got ${value}. Keeping current value (${this._defaultThrottleMs}).`, {
|
|
2439
|
+
error: /* @__PURE__ */ new Error(`setDefaultThrottleMs: invalid value (${value}), keeping current value (${this._defaultThrottleMs})`),
|
|
2440
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2441
|
+
context: {
|
|
2442
|
+
value,
|
|
2443
|
+
currentValue: this._defaultThrottleMs
|
|
2444
|
+
}
|
|
2445
|
+
});
|
|
2422
2446
|
return;
|
|
2423
2447
|
}
|
|
2424
2448
|
this._defaultThrottleMs = value;
|
|
@@ -2539,6 +2563,137 @@ var CopilotKitCore = class {
|
|
|
2539
2563
|
} };
|
|
2540
2564
|
}
|
|
2541
2565
|
/**
|
|
2566
|
+
* Subscribe to an agent's notification and lifecycle events with
|
|
2567
|
+
* optional configuration (e.g. throttling).
|
|
2568
|
+
*
|
|
2569
|
+
* Wraps every callback with error protection (`safeCall`) and applies
|
|
2570
|
+
* the options before delegating to `agent.subscribe()`.
|
|
2571
|
+
*
|
|
2572
|
+
* See {@link SubscribeToAgentSubscriber} for the accepted callback subset
|
|
2573
|
+
* and the rationale for excluding AG-UI event handlers.
|
|
2574
|
+
*/
|
|
2575
|
+
subscribeToAgentWithOptions(agent, subscriber, options) {
|
|
2576
|
+
const resolved = options?.throttleMs ?? this._defaultThrottleMs ?? 0;
|
|
2577
|
+
let effectiveMs = 0;
|
|
2578
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
2579
|
+
const source = options?.throttleMs !== void 0 ? "throttleMs" : "defaultThrottleMs";
|
|
2580
|
+
this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions: ${source} must be a non-negative finite number, got ${resolved}. Falling back to unthrottled.`, {
|
|
2581
|
+
error: /* @__PURE__ */ new Error(`subscribeToAgentWithOptions: invalid ${source} (${resolved}), falling back to unthrottled`),
|
|
2582
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2583
|
+
context: {
|
|
2584
|
+
agentId: agent.agentId,
|
|
2585
|
+
source,
|
|
2586
|
+
value: resolved
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
} else effectiveMs = resolved;
|
|
2590
|
+
const agentLabel = agent.agentId || "(unknown agent)";
|
|
2591
|
+
const safeCall = (label, fn, ...args) => {
|
|
2592
|
+
const reportError = (err, verb) => {
|
|
2593
|
+
this.logAndEmitError(`CopilotKitCore.subscribeToAgentWithOptions[${agentLabel}]: ${label} callback ${verb}:`, {
|
|
2594
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
2595
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2596
|
+
context: {
|
|
2597
|
+
agentId: agent.agentId,
|
|
2598
|
+
callback: label
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
};
|
|
2602
|
+
try {
|
|
2603
|
+
const result = fn(...args);
|
|
2604
|
+
if (result instanceof Promise) return result.catch((err) => {
|
|
2605
|
+
reportError(err, "rejected");
|
|
2606
|
+
});
|
|
2607
|
+
return result;
|
|
2608
|
+
} catch (err) {
|
|
2609
|
+
reportError(err, "threw");
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
const guardAll = (sub) => {
|
|
2613
|
+
const guarded = {};
|
|
2614
|
+
if (sub.onMessagesChanged) {
|
|
2615
|
+
const fn = sub.onMessagesChanged;
|
|
2616
|
+
guarded.onMessagesChanged = (params) => safeCall("onMessagesChanged", fn, params);
|
|
2617
|
+
}
|
|
2618
|
+
if (sub.onStateChanged) {
|
|
2619
|
+
const fn = sub.onStateChanged;
|
|
2620
|
+
guarded.onStateChanged = (params) => safeCall("onStateChanged", fn, params);
|
|
2621
|
+
}
|
|
2622
|
+
if (sub.onRunInitialized) {
|
|
2623
|
+
const fn = sub.onRunInitialized;
|
|
2624
|
+
guarded.onRunInitialized = (params) => safeCall("onRunInitialized", fn, params);
|
|
2625
|
+
}
|
|
2626
|
+
if (sub.onRunFinalized) {
|
|
2627
|
+
const fn = sub.onRunFinalized;
|
|
2628
|
+
guarded.onRunFinalized = (params) => safeCall("onRunFinalized", fn, params);
|
|
2629
|
+
}
|
|
2630
|
+
if (sub.onRunFailed) {
|
|
2631
|
+
const fn = sub.onRunFailed;
|
|
2632
|
+
guarded.onRunFailed = (params) => safeCall("onRunFailed", fn, params);
|
|
2633
|
+
}
|
|
2634
|
+
if (sub.onRunErrorEvent) {
|
|
2635
|
+
const fn = sub.onRunErrorEvent;
|
|
2636
|
+
guarded.onRunErrorEvent = (params) => safeCall("onRunErrorEvent", fn, params);
|
|
2637
|
+
}
|
|
2638
|
+
return guarded;
|
|
2639
|
+
};
|
|
2640
|
+
for (const key of Object.keys(subscriber)) if (typeof subscriber[key] === "function" && !ALLOWED_KEYS.has(key)) {
|
|
2641
|
+
const message = `CopilotKitCore.subscribeToAgentWithOptions[${agentLabel}]: callback "${key}" is not supported and was dropped. Supported callbacks: ${Array.from(ALLOWED_KEYS).join(", ")}. Use agent.subscribe() directly for event handlers and per-item notifications.`;
|
|
2642
|
+
this.logAndEmitError(message, {
|
|
2643
|
+
error: new Error(message),
|
|
2644
|
+
code: CopilotKitCoreErrorCode.SUBSCRIBER_CALLBACK_FAILED,
|
|
2645
|
+
context: {
|
|
2646
|
+
agentId: agent.agentId,
|
|
2647
|
+
droppedCallback: key
|
|
2648
|
+
}
|
|
2649
|
+
}, "warn");
|
|
2650
|
+
}
|
|
2651
|
+
if (effectiveMs <= 0) {
|
|
2652
|
+
const subscription = agent.subscribe(guardAll(subscriber));
|
|
2653
|
+
return { unsubscribe: () => subscription.unsubscribe() };
|
|
2654
|
+
}
|
|
2655
|
+
let active = true;
|
|
2656
|
+
let latestMessagesParams = null;
|
|
2657
|
+
let latestStateParams = null;
|
|
2658
|
+
const flushPending = () => {
|
|
2659
|
+
if (active && subscriber.onMessagesChanged && latestMessagesParams) {
|
|
2660
|
+
const params = latestMessagesParams;
|
|
2661
|
+
latestMessagesParams = null;
|
|
2662
|
+
safeCall("onMessagesChanged", subscriber.onMessagesChanged, params);
|
|
2663
|
+
}
|
|
2664
|
+
if (active && subscriber.onStateChanged && latestStateParams) {
|
|
2665
|
+
const params = latestStateParams;
|
|
2666
|
+
latestStateParams = null;
|
|
2667
|
+
safeCall("onStateChanged", subscriber.onStateChanged, params);
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
const throttler = new Throttler(flushPending, {
|
|
2671
|
+
wait: effectiveMs,
|
|
2672
|
+
leading: true,
|
|
2673
|
+
trailing: true
|
|
2674
|
+
});
|
|
2675
|
+
const lifecycleOnly = {};
|
|
2676
|
+
if (subscriber.onRunInitialized) lifecycleOnly.onRunInitialized = subscriber.onRunInitialized;
|
|
2677
|
+
if (subscriber.onRunFinalized) lifecycleOnly.onRunFinalized = subscriber.onRunFinalized;
|
|
2678
|
+
if (subscriber.onRunFailed) lifecycleOnly.onRunFailed = subscriber.onRunFailed;
|
|
2679
|
+
if (subscriber.onRunErrorEvent) lifecycleOnly.onRunErrorEvent = subscriber.onRunErrorEvent;
|
|
2680
|
+
const wrappedSubscriber = guardAll(lifecycleOnly);
|
|
2681
|
+
if (subscriber.onMessagesChanged) wrappedSubscriber.onMessagesChanged = (params) => {
|
|
2682
|
+
latestMessagesParams = params;
|
|
2683
|
+
throttler.maybeExecute();
|
|
2684
|
+
};
|
|
2685
|
+
if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
|
|
2686
|
+
latestStateParams = params;
|
|
2687
|
+
throttler.maybeExecute();
|
|
2688
|
+
};
|
|
2689
|
+
const subscription = agent.subscribe(wrappedSubscriber);
|
|
2690
|
+
return { unsubscribe: () => {
|
|
2691
|
+
active = false;
|
|
2692
|
+
throttler.cancel();
|
|
2693
|
+
subscription.unsubscribe();
|
|
2694
|
+
} };
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2542
2697
|
* Agent connectivity (delegated to RunHandler)
|
|
2543
2698
|
*/
|
|
2544
2699
|
async connectAgent(params) {
|
|
@@ -3085,15 +3240,18 @@ const threadDomainEvents = createActionGroup("Thread Domain", {
|
|
|
3085
3240
|
threadUpserted: props(),
|
|
3086
3241
|
threadDeleted: props()
|
|
3087
3242
|
});
|
|
3088
|
-
function
|
|
3089
|
-
return [...threads].sort((left, right) =>
|
|
3243
|
+
function sortThreadsByRecency(threads) {
|
|
3244
|
+
return [...threads].sort((left, right) => {
|
|
3245
|
+
const leftKey = left.lastRunAt ?? left.updatedAt ?? left.createdAt;
|
|
3246
|
+
return (right.lastRunAt ?? right.updatedAt ?? right.createdAt).localeCompare(leftKey);
|
|
3247
|
+
});
|
|
3090
3248
|
}
|
|
3091
3249
|
function upsertThread(threads, thread) {
|
|
3092
3250
|
const existingIndex = threads.findIndex((item) => item.id === thread.id);
|
|
3093
|
-
if (existingIndex === -1) return
|
|
3251
|
+
if (existingIndex === -1) return sortThreadsByRecency([...threads, thread]);
|
|
3094
3252
|
const next = [...threads];
|
|
3095
3253
|
next[existingIndex] = thread;
|
|
3096
|
-
return
|
|
3254
|
+
return sortThreadsByRecency(next);
|
|
3097
3255
|
}
|
|
3098
3256
|
const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.contextChanged, (state, { context }) => ({
|
|
3099
3257
|
...state,
|
|
@@ -3126,7 +3284,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3126
3284
|
if (sessionId !== state.sessionId) return state;
|
|
3127
3285
|
return {
|
|
3128
3286
|
...state,
|
|
3129
|
-
threads:
|
|
3287
|
+
threads: sortThreadsByRecency(threads),
|
|
3130
3288
|
isLoading: false,
|
|
3131
3289
|
error: null,
|
|
3132
3290
|
metadataJoinCode: joinCode,
|