@copilotkit/core 1.62.2 → 1.63.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 +1560 -664
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +483 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +483 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1547 -668
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1559 -667
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Throttler } from "@tanstack/pacer";
|
|
2
2
|
import { AbstractAgent, EventType, HttpAgent, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
|
|
3
3
|
import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema } from "@copilotkit/shared";
|
|
4
|
-
import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, of, switchMap, throwError } from "rxjs";
|
|
5
|
-
import { catchError, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
|
|
4
|
+
import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, observeOn, of, switchMap, throwError } from "rxjs";
|
|
5
|
+
import { catchError, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
|
|
6
6
|
import { Socket } from "phoenix";
|
|
7
7
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
8
|
+
import { fromFetch } from "rxjs/fetch";
|
|
8
9
|
|
|
9
10
|
//#region src/utils/phoenix-observable.ts
|
|
10
11
|
/**
|
|
@@ -542,12 +543,12 @@ function hasCredentials(agent) {
|
|
|
542
543
|
function isZodError(error) {
|
|
543
544
|
return error !== null && typeof error === "object" && "name" in error && error.name === "ZodError";
|
|
544
545
|
}
|
|
545
|
-
function isAbortError(error) {
|
|
546
|
+
function isAbortError$1(error) {
|
|
546
547
|
return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
|
|
547
548
|
}
|
|
548
549
|
function withAbortErrorHandling(observable) {
|
|
549
550
|
return observable.pipe(catchError((error) => {
|
|
550
|
-
if (isZodError(error) || isAbortError(error)) return EMPTY;
|
|
551
|
+
if (isZodError(error) || isAbortError$1(error)) return EMPTY;
|
|
551
552
|
throw error;
|
|
552
553
|
}));
|
|
553
554
|
}
|
|
@@ -887,6 +888,7 @@ var AgentRegistry = class {
|
|
|
887
888
|
localAgents = {};
|
|
888
889
|
remoteAgents = {};
|
|
889
890
|
_runtimeUrl;
|
|
891
|
+
_connectionInFlight;
|
|
890
892
|
_runtimeVersion;
|
|
891
893
|
_runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
|
|
892
894
|
_runtimeTransport = "auto";
|
|
@@ -895,6 +897,7 @@ var AgentRegistry = class {
|
|
|
895
897
|
_runtimeMode = RUNTIME_MODE_SSE;
|
|
896
898
|
_intelligence;
|
|
897
899
|
_threadEndpoints;
|
|
900
|
+
_suggestions;
|
|
898
901
|
_a2uiEnabled = false;
|
|
899
902
|
_a2uiAgents;
|
|
900
903
|
_openGenerativeUIEnabled = false;
|
|
@@ -944,6 +947,9 @@ var AgentRegistry = class {
|
|
|
944
947
|
get threadEndpoints() {
|
|
945
948
|
return this._threadEndpoints;
|
|
946
949
|
}
|
|
950
|
+
get suggestions() {
|
|
951
|
+
return this._suggestions;
|
|
952
|
+
}
|
|
947
953
|
get a2uiEnabled() {
|
|
948
954
|
return this._a2uiEnabled;
|
|
949
955
|
}
|
|
@@ -975,10 +981,28 @@ var AgentRegistry = class {
|
|
|
975
981
|
/**
|
|
976
982
|
* Set the runtime URL and update connection
|
|
977
983
|
*/
|
|
978
|
-
setRuntimeUrl(runtimeUrl) {
|
|
984
|
+
setRuntimeUrl(runtimeUrl, options) {
|
|
979
985
|
const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
|
|
980
986
|
if (this._runtimeUrl === normalizedRuntimeUrl) return;
|
|
981
987
|
this._runtimeUrl = normalizedRuntimeUrl;
|
|
988
|
+
if (options?.deferConnection) return;
|
|
989
|
+
this.updateRuntimeConnection();
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Start the initial runtime connection if it has not been started yet.
|
|
993
|
+
*
|
|
994
|
+
* Backs {@link CopilotKitCore.connect}. Idempotent: it only kicks off a fetch
|
|
995
|
+
* when a `runtimeUrl` is set and the connection is still `Disconnected` (its
|
|
996
|
+
* state before any connect attempt). `updateRuntimeConnection` flips the
|
|
997
|
+
* status to `Connecting` synchronously, so a second call — e.g. React
|
|
998
|
+
* StrictMode double-invoking the mount effect — bails here. A genuine config
|
|
999
|
+
* change still reconnects through `setRuntimeUrl`/`setRuntimeTransport`. See
|
|
1000
|
+
* #5801.
|
|
1001
|
+
*/
|
|
1002
|
+
connectRuntime() {
|
|
1003
|
+
if (typeof window === "undefined") return;
|
|
1004
|
+
if (!this._runtimeUrl) return;
|
|
1005
|
+
if (this._runtimeConnectionStatus !== CopilotKitCoreRuntimeConnectionStatus.Disconnected) return;
|
|
982
1006
|
this.updateRuntimeConnection();
|
|
983
1007
|
}
|
|
984
1008
|
setRuntimeTransport(runtimeTransport) {
|
|
@@ -1124,6 +1148,20 @@ var AgentRegistry = class {
|
|
|
1124
1148
|
*/
|
|
1125
1149
|
async updateRuntimeConnection() {
|
|
1126
1150
|
if (typeof window === "undefined") return;
|
|
1151
|
+
const key = `${this._runtimeUrl ?? ""}::${this._requestedTransport}`;
|
|
1152
|
+
const inFlight = this._connectionInFlight;
|
|
1153
|
+
if (inFlight && inFlight.key === key) return inFlight.promise;
|
|
1154
|
+
const promise = this.performRuntimeConnection();
|
|
1155
|
+
this._connectionInFlight = {
|
|
1156
|
+
key,
|
|
1157
|
+
promise
|
|
1158
|
+
};
|
|
1159
|
+
promise.finally(() => {
|
|
1160
|
+
if (this._connectionInFlight?.promise === promise) this._connectionInFlight = void 0;
|
|
1161
|
+
});
|
|
1162
|
+
return promise;
|
|
1163
|
+
}
|
|
1164
|
+
async performRuntimeConnection() {
|
|
1127
1165
|
if (!this.runtimeUrl) {
|
|
1128
1166
|
this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
|
|
1129
1167
|
this._runtimeVersion = void 0;
|
|
@@ -1131,6 +1169,7 @@ var AgentRegistry = class {
|
|
|
1131
1169
|
this._runtimeMode = RUNTIME_MODE_SSE;
|
|
1132
1170
|
this._intelligence = void 0;
|
|
1133
1171
|
this._threadEndpoints = void 0;
|
|
1172
|
+
this._suggestions = void 0;
|
|
1134
1173
|
this._a2uiEnabled = false;
|
|
1135
1174
|
this._a2uiAgents = void 0;
|
|
1136
1175
|
this._openGenerativeUIEnabled = false;
|
|
@@ -1178,6 +1217,7 @@ var AgentRegistry = class {
|
|
|
1178
1217
|
this._runtimeMode = runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE;
|
|
1179
1218
|
this._intelligence = runtimeInfoResponse.intelligence;
|
|
1180
1219
|
this._threadEndpoints = runtimeInfoResponse.threadEndpoints;
|
|
1220
|
+
this._suggestions = runtimeInfoResponse.suggestions;
|
|
1181
1221
|
const a2uiInfo = runtimeInfoResponse.a2ui;
|
|
1182
1222
|
this._a2uiEnabled = a2uiInfo?.enabled ?? runtimeInfoResponse.a2uiEnabled ?? false;
|
|
1183
1223
|
this._a2uiAgents = a2uiInfo?.enabled ? a2uiInfo.agents : void 0;
|
|
@@ -1193,6 +1233,7 @@ var AgentRegistry = class {
|
|
|
1193
1233
|
this._runtimeMode = RUNTIME_MODE_SSE;
|
|
1194
1234
|
this._intelligence = void 0;
|
|
1195
1235
|
this._threadEndpoints = void 0;
|
|
1236
|
+
this._suggestions = void 0;
|
|
1196
1237
|
this._a2uiEnabled = false;
|
|
1197
1238
|
this._a2uiAgents = void 0;
|
|
1198
1239
|
this._openGenerativeUIEnabled = false;
|
|
@@ -1432,38 +1473,81 @@ var SuggestionEngine = class {
|
|
|
1432
1473
|
};
|
|
1433
1474
|
}
|
|
1434
1475
|
/**
|
|
1435
|
-
* Generate suggestions
|
|
1476
|
+
* Generate suggestions by running a provider agent and extracting the
|
|
1477
|
+
* `copilotkitSuggest` tool call from its streamed messages.
|
|
1478
|
+
*
|
|
1479
|
+
* Two transports, one set of mechanics: both seed an agent with the
|
|
1480
|
+
* consumer's messages + state and the instruction, then `runAgent` with
|
|
1481
|
+
* forced `copilotkitSuggest` tool choice, parsing suggestions as messages
|
|
1482
|
+
* stream in via `onMessagesChanged`.
|
|
1483
|
+
*
|
|
1484
|
+
* - **Stateless** (runtime advertises `suggestions` and transport isn't
|
|
1485
|
+
* `single`): a stock `HttpAgent` pointed at `/agent/:id/suggest`. That
|
|
1486
|
+
* endpoint runs the provider directly and streams AG-UI SSE **without**
|
|
1487
|
+
* persisting a thread, and a plain `HttpAgent` only ever speaks REST SSE —
|
|
1488
|
+
* it never routes through the Intelligence websocket delegate (which would
|
|
1489
|
+
* persist a thread). This is the path that removes the thread flood.
|
|
1490
|
+
* - **Fallback** (capability absent → old runtime, or `single` transport):
|
|
1491
|
+
* clone the provider agent and run it client-side, as before.
|
|
1492
|
+
*
|
|
1493
|
+
* Gated to non-`single` transports because this client only builds the
|
|
1494
|
+
* multi-route `/agent/:id/suggest` URL; single-route deployments (not the
|
|
1495
|
+
* persisting-thread/Intelligence case) fall through to the clone fallback.
|
|
1436
1496
|
*/
|
|
1437
1497
|
async generateSuggestions(suggestionId, config, consumerAgentId) {
|
|
1438
|
-
let
|
|
1498
|
+
let runHandle = void 0;
|
|
1499
|
+
let aborted = false;
|
|
1439
1500
|
try {
|
|
1440
|
-
const
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1501
|
+
const friends = this.core;
|
|
1502
|
+
const resolvedProviderAgentId = config.providerAgentId ?? "default";
|
|
1503
|
+
const suggestionsProviderAgent = friends.getAgent(resolvedProviderAgentId);
|
|
1504
|
+
if (!suggestionsProviderAgent) throw new Error(`Suggestions provider agent not found: ${resolvedProviderAgentId}`);
|
|
1505
|
+
const suggestionsConsumerAgent = friends.getAgent(consumerAgentId);
|
|
1443
1506
|
if (!suggestionsConsumerAgent) throw new Error(`Suggestions consumer agent not found: ${consumerAgentId}`);
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1507
|
+
const instructionContent = [
|
|
1508
|
+
`Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
|
|
1509
|
+
`Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
|
|
1510
|
+
`The user has the following tools available: ${JSON.stringify(friends.buildFrontendTools(consumerAgentId))}.`,
|
|
1511
|
+
` ${config.instructions}`
|
|
1512
|
+
].join("\n");
|
|
1448
1513
|
this._suggestions[consumerAgentId] = {
|
|
1449
1514
|
...this._suggestions[consumerAgentId],
|
|
1450
1515
|
[suggestionId]: []
|
|
1451
1516
|
};
|
|
1452
|
-
|
|
1453
|
-
|
|
1517
|
+
const seededMessages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages));
|
|
1518
|
+
const seededState = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state ?? {}));
|
|
1519
|
+
const useStateless = this.core.suggestions === true && this.core.runtimeTransport !== "single";
|
|
1520
|
+
let suggestionAgent;
|
|
1521
|
+
if (useStateless) {
|
|
1522
|
+
const suggestUrl = `${this.core.runtimeUrl}/agent/${encodeURIComponent(resolvedProviderAgentId)}/suggest`;
|
|
1523
|
+
const credentials = this.core.credentials;
|
|
1524
|
+
suggestionAgent = new HttpAgent({
|
|
1525
|
+
agentId: resolvedProviderAgentId,
|
|
1526
|
+
url: suggestUrl,
|
|
1527
|
+
headers: { ...this.core.headers },
|
|
1528
|
+
...credentials ? { fetch: (url, requestInit) => fetch(url, {
|
|
1529
|
+
...requestInit,
|
|
1530
|
+
credentials
|
|
1531
|
+
}) } : {}
|
|
1532
|
+
});
|
|
1533
|
+
} else suggestionAgent = suggestionsProviderAgent.clone();
|
|
1534
|
+
suggestionAgent.threadId = suggestionId;
|
|
1535
|
+
suggestionAgent.setMessages(seededMessages);
|
|
1536
|
+
suggestionAgent.setState(seededState);
|
|
1537
|
+
runHandle = { abortRun: () => {
|
|
1538
|
+
aborted = true;
|
|
1539
|
+
suggestionAgent.abortRun();
|
|
1540
|
+
} };
|
|
1541
|
+
this._runningSuggestions[consumerAgentId] = [...this._runningSuggestions[consumerAgentId] ?? [], runHandle];
|
|
1542
|
+
suggestionAgent.addMessage({
|
|
1454
1543
|
id: suggestionId,
|
|
1455
1544
|
role: "user",
|
|
1456
|
-
content:
|
|
1457
|
-
`Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
|
|
1458
|
-
`Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
|
|
1459
|
-
`The user has the following tools available: ${JSON.stringify(this.core.buildFrontendTools(consumerAgentId))}.`,
|
|
1460
|
-
` ${config.instructions}`
|
|
1461
|
-
].join("\n")
|
|
1545
|
+
content: instructionContent
|
|
1462
1546
|
});
|
|
1463
|
-
await
|
|
1464
|
-
context:
|
|
1547
|
+
await suggestionAgent.runAgent({
|
|
1548
|
+
context: friends.getContextForAgent(consumerAgentId),
|
|
1465
1549
|
forwardedProps: {
|
|
1466
|
-
...
|
|
1550
|
+
...friends.properties,
|
|
1467
1551
|
toolChoice: {
|
|
1468
1552
|
type: "function",
|
|
1469
1553
|
function: { name: "copilotkitSuggest" }
|
|
@@ -1474,18 +1558,15 @@ var SuggestionEngine = class {
|
|
|
1474
1558
|
this.extractSuggestions(messages, suggestionId, consumerAgentId, true);
|
|
1475
1559
|
} });
|
|
1476
1560
|
} catch (error) {
|
|
1477
|
-
console.warn("Error generating suggestions:", error);
|
|
1561
|
+
if (!isAbortError(error) && !aborted) console.warn("Error generating suggestions:", error);
|
|
1478
1562
|
} finally {
|
|
1479
1563
|
this.finalizeSuggestions(suggestionId, consumerAgentId);
|
|
1480
1564
|
const runningAgents = this._runningSuggestions[consumerAgentId];
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
this._runningSuggestions[consumerAgentId]
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
await this.notifySuggestionsFinishedLoading(consumerAgentId);
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1565
|
+
const remaining = runHandle ? (runningAgents ?? []).filter((a) => a !== runHandle) : runningAgents ?? [];
|
|
1566
|
+
if (remaining.length === 0) {
|
|
1567
|
+
delete this._runningSuggestions[consumerAgentId];
|
|
1568
|
+
await this.notifySuggestionsFinishedLoading(consumerAgentId);
|
|
1569
|
+
} else this._runningSuggestions[consumerAgentId] = remaining;
|
|
1489
1570
|
}
|
|
1490
1571
|
}
|
|
1491
1572
|
/**
|
|
@@ -1605,6 +1686,13 @@ var SuggestionEngine = class {
|
|
|
1605
1686
|
}
|
|
1606
1687
|
};
|
|
1607
1688
|
/**
|
|
1689
|
+
* Detects an `AbortController`-driven cancellation so the stateless path can
|
|
1690
|
+
* treat it as an expected (non-error) outcome on clear/reload.
|
|
1691
|
+
*/
|
|
1692
|
+
function isAbortError(error) {
|
|
1693
|
+
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1608
1696
|
* Type guard for dynamic suggestions config
|
|
1609
1697
|
*/
|
|
1610
1698
|
function isDynamicSuggestionsConfig(config) {
|
|
@@ -2564,153 +2652,1135 @@ var ThreadStoreRegistry = class {
|
|
|
2564
2652
|
};
|
|
2565
2653
|
|
|
2566
2654
|
//#endregion
|
|
2567
|
-
//#region src/
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
|
|
2575
|
-
CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
|
|
2576
|
-
CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
|
|
2577
|
-
CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
|
|
2578
|
-
/**
|
|
2579
|
-
* Emitted when an agent run fails because the thread is already locked
|
|
2580
|
-
* by another active run.
|
|
2581
|
-
*
|
|
2582
|
-
* @example
|
|
2583
|
-
* ```tsx
|
|
2584
|
-
* <CopilotKitProvider
|
|
2585
|
-
* onError={({ code, error, context }) => {
|
|
2586
|
-
* if (code === "agent_thread_locked") {
|
|
2587
|
-
* // Show "Agent is busy, retry?" UI
|
|
2588
|
-
* }
|
|
2589
|
-
* }}
|
|
2590
|
-
* />
|
|
2591
|
-
* ```
|
|
2592
|
-
*/
|
|
2593
|
-
CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
|
|
2594
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
|
|
2595
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
|
|
2596
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
|
|
2597
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
|
|
2598
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
|
|
2599
|
-
CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
|
|
2600
|
-
CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
|
|
2601
|
-
return CopilotKitCoreErrorCode;
|
|
2602
|
-
}({});
|
|
2655
|
+
//#region src/utils/micro-redux.ts
|
|
2656
|
+
const INTERNAL_ACTION_TYPES = {
|
|
2657
|
+
boot: "@@micro-redux/boot",
|
|
2658
|
+
init: "@@micro-redux/init",
|
|
2659
|
+
stop: "@@micro-redux/stop"
|
|
2660
|
+
};
|
|
2661
|
+
const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
|
|
2603
2662
|
/**
|
|
2604
|
-
*
|
|
2605
|
-
* to module scope so the Set is allocated once, not per-subscription.
|
|
2663
|
+
* Builds a typed action creator from a type string and payload factory.
|
|
2606
2664
|
*/
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
|
|
2617
|
-
CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
|
|
2618
|
-
CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
|
|
2619
|
-
CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
|
|
2620
|
-
return CopilotKitCoreRuntimeConnectionStatus;
|
|
2621
|
-
}({});
|
|
2665
|
+
function createTypedActionCreator(type, factory) {
|
|
2666
|
+
const creator = ((...args) => ({
|
|
2667
|
+
...factory(...args),
|
|
2668
|
+
type
|
|
2669
|
+
}));
|
|
2670
|
+
creator.type = type;
|
|
2671
|
+
creator.match = (action) => action.type === type;
|
|
2672
|
+
return creator;
|
|
2673
|
+
}
|
|
2622
2674
|
/**
|
|
2623
|
-
*
|
|
2624
|
-
*
|
|
2625
|
-
*
|
|
2626
|
-
*
|
|
2675
|
+
* Declares a payload-based action config for `createActionGroup`.
|
|
2676
|
+
*
|
|
2677
|
+
* @example
|
|
2678
|
+
* ```ts
|
|
2679
|
+
* const actions = createActionGroup("User", {
|
|
2680
|
+
* loaded: props<{ id: string }>(),
|
|
2681
|
+
* });
|
|
2682
|
+
* ```
|
|
2627
2683
|
*/
|
|
2628
|
-
function
|
|
2629
|
-
return
|
|
2684
|
+
function props() {
|
|
2685
|
+
return { kind: "props" };
|
|
2630
2686
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
this.stateManager = new StateManager(this);
|
|
2661
|
-
this.threadStoreRegistry = new ThreadStoreRegistry(this);
|
|
2662
|
-
this.agentRegistry.initialize(agents__unsafe_dev_only);
|
|
2663
|
-
this.runHandler.initialize(tools);
|
|
2664
|
-
this.suggestionEngine.initialize(suggestionsConfig);
|
|
2665
|
-
this.stateManager.initialize();
|
|
2666
|
-
this.agentRegistry.setRuntimeTransport(runtimeTransport);
|
|
2667
|
-
this.agentRegistry.setRuntimeUrl(runtimeUrl);
|
|
2668
|
-
this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
|
|
2669
|
-
this.subscribe({ onAgentsChanged: ({ agents }) => {
|
|
2670
|
-
Object.values(agents).forEach((agent) => {
|
|
2671
|
-
if (agent.agentId) this.stateManager.subscribeToAgent(agent);
|
|
2672
|
-
});
|
|
2673
|
-
const currentAgentIds = new Set(Object.keys(agents));
|
|
2674
|
-
for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
|
|
2675
|
-
this.threadStoreRegistry.unregister(agentId);
|
|
2676
|
-
} catch (err) {
|
|
2677
|
-
console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
|
|
2678
|
-
}
|
|
2679
|
-
for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
|
|
2680
|
-
this.stateManager.unsubscribeFromAgent(agentId);
|
|
2681
|
-
} catch (err) {
|
|
2682
|
-
console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
|
|
2683
|
-
}
|
|
2684
|
-
this.previousAgentIds = currentAgentIds;
|
|
2685
|
-
} });
|
|
2687
|
+
/**
|
|
2688
|
+
* Declares a no-payload action config for `createActionGroup`.
|
|
2689
|
+
*
|
|
2690
|
+
* @example
|
|
2691
|
+
* ```ts
|
|
2692
|
+
* const actions = createActionGroup("User", {
|
|
2693
|
+
* reset: empty(),
|
|
2694
|
+
* });
|
|
2695
|
+
* ```
|
|
2696
|
+
*/
|
|
2697
|
+
function empty() {
|
|
2698
|
+
return { kind: "empty" };
|
|
2699
|
+
}
|
|
2700
|
+
/**
|
|
2701
|
+
* Creates a namespaced group of typed action creators.
|
|
2702
|
+
*
|
|
2703
|
+
* Action types are formatted as: `[Source] actionName`.
|
|
2704
|
+
*/
|
|
2705
|
+
function createActionGroup(source, config) {
|
|
2706
|
+
const group = {};
|
|
2707
|
+
for (const eventName of Object.keys(config)) {
|
|
2708
|
+
const eventConfig = config[eventName];
|
|
2709
|
+
if (!eventConfig) continue;
|
|
2710
|
+
const actionType = `[${source}] ${eventName}`;
|
|
2711
|
+
if (eventConfig.kind === "props") {
|
|
2712
|
+
group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
group[eventName] = createTypedActionCreator(actionType, () => ({}));
|
|
2686
2716
|
}
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2717
|
+
return group;
|
|
2718
|
+
}
|
|
2719
|
+
/**
|
|
2720
|
+
* Registers one reducer handler for one or more action creators.
|
|
2721
|
+
*
|
|
2722
|
+
* @throws Error when called without at least one action creator and reducer.
|
|
2723
|
+
*/
|
|
2724
|
+
function on(...args) {
|
|
2725
|
+
if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
|
|
2726
|
+
const reducer = args[args.length - 1];
|
|
2727
|
+
return {
|
|
2728
|
+
creators: args.slice(0, -1),
|
|
2729
|
+
reducer
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Creates a reducer from an initial state and `on(...)` handler entries.
|
|
2734
|
+
*
|
|
2735
|
+
* Unknown action types return the current state unchanged.
|
|
2736
|
+
*/
|
|
2737
|
+
function createReducer(initialState, ...entries) {
|
|
2738
|
+
const reducerMap = /* @__PURE__ */ new Map();
|
|
2739
|
+
for (const entry of entries) for (const creator of entry.creators) {
|
|
2740
|
+
const handlers = reducerMap.get(creator.type) ?? [];
|
|
2741
|
+
handlers.push(entry.reducer);
|
|
2742
|
+
reducerMap.set(creator.type, handlers);
|
|
2698
2743
|
}
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2744
|
+
return (state, action) => {
|
|
2745
|
+
const currentState = state ?? initialState;
|
|
2746
|
+
const handlers = reducerMap.get(action.type);
|
|
2747
|
+
if (!handlers || handlers.length === 0) return currentState;
|
|
2748
|
+
let nextState = currentState;
|
|
2749
|
+
for (const handler of handlers) nextState = handler(nextState, action);
|
|
2750
|
+
return nextState;
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Creates a selector that caches and reuses the last computed result
|
|
2755
|
+
* when all input references are unchanged.
|
|
2756
|
+
*/
|
|
2757
|
+
function createSelector(...args) {
|
|
2758
|
+
if (args.length === 1) {
|
|
2759
|
+
const projector = args[0];
|
|
2760
|
+
let hasCached = false;
|
|
2761
|
+
let lastState;
|
|
2762
|
+
let lastResult;
|
|
2763
|
+
return (state) => {
|
|
2764
|
+
if (hasCached && state === lastState) return lastResult;
|
|
2765
|
+
lastState = state;
|
|
2766
|
+
lastResult = projector(state);
|
|
2767
|
+
hasCached = true;
|
|
2768
|
+
return lastResult;
|
|
2769
|
+
};
|
|
2709
2770
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2771
|
+
const projector = args[args.length - 1];
|
|
2772
|
+
const selectors = args.slice(0, -1);
|
|
2773
|
+
let hasCached = false;
|
|
2774
|
+
let lastInputs = [];
|
|
2775
|
+
let lastResult;
|
|
2776
|
+
return (state) => {
|
|
2777
|
+
const inputs = selectors.map((selector) => selector(state));
|
|
2778
|
+
if (hasCached && inputs.length === lastInputs.length && inputs.every((value, index) => value === lastInputs[index])) return lastResult;
|
|
2779
|
+
lastInputs = inputs;
|
|
2780
|
+
lastResult = projector(...inputs);
|
|
2781
|
+
hasCached = true;
|
|
2782
|
+
return lastResult;
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* RxJS operator that maps state emissions through a selector and suppresses
|
|
2787
|
+
* unchanged projected values via reference equality.
|
|
2788
|
+
*/
|
|
2789
|
+
function select(selector) {
|
|
2790
|
+
return (source$) => source$.pipe(map(selector), distinctUntilChanged());
|
|
2791
|
+
}
|
|
2792
|
+
/**
|
|
2793
|
+
* RxJS operator that filters an action stream by action creators and narrows
|
|
2794
|
+
* the output action type to the matched creator union.
|
|
2795
|
+
*
|
|
2796
|
+
* @throws Error when called without at least one action creator.
|
|
2797
|
+
*/
|
|
2798
|
+
function ofType(...creators) {
|
|
2799
|
+
if (creators.length === 0) throw new Error("ofType requires at least one action creator");
|
|
2800
|
+
const actionTypes = new Set(creators.map((creator) => creator.type));
|
|
2801
|
+
return (source$) => {
|
|
2802
|
+
return source$.pipe(filter((action) => {
|
|
2803
|
+
return actionTypes.has(action.type);
|
|
2804
|
+
}));
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
/**
|
|
2808
|
+
* Creates an effect descriptor consumed by `createStore`.
|
|
2809
|
+
*/
|
|
2810
|
+
function createEffect(factory, options = {}) {
|
|
2811
|
+
if (options.dispatch === false) return {
|
|
2812
|
+
run: factory,
|
|
2813
|
+
dispatch: false
|
|
2814
|
+
};
|
|
2815
|
+
return {
|
|
2816
|
+
run: factory,
|
|
2817
|
+
dispatch: true
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Creates a small observable store with reducer + effects.
|
|
2822
|
+
*
|
|
2823
|
+
* Behavior:
|
|
2824
|
+
* - `init()` starts effects and dispatches `@@micro-redux/init`.
|
|
2825
|
+
* - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
|
|
2826
|
+
* - Effect action observation is scheduled on `asapScheduler` to avoid
|
|
2827
|
+
* synchronous re-entrancy in the effect loop.
|
|
2828
|
+
* - Any effect error triggers fail-fast teardown and errors both `actions$`
|
|
2829
|
+
* and `state$`.
|
|
2830
|
+
*/
|
|
2831
|
+
function createStore(options) {
|
|
2832
|
+
const reducer = options.reducer;
|
|
2833
|
+
const effects = options.effects ?? [];
|
|
2834
|
+
let hasFatalError = false;
|
|
2835
|
+
let isRunning = false;
|
|
2836
|
+
let effectSubscriptions = new Subscription();
|
|
2837
|
+
let currentState = reducer(void 0, INTERNAL_BOOT_ACTION);
|
|
2838
|
+
const stateSubject = new BehaviorSubject(currentState);
|
|
2839
|
+
const actionsSubject = new Subject();
|
|
2840
|
+
const dispatchInternal = (action) => {
|
|
2841
|
+
if (hasFatalError) throw new Error("Store is in a failed state due to an effect error");
|
|
2842
|
+
currentState = reducer(currentState, action);
|
|
2843
|
+
stateSubject.next(currentState);
|
|
2844
|
+
actionsSubject.next(action);
|
|
2845
|
+
};
|
|
2846
|
+
const failFast = (error) => {
|
|
2847
|
+
if (hasFatalError) return;
|
|
2848
|
+
hasFatalError = true;
|
|
2849
|
+
isRunning = false;
|
|
2850
|
+
effectSubscriptions.unsubscribe();
|
|
2851
|
+
effectSubscriptions = new Subscription();
|
|
2852
|
+
actionsSubject.error(error);
|
|
2853
|
+
stateSubject.error(error);
|
|
2854
|
+
};
|
|
2855
|
+
const startEffects = () => {
|
|
2856
|
+
for (const effect of effects) {
|
|
2857
|
+
const scheduledActions$ = actionsSubject.asObservable().pipe(observeOn$1(asapScheduler));
|
|
2858
|
+
const state$ = stateSubject.asObservable();
|
|
2859
|
+
if (effect.dispatch) {
|
|
2860
|
+
const subscription = effect.run(scheduledActions$, state$).subscribe({
|
|
2861
|
+
next: (effectAction) => {
|
|
2862
|
+
if (hasFatalError) return;
|
|
2863
|
+
dispatchInternal(effectAction);
|
|
2864
|
+
},
|
|
2865
|
+
error: (error) => {
|
|
2866
|
+
failFast(error);
|
|
2867
|
+
}
|
|
2868
|
+
});
|
|
2869
|
+
effectSubscriptions.add(subscription);
|
|
2870
|
+
continue;
|
|
2871
|
+
}
|
|
2872
|
+
const subscription = effect.run(scheduledActions$, state$).subscribe({ error: (error) => {
|
|
2873
|
+
failFast(error);
|
|
2874
|
+
} });
|
|
2875
|
+
effectSubscriptions.add(subscription);
|
|
2876
|
+
}
|
|
2877
|
+
};
|
|
2878
|
+
return {
|
|
2879
|
+
dispatch(action) {
|
|
2880
|
+
dispatchInternal(action);
|
|
2881
|
+
},
|
|
2882
|
+
getState() {
|
|
2883
|
+
return currentState;
|
|
2884
|
+
},
|
|
2885
|
+
get state$() {
|
|
2886
|
+
return stateSubject.asObservable();
|
|
2887
|
+
},
|
|
2888
|
+
get actions$() {
|
|
2889
|
+
return actionsSubject.asObservable();
|
|
2890
|
+
},
|
|
2891
|
+
select(selector) {
|
|
2892
|
+
return stateSubject.asObservable().pipe(select(selector));
|
|
2893
|
+
},
|
|
2894
|
+
init() {
|
|
2895
|
+
if (hasFatalError || isRunning) return;
|
|
2896
|
+
isRunning = true;
|
|
2897
|
+
startEffects();
|
|
2898
|
+
if (hasFatalError) return;
|
|
2899
|
+
dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
|
|
2900
|
+
},
|
|
2901
|
+
stop() {
|
|
2902
|
+
if (hasFatalError || !isRunning) return;
|
|
2903
|
+
dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
|
|
2904
|
+
effectSubscriptions.unsubscribe();
|
|
2905
|
+
effectSubscriptions = new Subscription();
|
|
2906
|
+
isRunning = false;
|
|
2907
|
+
}
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
//#endregion
|
|
2912
|
+
//#region src/memory-errors.ts
|
|
2913
|
+
/**
|
|
2914
|
+
* The memory error registry: the single source of truth for memory error
|
|
2915
|
+
* metadata. Keyed by `code` so a code maps to exactly one category/retryable
|
|
2916
|
+
* default.
|
|
2917
|
+
*
|
|
2918
|
+
* Categories/retryable rationale:
|
|
2919
|
+
* - List/credentials/mutation failures are `dependency` (the runtime/platform
|
|
2920
|
+
* the store calls failed). A 5xx is transient (`retryable: true`); a 4xx is
|
|
2921
|
+
* caller-caused, so the helpers below downgrade those to non-retryable.
|
|
2922
|
+
* - Timeouts are `dependency` and `retryable: true` (the request may simply be
|
|
2923
|
+
* slow).
|
|
2924
|
+
*/
|
|
2925
|
+
const MEMORY_ERROR_REGISTRY = Object.freeze({
|
|
2926
|
+
MEMORY_LIST_FAILED: {
|
|
2927
|
+
code: "MEMORY_LIST_FAILED",
|
|
2928
|
+
category: "dependency",
|
|
2929
|
+
retryable: true,
|
|
2930
|
+
message: "Failed to fetch memories",
|
|
2931
|
+
docsPath: "docs/errors/memory.md#memory_list_failed"
|
|
2932
|
+
},
|
|
2933
|
+
MEMORY_CREDENTIALS_FAILED: {
|
|
2934
|
+
code: "MEMORY_CREDENTIALS_FAILED",
|
|
2935
|
+
category: "dependency",
|
|
2936
|
+
retryable: true,
|
|
2937
|
+
message: "Failed to fetch memory subscribe credentials",
|
|
2938
|
+
docsPath: "docs/errors/memory.md#memory_credentials_failed"
|
|
2939
|
+
},
|
|
2940
|
+
MEMORY_MUTATION_FAILED: {
|
|
2941
|
+
code: "MEMORY_MUTATION_FAILED",
|
|
2942
|
+
category: "dependency",
|
|
2943
|
+
retryable: true,
|
|
2944
|
+
message: "Memory mutation request failed",
|
|
2945
|
+
docsPath: "docs/errors/memory.md#memory_mutation_failed"
|
|
2946
|
+
},
|
|
2947
|
+
MEMORY_REQUEST_TIMEOUT: {
|
|
2948
|
+
code: "MEMORY_REQUEST_TIMEOUT",
|
|
2949
|
+
category: "dependency",
|
|
2950
|
+
retryable: true,
|
|
2951
|
+
message: "Request timed out",
|
|
2952
|
+
docsPath: "docs/errors/memory.md#memory_request_timeout"
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
/**
|
|
2956
|
+
* An `Error` carrying a stable memory error `code`, `category`, and `retryable`
|
|
2957
|
+
* flag so consumers and observability can branch on it without string-matching
|
|
2958
|
+
* the message.
|
|
2959
|
+
*
|
|
2960
|
+
* It is a real `Error` subclass: `instanceof Error` is true, so the public
|
|
2961
|
+
* `MemoryState.error: Error | null` contract (and the `error: Error | null`
|
|
2962
|
+
* surfaced by `useMemories` / `injectMemories`) holds unchanged. Existing
|
|
2963
|
+
* consumers that only read `.message` keep working; the richer fields are
|
|
2964
|
+
* additive.
|
|
2965
|
+
*/
|
|
2966
|
+
var MemoryError = class MemoryError extends Error {
|
|
2967
|
+
/** Stable, machine-readable error identifier. */
|
|
2968
|
+
code;
|
|
2969
|
+
/** Coarse error category for grouping/observability. */
|
|
2970
|
+
category;
|
|
2971
|
+
/** Whether retrying the operation could plausibly succeed. */
|
|
2972
|
+
retryable;
|
|
2973
|
+
/**
|
|
2974
|
+
* @param code - Registry code identifying this error.
|
|
2975
|
+
* @param options - Optional overrides:
|
|
2976
|
+
* - `message`: human-readable message (defaults to the registry message;
|
|
2977
|
+
* callers pass a status-bearing string to preserve prior wording).
|
|
2978
|
+
* - `retryable`: overrides the registry default (e.g. a 4xx list failure is
|
|
2979
|
+
* not retryable even though the code's default is).
|
|
2980
|
+
* - `cause`: the underlying error, preserved for debugging.
|
|
2981
|
+
*/
|
|
2982
|
+
constructor(code, options) {
|
|
2983
|
+
const entry = MEMORY_ERROR_REGISTRY[code];
|
|
2984
|
+
super(options?.message ?? entry.message, { cause: options?.cause });
|
|
2985
|
+
this.name = "MemoryError";
|
|
2986
|
+
this.code = code;
|
|
2987
|
+
this.category = entry.category;
|
|
2988
|
+
this.retryable = options?.retryable ?? entry.retryable;
|
|
2989
|
+
Object.setPrototypeOf(this, MemoryError.prototype);
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2992
|
+
/**
|
|
2993
|
+
* Narrows an HTTP status to whether the failure is worth retrying: a 5xx (or a
|
|
2994
|
+
* missing/0 status) is a transient dependency failure and retryable; a 4xx is
|
|
2995
|
+
* caller-caused and not. Used so a `MEMORY_LIST_FAILED`/`MEMORY_MUTATION_FAILED`
|
|
2996
|
+
* carries an accurate `retryable` flag derived from the response status.
|
|
2997
|
+
*/
|
|
2998
|
+
function isRetryableStatus(status) {
|
|
2999
|
+
return !(Number.isInteger(status) && status >= 400 && status <= 499);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
//#endregion
|
|
3003
|
+
//#region src/memory.ts
|
|
3004
|
+
const MEMORIES_PATH = "/memories";
|
|
3005
|
+
const MEMORIES_SUBSCRIBE_PATH = "/memories/subscribe";
|
|
3006
|
+
const REQUEST_TIMEOUT_MS$1 = 15e3;
|
|
3007
|
+
/** Consecutive socket errors tolerated before the realtime stream gives up. */
|
|
3008
|
+
const MAX_SOCKET_RETRIES$1 = 5;
|
|
3009
|
+
/**
|
|
3010
|
+
* HTTP status codes that indicate memory routes are not configured (non-fatal).
|
|
3011
|
+
* 404/501 mean the route is absent/unimplemented; 422 is the runtime's
|
|
3012
|
+
* MISSING_INTELLIGENCE signal (intelligence not configured on the deployment —
|
|
3013
|
+
* see packages/runtime/.../handlers/intelligence/memories.ts). All three map to
|
|
3014
|
+
* the graceful "not available" locked-teaser path, not a hard error.
|
|
3015
|
+
*/
|
|
3016
|
+
const ROUTE_UNAVAILABLE_STATUSES = new Set([
|
|
3017
|
+
404,
|
|
3018
|
+
422,
|
|
3019
|
+
501
|
|
3020
|
+
]);
|
|
3021
|
+
/**
|
|
3022
|
+
* Thrown when a memory route returns a 404/422/501 — treated as "not
|
|
3023
|
+
* configured" (422 is the runtime's MISSING_INTELLIGENCE signal).
|
|
3024
|
+
*/
|
|
3025
|
+
var MemoryRouteUnavailableError = class extends Error {};
|
|
3026
|
+
const initialMemoryState = Object.freeze({
|
|
3027
|
+
memories: Object.freeze([]),
|
|
3028
|
+
isLoading: false,
|
|
3029
|
+
inFlightMutationCount: 0,
|
|
3030
|
+
error: null,
|
|
3031
|
+
context: null,
|
|
3032
|
+
sessionId: 0,
|
|
3033
|
+
available: true,
|
|
3034
|
+
realtimeStatus: "connecting"
|
|
3035
|
+
});
|
|
3036
|
+
const memoryAdapterEvents = createActionGroup("Memory Adapter", {
|
|
3037
|
+
started: empty(),
|
|
3038
|
+
stopped: empty(),
|
|
3039
|
+
contextChanged: props(),
|
|
3040
|
+
addRequested: props(),
|
|
3041
|
+
updateRequested: props(),
|
|
3042
|
+
removeRequested: props()
|
|
3043
|
+
});
|
|
3044
|
+
const memoryRestEvents = createActionGroup("Memory REST", {
|
|
3045
|
+
listRequested: props(),
|
|
3046
|
+
listSucceeded: props(),
|
|
3047
|
+
listFailed: props(),
|
|
3048
|
+
listUnavailable: props(),
|
|
3049
|
+
mutationFinished: props(),
|
|
3050
|
+
credentialsRequested: props(),
|
|
3051
|
+
credentialsSucceeded: props(),
|
|
3052
|
+
credentialsFailed: props(),
|
|
3053
|
+
credentialsUnavailable: props()
|
|
3054
|
+
});
|
|
3055
|
+
const memoryDomainEvents = createActionGroup("Memory Domain", {
|
|
3056
|
+
memoryUpserted: props(),
|
|
3057
|
+
memoryInvalidated: props(),
|
|
3058
|
+
realtimeConnecting: props(),
|
|
3059
|
+
realtimeConnected: props(),
|
|
3060
|
+
realtimeUnavailable: props()
|
|
3061
|
+
});
|
|
3062
|
+
/** Projects a wire memory to the public {@link Memory} shape (drops tenant ids). */
|
|
3063
|
+
function toMemory(memory) {
|
|
3064
|
+
return {
|
|
3065
|
+
id: memory.id,
|
|
3066
|
+
kind: memory.kind,
|
|
3067
|
+
scope: memory.scope,
|
|
3068
|
+
content: memory.content,
|
|
3069
|
+
sourceThreadIds: memory.sourceThreadIds,
|
|
3070
|
+
invalidatedAt: memory.invalidatedAt
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
/**
|
|
3074
|
+
* Maps a realtime `memory_metadata` event to the domain action that applies it:
|
|
3075
|
+
* `created`/`updated` upsert the projected memory, `invalidated` removes it by
|
|
3076
|
+
* id. Carries the current `sessionId` so the reducer's session guard can drop
|
|
3077
|
+
* deltas left over from a previous context.
|
|
3078
|
+
*/
|
|
3079
|
+
function mapMemoryMetadataEvent(event, sessionId) {
|
|
3080
|
+
if (event.operation === "invalidated") return memoryDomainEvents.memoryInvalidated({
|
|
3081
|
+
sessionId,
|
|
3082
|
+
memoryId: event.invalidated.id
|
|
3083
|
+
});
|
|
3084
|
+
return memoryDomainEvents.memoryUpserted({
|
|
3085
|
+
sessionId,
|
|
3086
|
+
memory: toMemory(event.memory)
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
/**
|
|
3090
|
+
* Inserts or replaces a memory by id. A new memory is prepended (newest first,
|
|
3091
|
+
* matching the REST list's `created_at DESC` ordering without needing a date
|
|
3092
|
+
* field on the public projection); an existing memory is replaced in place.
|
|
3093
|
+
*/
|
|
3094
|
+
function upsertMemory(memories, memory) {
|
|
3095
|
+
const existingIndex = memories.findIndex((item) => item.id === memory.id);
|
|
3096
|
+
if (existingIndex === -1) return [memory, ...memories];
|
|
3097
|
+
const next = [...memories];
|
|
3098
|
+
next[existingIndex] = memory;
|
|
3099
|
+
return next;
|
|
3100
|
+
}
|
|
3101
|
+
const memoryReducer = createReducer(initialMemoryState, on(memoryAdapterEvents.contextChanged, (state, { context }) => ({
|
|
3102
|
+
...state,
|
|
3103
|
+
context,
|
|
3104
|
+
sessionId: state.sessionId + 1,
|
|
3105
|
+
memories: [],
|
|
3106
|
+
isLoading: Boolean(context),
|
|
3107
|
+
inFlightMutationCount: 0,
|
|
3108
|
+
error: null,
|
|
3109
|
+
available: true,
|
|
3110
|
+
realtimeStatus: "connecting"
|
|
3111
|
+
})), on(memoryAdapterEvents.stopped, (state) => ({
|
|
3112
|
+
...state,
|
|
3113
|
+
memories: [],
|
|
3114
|
+
isLoading: false,
|
|
3115
|
+
inFlightMutationCount: 0,
|
|
3116
|
+
error: null,
|
|
3117
|
+
available: true,
|
|
3118
|
+
realtimeStatus: "connecting"
|
|
3119
|
+
})), on(memoryRestEvents.listRequested, (state, { sessionId }) => {
|
|
3120
|
+
if (sessionId !== state.sessionId || !state.context) return state;
|
|
3121
|
+
return {
|
|
3122
|
+
...state,
|
|
3123
|
+
isLoading: true,
|
|
3124
|
+
error: null,
|
|
3125
|
+
available: true
|
|
3126
|
+
};
|
|
3127
|
+
}), on(memoryRestEvents.listFailed, (state, { sessionId, error }) => {
|
|
3128
|
+
if (sessionId !== state.sessionId) return state;
|
|
3129
|
+
return {
|
|
3130
|
+
...state,
|
|
3131
|
+
isLoading: false,
|
|
3132
|
+
error
|
|
3133
|
+
};
|
|
3134
|
+
}), on(memoryRestEvents.listUnavailable, (state, { sessionId }) => {
|
|
3135
|
+
if (sessionId !== state.sessionId) return state;
|
|
3136
|
+
return {
|
|
3137
|
+
...state,
|
|
3138
|
+
memories: [],
|
|
3139
|
+
isLoading: false,
|
|
3140
|
+
error: null,
|
|
3141
|
+
available: false
|
|
3142
|
+
};
|
|
3143
|
+
}), on(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested, (state) => ({
|
|
3144
|
+
...state,
|
|
3145
|
+
inFlightMutationCount: state.inFlightMutationCount + 1
|
|
3146
|
+
})), on(memoryRestEvents.mutationFinished, (state, { outcome }) => {
|
|
3147
|
+
if (outcome.sessionId !== state.sessionId) return state;
|
|
3148
|
+
return {
|
|
3149
|
+
...state,
|
|
3150
|
+
inFlightMutationCount: Math.max(0, state.inFlightMutationCount - 1),
|
|
3151
|
+
error: outcome.ok ? null : outcome.error
|
|
3152
|
+
};
|
|
3153
|
+
}), on(memoryRestEvents.listSucceeded, (state, { sessionId, memories }) => {
|
|
3154
|
+
if (sessionId !== state.sessionId) return state;
|
|
3155
|
+
return {
|
|
3156
|
+
...state,
|
|
3157
|
+
memories,
|
|
3158
|
+
isLoading: false,
|
|
3159
|
+
error: null,
|
|
3160
|
+
available: true
|
|
3161
|
+
};
|
|
3162
|
+
}), on(memoryDomainEvents.memoryUpserted, (state, { sessionId, memory }) => {
|
|
3163
|
+
if (sessionId !== state.sessionId) return state;
|
|
3164
|
+
return {
|
|
3165
|
+
...state,
|
|
3166
|
+
memories: upsertMemory(state.memories, memory)
|
|
3167
|
+
};
|
|
3168
|
+
}), on(memoryDomainEvents.memoryInvalidated, (state, { sessionId, memoryId }) => {
|
|
3169
|
+
if (sessionId !== state.sessionId) return state;
|
|
3170
|
+
return {
|
|
3171
|
+
...state,
|
|
3172
|
+
memories: state.memories.filter((memory) => memory.id !== memoryId)
|
|
3173
|
+
};
|
|
3174
|
+
}), on(memoryDomainEvents.realtimeConnecting, (state, { sessionId }) => {
|
|
3175
|
+
if (sessionId !== state.sessionId) return state;
|
|
3176
|
+
return {
|
|
3177
|
+
...state,
|
|
3178
|
+
realtimeStatus: "connecting"
|
|
3179
|
+
};
|
|
3180
|
+
}), on(memoryDomainEvents.realtimeConnected, (state, { sessionId }) => {
|
|
3181
|
+
if (sessionId !== state.sessionId) return state;
|
|
3182
|
+
return {
|
|
3183
|
+
...state,
|
|
3184
|
+
realtimeStatus: "connected"
|
|
3185
|
+
};
|
|
3186
|
+
}), on(memoryDomainEvents.realtimeUnavailable, (state, { sessionId }) => {
|
|
3187
|
+
if (sessionId !== state.sessionId) return state;
|
|
3188
|
+
return {
|
|
3189
|
+
...state,
|
|
3190
|
+
realtimeStatus: "unavailable"
|
|
3191
|
+
};
|
|
3192
|
+
}));
|
|
3193
|
+
const selectMemories = createSelector((state) => state.memories);
|
|
3194
|
+
const selectMemoriesIsLoading = createSelector((state) => state.isLoading);
|
|
3195
|
+
const selectMemoriesError = createSelector((state) => state.error);
|
|
3196
|
+
/**
|
|
3197
|
+
* Reports whether at least one mutation is in flight. Derived from
|
|
3198
|
+
* `inFlightMutationCount` so concurrent mutations stay correct: the boolean
|
|
3199
|
+
* only clears once every in-flight mutation has settled.
|
|
3200
|
+
*/
|
|
3201
|
+
const selectMemoriesIsMutating = createSelector((state) => state.inFlightMutationCount > 0);
|
|
3202
|
+
/**
|
|
3203
|
+
* Reports whether the memory routes are available. Becomes `false` after a
|
|
3204
|
+
* 404, 422 (the runtime's MISSING_INTELLIGENCE signal), or 501 response during
|
|
3205
|
+
* an auto-activation read; defaults to `true`.
|
|
3206
|
+
*/
|
|
3207
|
+
const selectMemoriesAvailable = createSelector((state) => state.available);
|
|
3208
|
+
/**
|
|
3209
|
+
* Reports the realtime-connection health (see {@link MemoryRealtimeStatus}).
|
|
3210
|
+
* Distinct from `available`/`error`: it reflects ONLY the live socket/channel,
|
|
3211
|
+
* so the UI can suppress a "live" indicator once realtime permanently dies even
|
|
3212
|
+
* while the REST list route stays healthy. Defaults to `"connecting"`.
|
|
3213
|
+
*/
|
|
3214
|
+
const selectMemoriesRealtimeStatus = createSelector((state) => state.realtimeStatus);
|
|
3215
|
+
const MEMORY_METADATA_EVENT = "memory_metadata";
|
|
3216
|
+
function memoryFromFetch(input, init) {
|
|
3217
|
+
return fromFetch(input, init);
|
|
3218
|
+
}
|
|
3219
|
+
/**
|
|
3220
|
+
* Fetches the memory snapshot and maps it to a success/failure action. Keeps
|
|
3221
|
+
* only user-scoped memories (v1 surfaces user scope only; the realtime stream
|
|
3222
|
+
* is user-scoped too), so project-scoped rows visible to the caller are
|
|
3223
|
+
* dropped from the store.
|
|
3224
|
+
*/
|
|
3225
|
+
function createMemoryFetchObservable(environment, context, sessionId) {
|
|
3226
|
+
return defer(() => {
|
|
3227
|
+
const qs = context.includeInvalidated ? "?includeInvalidated=true" : "";
|
|
3228
|
+
return memoryFromFetch(`${context.runtimeUrl}${MEMORIES_PATH}${qs}`, {
|
|
3229
|
+
selector: (response) => {
|
|
3230
|
+
if (!response.ok) {
|
|
3231
|
+
if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
|
|
3232
|
+
throw new MemoryError("MEMORY_LIST_FAILED", {
|
|
3233
|
+
message: `Failed to fetch memories: ${response.status}`,
|
|
3234
|
+
retryable: isRetryableStatus(response.status)
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
return response.json();
|
|
3238
|
+
},
|
|
3239
|
+
fetch: environment.fetch,
|
|
3240
|
+
method: "GET",
|
|
3241
|
+
headers: { ...context.headers }
|
|
3242
|
+
}).pipe(timeout({
|
|
3243
|
+
first: REQUEST_TIMEOUT_MS$1,
|
|
3244
|
+
with: () => {
|
|
3245
|
+
throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
|
|
3246
|
+
}
|
|
3247
|
+
}), map((data) => memoryRestEvents.listSucceeded({
|
|
3248
|
+
sessionId,
|
|
3249
|
+
memories: data.memories.filter((memory) => memory.scope === "user")
|
|
3250
|
+
})), catchError((error) => {
|
|
3251
|
+
if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.listUnavailable({ sessionId }));
|
|
3252
|
+
return of(memoryRestEvents.listFailed({
|
|
3253
|
+
sessionId,
|
|
3254
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
3255
|
+
}));
|
|
3256
|
+
}));
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Fetches join credentials from the `/memories/subscribe` endpoint and maps
|
|
3261
|
+
* the response to a success/failure action. Requires both `joinToken` and
|
|
3262
|
+
* `joinCode` to be non-empty strings; throws otherwise so the `catchError`
|
|
3263
|
+
* path emits `credentialsFailed`.
|
|
3264
|
+
*/
|
|
3265
|
+
function createMemoryCredentialsFetchObservable(environment, context, sessionId) {
|
|
3266
|
+
return defer(() => memoryFromFetch(`${context.runtimeUrl}${MEMORIES_SUBSCRIBE_PATH}`, {
|
|
3267
|
+
selector: async (response) => {
|
|
3268
|
+
if (!response.ok) {
|
|
3269
|
+
if (ROUTE_UNAVAILABLE_STATUSES.has(response.status)) throw new MemoryRouteUnavailableError(String(response.status));
|
|
3270
|
+
throw new MemoryError("MEMORY_CREDENTIALS_FAILED", {
|
|
3271
|
+
message: `Failed to fetch memory subscribe credentials: ${response.status}`,
|
|
3272
|
+
retryable: isRetryableStatus(response.status)
|
|
3273
|
+
});
|
|
3274
|
+
}
|
|
3275
|
+
return response.json();
|
|
3276
|
+
},
|
|
3277
|
+
fetch: environment.fetch,
|
|
3278
|
+
method: "POST",
|
|
3279
|
+
headers: {
|
|
3280
|
+
...context.headers,
|
|
3281
|
+
"Content-Type": "application/json"
|
|
3282
|
+
},
|
|
3283
|
+
body: JSON.stringify({})
|
|
3284
|
+
}).pipe(timeout({
|
|
3285
|
+
first: REQUEST_TIMEOUT_MS$1,
|
|
3286
|
+
with: () => {
|
|
3287
|
+
throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
|
|
3288
|
+
}
|
|
3289
|
+
}), map((data) => {
|
|
3290
|
+
if (typeof data.joinToken !== "string" || data.joinToken.length === 0) throw new Error("missing joinToken");
|
|
3291
|
+
if (typeof data.joinCode !== "string" || data.joinCode.length === 0) throw new Error("missing joinCode");
|
|
3292
|
+
return memoryRestEvents.credentialsSucceeded({
|
|
3293
|
+
sessionId,
|
|
3294
|
+
joinToken: data.joinToken,
|
|
3295
|
+
joinCode: data.joinCode
|
|
3296
|
+
});
|
|
3297
|
+
}), catchError((error) => {
|
|
3298
|
+
if (error instanceof MemoryRouteUnavailableError) return of(memoryRestEvents.credentialsUnavailable({ sessionId }));
|
|
3299
|
+
return of(memoryRestEvents.credentialsFailed({
|
|
3300
|
+
sessionId,
|
|
3301
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
3302
|
+
}));
|
|
3303
|
+
})));
|
|
3304
|
+
}
|
|
3305
|
+
/** Projects a REST mutation response to the public {@link Memory} shape. */
|
|
3306
|
+
function responseToMemory(data) {
|
|
3307
|
+
return {
|
|
3308
|
+
id: data.id,
|
|
3309
|
+
kind: data.kind,
|
|
3310
|
+
scope: data.scope,
|
|
3311
|
+
content: data.content,
|
|
3312
|
+
sourceThreadIds: data.sourceThreadIds,
|
|
3313
|
+
invalidatedAt: data.invalidatedAt
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
const MEMORY_KINDS = new Set([
|
|
3317
|
+
"topical",
|
|
3318
|
+
"episodic",
|
|
3319
|
+
"operational"
|
|
3320
|
+
]);
|
|
3321
|
+
const MEMORY_SCOPES = new Set(["user", "project"]);
|
|
3322
|
+
/**
|
|
3323
|
+
* Validates the required fields of a create/supersede REST response before it is
|
|
3324
|
+
* projected to a {@link Memory}. Returns an `Error` describing the first
|
|
3325
|
+
* missing/invalid field, or `null` when the body is well-formed. Without this a
|
|
3326
|
+
* malformed 200 body yields a Memory with undefined fields that gets upserted.
|
|
3327
|
+
*/
|
|
3328
|
+
function validateMutationResponse(data) {
|
|
3329
|
+
if (!data || typeof data !== "object") return /* @__PURE__ */ new Error("memory mutation response missing/invalid body");
|
|
3330
|
+
if (typeof data.id !== "string" || data.id.length === 0) return /* @__PURE__ */ new Error("memory mutation response missing/invalid id");
|
|
3331
|
+
if (typeof data.kind !== "string" || !MEMORY_KINDS.has(data.kind)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid kind");
|
|
3332
|
+
if (typeof data.scope !== "string" || !MEMORY_SCOPES.has(data.scope)) return /* @__PURE__ */ new Error("memory mutation response missing/invalid scope");
|
|
3333
|
+
if (typeof data.content !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid content");
|
|
3334
|
+
if (!Array.isArray(data.sourceThreadIds) || !data.sourceThreadIds.every((id) => typeof id === "string")) return /* @__PURE__ */ new Error("memory mutation response missing/invalid sourceThreadIds");
|
|
3335
|
+
if (data.invalidatedAt !== null && typeof data.invalidatedAt !== "string") return /* @__PURE__ */ new Error("memory mutation response missing/invalid invalidatedAt");
|
|
3336
|
+
return null;
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Request body for create/supersede. `scope` is forwarded only when the caller
|
|
3340
|
+
* supplies it: the platform owns the default (`scope` defaults to `"user"` in
|
|
3341
|
+
* the `/api/memories` schema), so the store does not second-guess it here —
|
|
3342
|
+
* one default, at the contract owner. `sourceThreadIds` defaults to `[]`.
|
|
3343
|
+
*/
|
|
3344
|
+
function toMutationBody(input) {
|
|
3345
|
+
return {
|
|
3346
|
+
content: input.content,
|
|
3347
|
+
kind: input.kind,
|
|
3348
|
+
...input.scope !== void 0 ? { scope: input.scope } : {},
|
|
3349
|
+
sourceThreadIds: input.sourceThreadIds ?? []
|
|
3350
|
+
};
|
|
3351
|
+
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Builds the actions a successful mutation dispatches (server-authoritative):
|
|
3354
|
+
* the REST response is applied to local state immediately, and the realtime
|
|
3355
|
+
* event reconciles idempotently (upsert-by-id). `mutationFinished` resolves the
|
|
3356
|
+
* caller's promise.
|
|
3357
|
+
*/
|
|
3358
|
+
function buildMutationSuccessActions(request, data) {
|
|
3359
|
+
const { requestId, sessionId } = request;
|
|
3360
|
+
if (request.kind === "remove") return [memoryDomainEvents.memoryInvalidated({
|
|
3361
|
+
sessionId,
|
|
3362
|
+
memoryId: request.id
|
|
3363
|
+
}), memoryRestEvents.mutationFinished({ outcome: {
|
|
3364
|
+
requestId,
|
|
3365
|
+
sessionId,
|
|
3366
|
+
ok: true,
|
|
3367
|
+
memory: null
|
|
3368
|
+
} })];
|
|
3369
|
+
const validationError = validateMutationResponse(data);
|
|
3370
|
+
if (validationError) return [memoryRestEvents.mutationFinished({ outcome: {
|
|
3371
|
+
requestId,
|
|
3372
|
+
sessionId,
|
|
3373
|
+
ok: false,
|
|
3374
|
+
error: validationError
|
|
3375
|
+
} })];
|
|
3376
|
+
const memory = responseToMemory(data);
|
|
3377
|
+
if (request.kind === "update") {
|
|
3378
|
+
const retiredId = data.retiredId;
|
|
3379
|
+
if (typeof retiredId !== "string" || retiredId.length === 0) return [memoryRestEvents.mutationFinished({ outcome: {
|
|
3380
|
+
requestId,
|
|
3381
|
+
sessionId,
|
|
3382
|
+
ok: false,
|
|
3383
|
+
error: /* @__PURE__ */ new Error("supersede response missing retiredId")
|
|
3384
|
+
} })];
|
|
3385
|
+
return [
|
|
3386
|
+
memoryDomainEvents.memoryInvalidated({
|
|
3387
|
+
sessionId,
|
|
3388
|
+
memoryId: retiredId
|
|
3389
|
+
}),
|
|
3390
|
+
memoryDomainEvents.memoryUpserted({
|
|
3391
|
+
sessionId,
|
|
3392
|
+
memory
|
|
3393
|
+
}),
|
|
3394
|
+
memoryRestEvents.mutationFinished({ outcome: {
|
|
3395
|
+
requestId,
|
|
3396
|
+
sessionId,
|
|
3397
|
+
ok: true,
|
|
3398
|
+
memory
|
|
3399
|
+
} })
|
|
3400
|
+
];
|
|
3401
|
+
}
|
|
3402
|
+
return [memoryDomainEvents.memoryUpserted({
|
|
3403
|
+
sessionId,
|
|
3404
|
+
memory
|
|
3405
|
+
}), memoryRestEvents.mutationFinished({ outcome: {
|
|
3406
|
+
requestId,
|
|
3407
|
+
sessionId,
|
|
3408
|
+
ok: true,
|
|
3409
|
+
memory
|
|
3410
|
+
} })];
|
|
3411
|
+
}
|
|
3412
|
+
/** Performs a create/supersede/retire HTTP call and emits its resulting actions. */
|
|
3413
|
+
function createMemoryMutationObservable(environment, context, request) {
|
|
3414
|
+
const method = request.kind === "add" ? "POST" : request.kind === "update" ? "PATCH" : "DELETE";
|
|
3415
|
+
const path = request.kind === "add" ? MEMORIES_PATH : `${MEMORIES_PATH}/${encodeURIComponent(request.id)}`;
|
|
3416
|
+
return defer(() => memoryFromFetch(`${context.runtimeUrl}${path}`, {
|
|
3417
|
+
selector: async (response) => {
|
|
3418
|
+
if (!response.ok) throw new MemoryError("MEMORY_MUTATION_FAILED", {
|
|
3419
|
+
message: `Request failed: ${response.status}`,
|
|
3420
|
+
retryable: isRetryableStatus(response.status)
|
|
3421
|
+
});
|
|
3422
|
+
return request.kind === "remove" ? null : await response.json();
|
|
3423
|
+
},
|
|
3424
|
+
fetch: environment.fetch,
|
|
3425
|
+
method,
|
|
3426
|
+
headers: {
|
|
3427
|
+
...context.headers,
|
|
3428
|
+
"Content-Type": "application/json"
|
|
3429
|
+
},
|
|
3430
|
+
body: request.kind === "remove" ? void 0 : JSON.stringify(request.body)
|
|
3431
|
+
}).pipe(timeout({
|
|
3432
|
+
first: REQUEST_TIMEOUT_MS$1,
|
|
3433
|
+
with: () => {
|
|
3434
|
+
throw new MemoryError("MEMORY_REQUEST_TIMEOUT");
|
|
3435
|
+
}
|
|
3436
|
+
}), mergeMap((data) => of(...buildMutationSuccessActions(request, data))), catchError((error) => of(memoryRestEvents.mutationFinished({ outcome: {
|
|
3437
|
+
requestId: request.requestId,
|
|
3438
|
+
sessionId: request.sessionId,
|
|
3439
|
+
ok: false,
|
|
3440
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
3441
|
+
} })))));
|
|
3442
|
+
}
|
|
3443
|
+
/**
|
|
3444
|
+
* Creates the framework-agnostic memory store: a REST snapshot on `setContext`,
|
|
3445
|
+
* server-authoritative add/update/remove mutations, and realtime
|
|
3446
|
+
* `memory_metadata` deltas off the injected `user_meta` event source — all
|
|
3447
|
+
* reduced into observable state.
|
|
3448
|
+
*/
|
|
3449
|
+
function createMemoryStore(environment) {
|
|
3450
|
+
let memoryRequestId = 0;
|
|
3451
|
+
const createMemoryRequestId = () => {
|
|
3452
|
+
memoryRequestId += 1;
|
|
3453
|
+
return `memory-request-${memoryRequestId}`;
|
|
3454
|
+
};
|
|
3455
|
+
const bootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.listRequested({ sessionId: state.sessionId }))));
|
|
3456
|
+
const credentialsBootstrapEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.contextChanged), withLatestFrom(state$), filter(([, state]) => Boolean(state.context)), map(([, state]) => memoryRestEvents.credentialsRequested({ sessionId: state.sessionId }))));
|
|
3457
|
+
const credentialsFetchEffect = createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.credentialsRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
|
|
3458
|
+
action,
|
|
3459
|
+
context
|
|
3460
|
+
})), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryCredentialsFetchObservable(environment, context, currentAction.sessionId))))));
|
|
3461
|
+
const store = createStore({
|
|
3462
|
+
reducer: memoryReducer,
|
|
3463
|
+
effects: [
|
|
3464
|
+
bootstrapEffect,
|
|
3465
|
+
credentialsBootstrapEffect,
|
|
3466
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.listRequested), switchMap$1((action) => state$.pipe(map((state) => state.context), filter((context) => Boolean(context)), take(1), map((context) => ({
|
|
3467
|
+
action,
|
|
3468
|
+
context
|
|
3469
|
+
})), takeUntil(actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped))), switchMap$1(({ action: currentAction, context }) => createMemoryFetchObservable(environment, context, currentAction.sessionId)))))),
|
|
3470
|
+
credentialsFetchEffect,
|
|
3471
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(memoryAdapterEvents.addRequested, memoryAdapterEvents.updateRequested, memoryAdapterEvents.removeRequested), withLatestFrom(state$), mergeMap(([action, state]) => {
|
|
3472
|
+
const context = state.context;
|
|
3473
|
+
const sessionId = state.sessionId;
|
|
3474
|
+
if (!context?.runtimeUrl) return of(memoryRestEvents.mutationFinished({ outcome: {
|
|
3475
|
+
requestId: action.requestId,
|
|
3476
|
+
sessionId,
|
|
3477
|
+
ok: false,
|
|
3478
|
+
error: /* @__PURE__ */ new Error("Runtime URL is not configured")
|
|
3479
|
+
} }));
|
|
3480
|
+
if (memoryAdapterEvents.addRequested.match(action)) return createMemoryMutationObservable(environment, context, {
|
|
3481
|
+
requestId: action.requestId,
|
|
3482
|
+
sessionId,
|
|
3483
|
+
kind: "add",
|
|
3484
|
+
body: toMutationBody(action.input)
|
|
3485
|
+
});
|
|
3486
|
+
if (memoryAdapterEvents.updateRequested.match(action)) return createMemoryMutationObservable(environment, context, {
|
|
3487
|
+
requestId: action.requestId,
|
|
3488
|
+
sessionId,
|
|
3489
|
+
kind: "update",
|
|
3490
|
+
id: action.id,
|
|
3491
|
+
body: toMutationBody(action.changes)
|
|
3492
|
+
});
|
|
3493
|
+
return createMemoryMutationObservable(environment, context, {
|
|
3494
|
+
requestId: action.requestId,
|
|
3495
|
+
sessionId,
|
|
3496
|
+
kind: "remove",
|
|
3497
|
+
id: action.id
|
|
3498
|
+
});
|
|
3499
|
+
}))),
|
|
3500
|
+
createEffect((actions$, state$) => actions$.pipe(ofType(memoryRestEvents.credentialsSucceeded), withLatestFrom(state$), filter(([action, state]) => {
|
|
3501
|
+
return action.sessionId === state.sessionId && Boolean(state.context?.wsUrl);
|
|
3502
|
+
}), switchMap$1(([action, state]) => {
|
|
3503
|
+
const context = state.context;
|
|
3504
|
+
const { joinToken, joinCode } = action;
|
|
3505
|
+
const sessionId = action.sessionId;
|
|
3506
|
+
const shutdown$ = actions$.pipe(ofType(memoryAdapterEvents.contextChanged, memoryAdapterEvents.stopped));
|
|
3507
|
+
return defer(() => {
|
|
3508
|
+
const socket$ = ɵphoenixSocket$({
|
|
3509
|
+
url: context.wsUrl,
|
|
3510
|
+
options: {
|
|
3511
|
+
params: { join_token: joinToken },
|
|
3512
|
+
reconnectAfterMs: phoenixExponentialBackoff(100, 1e4),
|
|
3513
|
+
rejoinAfterMs: phoenixExponentialBackoff(1e3, 3e4)
|
|
3514
|
+
}
|
|
3515
|
+
}).pipe(shareReplay({
|
|
3516
|
+
bufferSize: 1,
|
|
3517
|
+
refCount: true
|
|
3518
|
+
}));
|
|
3519
|
+
const channel$ = ɵphoenixChannel$({
|
|
3520
|
+
socket$,
|
|
3521
|
+
topic: `user_meta:memories:${joinCode}`
|
|
3522
|
+
}).pipe(shareReplay({
|
|
3523
|
+
bufferSize: 1,
|
|
3524
|
+
refCount: true
|
|
3525
|
+
}));
|
|
3526
|
+
const fatalSocketShutdown$ = ɵobservePhoenixSocketHealth$(ɵobservePhoenixSocketSignals$(socket$).pipe(share()), MAX_SOCKET_RETRIES$1).pipe(catchError(() => {
|
|
3527
|
+
console.warn(`[memory] WebSocket failed after ${MAX_SOCKET_RETRIES$1} attempts, giving up`);
|
|
3528
|
+
return of(void 0);
|
|
3529
|
+
}), share());
|
|
3530
|
+
const fatalStatus$ = fatalSocketShutdown$.pipe(map(() => memoryDomainEvents.realtimeUnavailable({ sessionId })));
|
|
3531
|
+
const metadata$ = channel$.pipe(switchMap$1(({ channel }) => ɵobservePhoenixEvent$(channel, MEMORY_METADATA_EVENT)), map((event) => mapMemoryMetadataEvent(event, action.sessionId)));
|
|
3532
|
+
const join$ = ɵjoinPhoenixChannel$(channel$).pipe(concatWith(of(memoryDomainEvents.realtimeConnected({ sessionId }))), catchError((error) => {
|
|
3533
|
+
console.warn(`[memory] failed to join user_meta:memories:${joinCode}`, error);
|
|
3534
|
+
return of(memoryDomainEvents.realtimeUnavailable({ sessionId }));
|
|
3535
|
+
}));
|
|
3536
|
+
return merge(of(memoryDomainEvents.realtimeConnecting({ sessionId })), metadata$, join$, fatalStatus$).pipe(takeUntil(merge(shutdown$, fatalSocketShutdown$.pipe(observeOn(asapScheduler)))), finalize(() => {}));
|
|
3537
|
+
});
|
|
3538
|
+
})))
|
|
3539
|
+
]
|
|
3540
|
+
});
|
|
3541
|
+
function trackMutation(dispatchAction, extract) {
|
|
3542
|
+
const { requestId } = dispatchAction;
|
|
3543
|
+
const resultPromise = firstValueFrom(merge(store.actions$.pipe(ofType(memoryRestEvents.mutationFinished), filter((action) => action.outcome.requestId === requestId), map((action) => action.outcome)), store.actions$.pipe(ofType(memoryAdapterEvents.stopped), map(() => ({
|
|
3544
|
+
requestId,
|
|
3545
|
+
sessionId: store.getState().sessionId,
|
|
3546
|
+
ok: false,
|
|
3547
|
+
error: /* @__PURE__ */ new Error("Memory store stopped before mutation completed")
|
|
3548
|
+
})))).pipe(take(1))).then((outcome) => {
|
|
3549
|
+
if (!outcome.ok) throw outcome.error;
|
|
3550
|
+
return extract(outcome);
|
|
3551
|
+
});
|
|
3552
|
+
store.dispatch(dispatchAction);
|
|
3553
|
+
return resultPromise;
|
|
3554
|
+
}
|
|
3555
|
+
return {
|
|
3556
|
+
start() {
|
|
3557
|
+
store.init();
|
|
3558
|
+
store.dispatch(memoryAdapterEvents.started());
|
|
3559
|
+
},
|
|
3560
|
+
stop() {
|
|
3561
|
+
store.dispatch(memoryAdapterEvents.stopped());
|
|
3562
|
+
store.stop();
|
|
3563
|
+
},
|
|
3564
|
+
setContext(context) {
|
|
3565
|
+
store.dispatch(memoryAdapterEvents.contextChanged({ context }));
|
|
3566
|
+
},
|
|
3567
|
+
refresh() {
|
|
3568
|
+
const { sessionId, context } = store.getState();
|
|
3569
|
+
if (!context) return Promise.resolve();
|
|
3570
|
+
const done = firstValueFrom(merge(store.actions$.pipe(ofType(memoryRestEvents.listSucceeded, memoryRestEvents.listFailed, memoryRestEvents.listUnavailable), filter((action) => action.sessionId === sessionId)), store.actions$.pipe(ofType(memoryAdapterEvents.stopped)), store.actions$.pipe(ofType(memoryAdapterEvents.contextChanged))).pipe(take(1))).then((action) => {
|
|
3571
|
+
if (memoryRestEvents.listFailed.match(action)) throw action.error;
|
|
3572
|
+
if (memoryAdapterEvents.stopped.match(action)) throw new Error("Memory store stopped before refresh completed");
|
|
3573
|
+
if (memoryAdapterEvents.contextChanged.match(action)) return;
|
|
3574
|
+
});
|
|
3575
|
+
store.dispatch(memoryRestEvents.listRequested({ sessionId }));
|
|
3576
|
+
return done;
|
|
3577
|
+
},
|
|
3578
|
+
addMemory(input) {
|
|
3579
|
+
return trackMutation(memoryAdapterEvents.addRequested({
|
|
3580
|
+
requestId: createMemoryRequestId(),
|
|
3581
|
+
input
|
|
3582
|
+
}), (outcome) => {
|
|
3583
|
+
if (outcome.memory == null) throw new Error("add resolved without a memory");
|
|
3584
|
+
return outcome.memory;
|
|
3585
|
+
});
|
|
3586
|
+
},
|
|
3587
|
+
updateMemory(id, changes) {
|
|
3588
|
+
return trackMutation(memoryAdapterEvents.updateRequested({
|
|
3589
|
+
requestId: createMemoryRequestId(),
|
|
3590
|
+
id,
|
|
3591
|
+
changes
|
|
3592
|
+
}), (outcome) => {
|
|
3593
|
+
if (outcome.memory == null) throw new Error("update resolved without a memory");
|
|
3594
|
+
return outcome.memory;
|
|
3595
|
+
});
|
|
3596
|
+
},
|
|
3597
|
+
removeMemory(id) {
|
|
3598
|
+
return trackMutation(memoryAdapterEvents.removeRequested({
|
|
3599
|
+
requestId: createMemoryRequestId(),
|
|
3600
|
+
id
|
|
3601
|
+
}), () => void 0);
|
|
3602
|
+
},
|
|
3603
|
+
getState() {
|
|
3604
|
+
return store.getState();
|
|
3605
|
+
},
|
|
3606
|
+
getServerState() {
|
|
3607
|
+
return initialMemoryState;
|
|
3608
|
+
},
|
|
3609
|
+
select: store.select.bind(store)
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
const ɵmemoryAdapterEvents = memoryAdapterEvents;
|
|
3613
|
+
const ɵmemoryRestEvents = memoryRestEvents;
|
|
3614
|
+
const ɵmemoryDomainEvents = memoryDomainEvents;
|
|
3615
|
+
const ɵmemoryReducer = memoryReducer;
|
|
3616
|
+
const ɵmapMemoryMetadataEvent = mapMemoryMetadataEvent;
|
|
3617
|
+
const ɵselectMemories = selectMemories;
|
|
3618
|
+
const ɵselectMemoriesIsLoading = selectMemoriesIsLoading;
|
|
3619
|
+
const ɵselectMemoriesError = selectMemoriesError;
|
|
3620
|
+
const ɵselectMemoriesIsMutating = selectMemoriesIsMutating;
|
|
3621
|
+
const ɵselectMemoriesAvailable = selectMemoriesAvailable;
|
|
3622
|
+
const ɵselectMemoriesRealtimeStatus = selectMemoriesRealtimeStatus;
|
|
3623
|
+
|
|
3624
|
+
//#endregion
|
|
3625
|
+
//#region src/core/core.ts
|
|
3626
|
+
let CopilotKitCoreErrorCode = /* @__PURE__ */ function(CopilotKitCoreErrorCode) {
|
|
3627
|
+
CopilotKitCoreErrorCode["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
|
|
3628
|
+
CopilotKitCoreErrorCode["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
|
|
3629
|
+
CopilotKitCoreErrorCode["AGENT_RUN_FAILED"] = "agent_run_failed";
|
|
3630
|
+
CopilotKitCoreErrorCode["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
|
|
3631
|
+
CopilotKitCoreErrorCode["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
|
|
3632
|
+
CopilotKitCoreErrorCode["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
|
|
3633
|
+
CopilotKitCoreErrorCode["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
|
|
3634
|
+
CopilotKitCoreErrorCode["TOOL_NOT_FOUND"] = "tool_not_found";
|
|
3635
|
+
CopilotKitCoreErrorCode["AGENT_NOT_FOUND"] = "agent_not_found";
|
|
3636
|
+
/**
|
|
3637
|
+
* Emitted when an agent run fails because the thread is already locked
|
|
3638
|
+
* by another active run.
|
|
3639
|
+
*
|
|
3640
|
+
* @example
|
|
3641
|
+
* ```tsx
|
|
3642
|
+
* <CopilotKitProvider
|
|
3643
|
+
* onError={({ code, error, context }) => {
|
|
3644
|
+
* if (code === "agent_thread_locked") {
|
|
3645
|
+
* // Show "Agent is busy, retry?" UI
|
|
3646
|
+
* }
|
|
3647
|
+
* }}
|
|
3648
|
+
* />
|
|
3649
|
+
* ```
|
|
3650
|
+
*/
|
|
3651
|
+
CopilotKitCoreErrorCode["AGENT_THREAD_LOCKED"] = "agent_thread_locked";
|
|
3652
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_FAILED"] = "transcription_failed";
|
|
3653
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_SERVICE_NOT_CONFIGURED"] = "transcription_service_not_configured";
|
|
3654
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_INVALID_AUDIO"] = "transcription_invalid_audio";
|
|
3655
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_RATE_LIMITED"] = "transcription_rate_limited";
|
|
3656
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_AUTH_FAILED"] = "transcription_auth_failed";
|
|
3657
|
+
CopilotKitCoreErrorCode["TRANSCRIPTION_NETWORK_ERROR"] = "transcription_network_error";
|
|
3658
|
+
CopilotKitCoreErrorCode["SUBSCRIBER_CALLBACK_FAILED"] = "subscriber_callback_failed";
|
|
3659
|
+
return CopilotKitCoreErrorCode;
|
|
3660
|
+
}({});
|
|
3661
|
+
/**
|
|
3662
|
+
* Runtime allowlist derived from {@link SUBSCRIBE_TO_AGENT_KEYS}. Hoisted
|
|
3663
|
+
* to module scope so the Set is allocated once, not per-subscription.
|
|
3664
|
+
*/
|
|
3665
|
+
const ALLOWED_KEYS = new Set([
|
|
3666
|
+
"onMessagesChanged",
|
|
3667
|
+
"onStateChanged",
|
|
3668
|
+
"onRunInitialized",
|
|
3669
|
+
"onRunFinalized",
|
|
3670
|
+
"onRunFailed",
|
|
3671
|
+
"onRunErrorEvent"
|
|
3672
|
+
]);
|
|
3673
|
+
let CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ function(CopilotKitCoreRuntimeConnectionStatus) {
|
|
3674
|
+
CopilotKitCoreRuntimeConnectionStatus["Disconnected"] = "disconnected";
|
|
3675
|
+
CopilotKitCoreRuntimeConnectionStatus["Connected"] = "connected";
|
|
3676
|
+
CopilotKitCoreRuntimeConnectionStatus["Connecting"] = "connecting";
|
|
3677
|
+
CopilotKitCoreRuntimeConnectionStatus["Error"] = "error";
|
|
3678
|
+
return CopilotKitCoreRuntimeConnectionStatus;
|
|
3679
|
+
}({});
|
|
3680
|
+
/**
|
|
3681
|
+
* Normalize a header map to the internal invariant: a `Record<string, string>`
|
|
3682
|
+
* with no `null`/`undefined` values. Entries whose value is `null`/`undefined`
|
|
3683
|
+
* are dropped (this is how a header is cleared). Shared by the constructor and
|
|
3684
|
+
* `setHeaders` so both write paths into `_headers` enforce the same invariant.
|
|
3685
|
+
*/
|
|
3686
|
+
function normalizeHeaders(headers) {
|
|
3687
|
+
return Object.fromEntries(Object.entries(headers).filter((entry) => entry[1] != null));
|
|
3688
|
+
}
|
|
3689
|
+
var CopilotKitCore = class {
|
|
3690
|
+
_headers;
|
|
3691
|
+
_credentials;
|
|
3692
|
+
_properties;
|
|
3693
|
+
_defaultThrottleMs;
|
|
3694
|
+
_debug;
|
|
3695
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
3696
|
+
agentRegistry;
|
|
3697
|
+
contextStore;
|
|
3698
|
+
suggestionEngine;
|
|
3699
|
+
runHandler;
|
|
3700
|
+
stateManager;
|
|
3701
|
+
threadStoreRegistry;
|
|
3702
|
+
/**
|
|
3703
|
+
* The single core-owned memory store, created lazily on first
|
|
3704
|
+
* `getMemoryStore()` and kept user-scoped for the lifetime of the core.
|
|
3705
|
+
* Its runtime context is wired by the core itself (see `syncMemoryContext`),
|
|
3706
|
+
* so callers never register or look it up by `agentId`.
|
|
3707
|
+
*/
|
|
3708
|
+
_memoryStore;
|
|
3709
|
+
/**
|
|
3710
|
+
* Tracks the agent IDs from the most recent `onAgentsChanged` notification.
|
|
3711
|
+
* Used to gate thread-store auto-unregister so the FIRST empty-agents
|
|
3712
|
+
* notification (before published agents are merged in) does not rip out a
|
|
3713
|
+
* store that was registered prior to that initial notification.
|
|
3714
|
+
*/
|
|
3715
|
+
previousAgentIds = /* @__PURE__ */ new Set();
|
|
3716
|
+
constructor({ runtimeUrl, runtimeTransport = "auto", deferInitialConnection = false, headers = {}, credentials, properties = {}, agents__unsafe_dev_only = {}, tools = [], suggestionsConfig = [], debug }) {
|
|
3717
|
+
this._headers = normalizeHeaders(headers);
|
|
3718
|
+
this._credentials = credentials;
|
|
3719
|
+
this._properties = properties;
|
|
3720
|
+
this._debug = debug;
|
|
3721
|
+
this.agentRegistry = new AgentRegistry(this);
|
|
3722
|
+
this.contextStore = new ContextStore(this);
|
|
3723
|
+
this.suggestionEngine = new SuggestionEngine(this);
|
|
3724
|
+
this.runHandler = new RunHandler(this);
|
|
3725
|
+
this.stateManager = new StateManager(this);
|
|
3726
|
+
this.threadStoreRegistry = new ThreadStoreRegistry(this);
|
|
3727
|
+
this.agentRegistry.initialize(agents__unsafe_dev_only);
|
|
3728
|
+
this.runHandler.initialize(tools);
|
|
3729
|
+
this.suggestionEngine.initialize(suggestionsConfig);
|
|
3730
|
+
this.stateManager.initialize();
|
|
3731
|
+
this.agentRegistry.setRuntimeTransport(runtimeTransport);
|
|
3732
|
+
this.agentRegistry.setRuntimeUrl(runtimeUrl, { deferConnection: deferInitialConnection });
|
|
3733
|
+
this.previousAgentIds = new Set(Object.keys(agents__unsafe_dev_only));
|
|
3734
|
+
this.subscribe({
|
|
3735
|
+
onRuntimeConnectionStatusChanged: () => {
|
|
3736
|
+
if (this._memoryStore) this.syncMemoryContext();
|
|
3737
|
+
},
|
|
3738
|
+
onAgentsChanged: ({ agents }) => {
|
|
3739
|
+
Object.values(agents).forEach((agent) => {
|
|
3740
|
+
if (agent.agentId) this.stateManager.subscribeToAgent(agent);
|
|
3741
|
+
});
|
|
3742
|
+
const currentAgentIds = new Set(Object.keys(agents));
|
|
3743
|
+
for (const agentId of Object.keys(this.threadStoreRegistry.getAll())) if (this.previousAgentIds.has(agentId) && !currentAgentIds.has(agentId)) try {
|
|
3744
|
+
this.threadStoreRegistry.unregister(agentId);
|
|
3745
|
+
} catch (err) {
|
|
3746
|
+
console.error(`CopilotKitCore.onAgentsChanged: threadStoreRegistry.unregister failed for "${agentId}":`, err);
|
|
3747
|
+
}
|
|
3748
|
+
for (const agentId of this.previousAgentIds) if (!currentAgentIds.has(agentId)) try {
|
|
3749
|
+
this.stateManager.unsubscribeFromAgent(agentId);
|
|
3750
|
+
} catch (err) {
|
|
3751
|
+
console.error(`CopilotKitCore.onAgentsChanged: stateManager.unsubscribeFromAgent failed for "${agentId}":`, err);
|
|
3752
|
+
}
|
|
3753
|
+
this.previousAgentIds = currentAgentIds;
|
|
3754
|
+
}
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
/**
|
|
3758
|
+
* Internal method used by delegate classes and subclasses to notify subscribers
|
|
3759
|
+
*/
|
|
3760
|
+
async notifySubscribers(handler, errorMessage) {
|
|
3761
|
+
await Promise.all(Array.from(this.subscribers).map(async (subscriber) => {
|
|
3762
|
+
try {
|
|
3763
|
+
await handler(subscriber);
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
console.error(errorMessage, error);
|
|
3766
|
+
}
|
|
3767
|
+
}));
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Internal method used by delegate classes to emit errors
|
|
3771
|
+
*/
|
|
3772
|
+
async emitError({ error, code, context = {} }) {
|
|
3773
|
+
await this.notifySubscribers((subscriber) => subscriber.onError?.({
|
|
3774
|
+
copilotkit: this,
|
|
3775
|
+
error,
|
|
3776
|
+
code,
|
|
3777
|
+
context
|
|
3778
|
+
}), "Subscriber onError error:");
|
|
3779
|
+
}
|
|
3780
|
+
/**
|
|
3781
|
+
* Log a message to the console and emit an error to subscribers.
|
|
3782
|
+
* Catches failures from `emitError` itself to prevent unhandled rejections.
|
|
3783
|
+
*/
|
|
2714
3784
|
logAndEmitError(message, params, logLevel = "error") {
|
|
2715
3785
|
console[logLevel](message, params.error);
|
|
2716
3786
|
this.emitError(params).catch((emitErr) => {
|
|
@@ -2735,6 +3805,18 @@ var CopilotKitCore = class {
|
|
|
2735
3805
|
setRuntimeUrl(runtimeUrl) {
|
|
2736
3806
|
this.agentRegistry.setRuntimeUrl(runtimeUrl);
|
|
2737
3807
|
}
|
|
3808
|
+
/**
|
|
3809
|
+
* Start the runtime `/info` connection if it has not been started yet.
|
|
3810
|
+
*
|
|
3811
|
+
* Intended to be driven from a host's commit-phase effect when the core was
|
|
3812
|
+
* constructed with {@link CopilotKitCoreConfig.deferInitialConnection}. Safe
|
|
3813
|
+
* to call repeatedly — it is a no-op once a connection is in progress or
|
|
3814
|
+
* settled, so a double-invoked mount effect (React StrictMode) collapses to a
|
|
3815
|
+
* single request. See #5801.
|
|
3816
|
+
*/
|
|
3817
|
+
connect() {
|
|
3818
|
+
this.agentRegistry.connectRuntime();
|
|
3819
|
+
}
|
|
2738
3820
|
get runtimeTransport() {
|
|
2739
3821
|
return this.agentRegistry.runtimeTransport;
|
|
2740
3822
|
}
|
|
@@ -2803,6 +3885,9 @@ var CopilotKitCore = class {
|
|
|
2803
3885
|
get threadEndpoints() {
|
|
2804
3886
|
return this.agentRegistry.threadEndpoints;
|
|
2805
3887
|
}
|
|
3888
|
+
get suggestions() {
|
|
3889
|
+
return this.agentRegistry.suggestions;
|
|
3890
|
+
}
|
|
2806
3891
|
get a2uiEnabled() {
|
|
2807
3892
|
return this.agentRegistry.a2uiEnabled;
|
|
2808
3893
|
}
|
|
@@ -2856,6 +3941,7 @@ var CopilotKitCore = class {
|
|
|
2856
3941
|
*/
|
|
2857
3942
|
setHeaders(headers) {
|
|
2858
3943
|
this._headers = normalizeHeaders(headers);
|
|
3944
|
+
if (this._memoryStore) this.syncMemoryContext();
|
|
2859
3945
|
this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
|
|
2860
3946
|
this.notifySubscribers((subscriber) => subscriber.onHeadersChanged?.({
|
|
2861
3947
|
copilotkit: this,
|
|
@@ -2961,6 +4047,46 @@ var CopilotKitCore = class {
|
|
|
2961
4047
|
return this.threadStoreRegistry.getAll();
|
|
2962
4048
|
}
|
|
2963
4049
|
/**
|
|
4050
|
+
* Returns the single core-owned, user-scoped memory store, creating and
|
|
4051
|
+
* starting it on first access. Unlike thread stores, memory is not scoped per
|
|
4052
|
+
* agent: there is exactly one store whose runtime context the core wires
|
|
4053
|
+
* itself (see `syncMemoryContext`), so consumers (e.g. a `useMemories`
|
|
4054
|
+
* binding) just read this store rather than registering one.
|
|
4055
|
+
*/
|
|
4056
|
+
getMemoryStore() {
|
|
4057
|
+
return this.ensureMemoryStore();
|
|
4058
|
+
}
|
|
4059
|
+
/**
|
|
4060
|
+
* Lazily creates, starts, and context-syncs the core-owned memory store on
|
|
4061
|
+
* first access, then returns it. Subsequent calls return the existing store.
|
|
4062
|
+
* The store is constructed with a bound `globalThis.fetch` and immediately
|
|
4063
|
+
* has its runtime context synced from the current connection state.
|
|
4064
|
+
*/
|
|
4065
|
+
ensureMemoryStore() {
|
|
4066
|
+
if (!this._memoryStore) {
|
|
4067
|
+
this._memoryStore = createMemoryStore({ fetch: globalThis.fetch.bind(globalThis) });
|
|
4068
|
+
this._memoryStore.start();
|
|
4069
|
+
this.syncMemoryContext();
|
|
4070
|
+
}
|
|
4071
|
+
return this._memoryStore;
|
|
4072
|
+
}
|
|
4073
|
+
/**
|
|
4074
|
+
* Pushes the current runtime wiring into the memory store. When the runtime
|
|
4075
|
+
* is connected and both the intelligence WebSocket URL and runtime URL are
|
|
4076
|
+
* available, the store receives a context (runtime URL, WebSocket URL, and a
|
|
4077
|
+
* copy of the current headers); otherwise its context is cleared. No-op when
|
|
4078
|
+
* the store has not been created yet.
|
|
4079
|
+
*/
|
|
4080
|
+
syncMemoryContext() {
|
|
4081
|
+
if (!this._memoryStore) return;
|
|
4082
|
+
if (this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connected && this.intelligence?.wsUrl && this.runtimeUrl) this._memoryStore.setContext({
|
|
4083
|
+
runtimeUrl: this.runtimeUrl,
|
|
4084
|
+
wsUrl: this.intelligence.wsUrl,
|
|
4085
|
+
headers: { ...this.headers }
|
|
4086
|
+
});
|
|
4087
|
+
else this._memoryStore.setContext(null);
|
|
4088
|
+
}
|
|
4089
|
+
/**
|
|
2964
4090
|
* Suggestions management (delegated to SuggestionEngine)
|
|
2965
4091
|
*/
|
|
2966
4092
|
addSuggestionsConfig(config) {
|
|
@@ -3123,510 +4249,253 @@ var CopilotKitCore = class {
|
|
|
3123
4249
|
throttler.maybeExecute();
|
|
3124
4250
|
};
|
|
3125
4251
|
if (subscriber.onStateChanged) wrappedSubscriber.onStateChanged = (params) => {
|
|
3126
|
-
latestStateParams = params;
|
|
3127
|
-
throttler.maybeExecute();
|
|
3128
|
-
};
|
|
3129
|
-
const subscription = agent.subscribe(wrappedSubscriber);
|
|
3130
|
-
return { unsubscribe: () => {
|
|
3131
|
-
active = false;
|
|
3132
|
-
throttler.cancel();
|
|
3133
|
-
subscription.unsubscribe();
|
|
3134
|
-
} };
|
|
3135
|
-
}
|
|
3136
|
-
/**
|
|
3137
|
-
* Agent connectivity (delegated to RunHandler)
|
|
3138
|
-
*/
|
|
3139
|
-
async connectAgent(params) {
|
|
3140
|
-
return this.runHandler.connectAgent(params);
|
|
3141
|
-
}
|
|
3142
|
-
stopAgent(params) {
|
|
3143
|
-
this.runHandler.abortCurrentRun();
|
|
3144
|
-
params.agent.abortRun();
|
|
3145
|
-
}
|
|
3146
|
-
async runAgent(params) {
|
|
3147
|
-
return this.runHandler.runAgent(params);
|
|
3148
|
-
}
|
|
3149
|
-
/**
|
|
3150
|
-
* Programmatically execute a registered frontend tool without going through an LLM turn.
|
|
3151
|
-
* The handler runs, render components show up in the UI, and both the tool call and
|
|
3152
|
-
* result messages are added to `agent.messages`.
|
|
3153
|
-
*/
|
|
3154
|
-
async runTool(params) {
|
|
3155
|
-
return this.runHandler.runTool(params);
|
|
3156
|
-
}
|
|
3157
|
-
/**
|
|
3158
|
-
* State management (delegated to StateManager)
|
|
3159
|
-
*/
|
|
3160
|
-
getStateByRun(agentId, threadId, runId) {
|
|
3161
|
-
return this.stateManager.getStateByRun(agentId, threadId, runId);
|
|
3162
|
-
}
|
|
3163
|
-
getRunIdForMessage(agentId, threadId, messageId) {
|
|
3164
|
-
return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
|
|
3165
|
-
}
|
|
3166
|
-
getRunIdsForThread(agentId, threadId) {
|
|
3167
|
-
return this.stateManager.getRunIdsForThread(agentId, threadId);
|
|
3168
|
-
}
|
|
3169
|
-
/**
|
|
3170
|
-
* Internal method used by RunHandler to build frontend tools
|
|
3171
|
-
*/
|
|
3172
|
-
buildFrontendTools(agentId) {
|
|
3173
|
-
return this.runHandler.buildFrontendTools(agentId);
|
|
3174
|
-
}
|
|
3175
|
-
/**
|
|
3176
|
-
* Called before each follow-up agent run (after tool execution).
|
|
3177
|
-
*
|
|
3178
|
-
* When a frontend tool handler calls framework state setters (e.g. React's
|
|
3179
|
-
* setState), those updates are batched and deferred — they do not take effect
|
|
3180
|
-
* until the framework's scheduler runs (React uses MessageChannel).
|
|
3181
|
-
* useAgentContext registers context via useLayoutEffect, which runs
|
|
3182
|
-
* synchronously after React commits that deferred batch.
|
|
3183
|
-
*
|
|
3184
|
-
* Without yielding here, the follow-up runAgent reads the context store
|
|
3185
|
-
* synchronously while the deferred updates are still pending, producing stale
|
|
3186
|
-
* context for the next agent turn.
|
|
3187
|
-
*
|
|
3188
|
-
* Override in framework-specific subclasses to yield to the framework
|
|
3189
|
-
* scheduler before the follow-up run. The base implementation is a no-op
|
|
3190
|
-
* because non-React environments have no deferred state to flush.
|
|
3191
|
-
*/
|
|
3192
|
-
async waitForPendingFrameworkUpdates() {}
|
|
3193
|
-
};
|
|
3194
|
-
|
|
3195
|
-
//#endregion
|
|
3196
|
-
//#region src/types.ts
|
|
3197
|
-
/**
|
|
3198
|
-
* Status of a tool call execution
|
|
3199
|
-
*/
|
|
3200
|
-
let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
|
|
3201
|
-
ToolCallStatus["InProgress"] = "inProgress";
|
|
3202
|
-
ToolCallStatus["Executing"] = "executing";
|
|
3203
|
-
ToolCallStatus["Complete"] = "complete";
|
|
3204
|
-
return ToolCallStatus;
|
|
3205
|
-
}({});
|
|
3206
|
-
|
|
3207
|
-
//#endregion
|
|
3208
|
-
//#region src/utils/markdown.ts
|
|
3209
|
-
function completePartialMarkdown(input) {
|
|
3210
|
-
let s = input;
|
|
3211
|
-
const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
|
|
3212
|
-
if (fenceMatches.length % 2 === 1) {
|
|
3213
|
-
const [, indent, fence] = fenceMatches[0];
|
|
3214
|
-
s += `\n${indent}${fence}`;
|
|
3215
|
-
}
|
|
3216
|
-
if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
|
|
3217
|
-
const openElements = [];
|
|
3218
|
-
const chars = Array.from(s);
|
|
3219
|
-
const codeBlockRanges = [];
|
|
3220
|
-
const inlineCodeRanges = [];
|
|
3221
|
-
let tempCodeFenceCount = 0;
|
|
3222
|
-
let currentCodeBlockStart = -1;
|
|
3223
|
-
for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
|
|
3224
|
-
const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
|
|
3225
|
-
if (lineMatch) {
|
|
3226
|
-
tempCodeFenceCount++;
|
|
3227
|
-
if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
|
|
3228
|
-
else if (currentCodeBlockStart !== -1) {
|
|
3229
|
-
codeBlockRanges.push({
|
|
3230
|
-
start: currentCodeBlockStart,
|
|
3231
|
-
end: i + lineMatch[0].length
|
|
3232
|
-
});
|
|
3233
|
-
currentCodeBlockStart = -1;
|
|
3234
|
-
}
|
|
3235
|
-
i += lineMatch[0].length - 1;
|
|
3236
|
-
}
|
|
3237
|
-
}
|
|
3238
|
-
for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
|
|
3239
|
-
let backslashCount = 0;
|
|
3240
|
-
for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
|
|
3241
|
-
if (backslashCount % 2 === 0) {
|
|
3242
|
-
for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
|
|
3243
|
-
let closingBackslashCount = 0;
|
|
3244
|
-
for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
|
|
3245
|
-
if (closingBackslashCount % 2 === 0) {
|
|
3246
|
-
inlineCodeRanges.push({
|
|
3247
|
-
start: i,
|
|
3248
|
-
end: j + 1
|
|
3249
|
-
});
|
|
3250
|
-
i = j;
|
|
3251
|
-
break;
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
const isInCode = (pos) => {
|
|
3257
|
-
return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
|
|
3258
|
-
};
|
|
3259
|
-
for (let i = 0; i < chars.length; i++) {
|
|
3260
|
-
const char = chars[i];
|
|
3261
|
-
const nextChar = chars[i + 1];
|
|
3262
|
-
const prevChar = chars[i - 1];
|
|
3263
|
-
if (isInCode(i)) continue;
|
|
3264
|
-
if (char === "[") {
|
|
3265
|
-
let isCompleteLink = false;
|
|
3266
|
-
let bracketDepth = 1;
|
|
3267
|
-
let j = i + 1;
|
|
3268
|
-
while (j < chars.length && bracketDepth > 0) {
|
|
3269
|
-
if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
|
|
3270
|
-
if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
|
|
3271
|
-
j++;
|
|
3272
|
-
}
|
|
3273
|
-
if (bracketDepth === 0 && chars[j] === "(") {
|
|
3274
|
-
let parenDepth = 1;
|
|
3275
|
-
j++;
|
|
3276
|
-
while (j < chars.length && parenDepth > 0) {
|
|
3277
|
-
if (chars[j] === "(" && !isInCode(j)) parenDepth++;
|
|
3278
|
-
if (chars[j] === ")" && !isInCode(j)) parenDepth--;
|
|
3279
|
-
j++;
|
|
3280
|
-
}
|
|
3281
|
-
if (parenDepth === 0) {
|
|
3282
|
-
isCompleteLink = true;
|
|
3283
|
-
i = j - 1;
|
|
3284
|
-
continue;
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
if (!isCompleteLink) {
|
|
3288
|
-
const existingIndex = openElements.findIndex((el) => el.type === "bracket");
|
|
3289
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3290
|
-
else openElements.push({
|
|
3291
|
-
type: "bracket",
|
|
3292
|
-
marker: "[",
|
|
3293
|
-
position: i
|
|
3294
|
-
});
|
|
3295
|
-
}
|
|
3296
|
-
} else if (char === "*" && nextChar === "*") {
|
|
3297
|
-
const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
|
|
3298
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3299
|
-
else openElements.push({
|
|
3300
|
-
type: "bold_star",
|
|
3301
|
-
marker: "**",
|
|
3302
|
-
position: i
|
|
3303
|
-
});
|
|
3304
|
-
i++;
|
|
3305
|
-
} else if (char === "_" && nextChar === "_") {
|
|
3306
|
-
const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
|
|
3307
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3308
|
-
else openElements.push({
|
|
3309
|
-
type: "bold_underscore",
|
|
3310
|
-
marker: "__",
|
|
3311
|
-
position: i
|
|
3312
|
-
});
|
|
3313
|
-
i++;
|
|
3314
|
-
} else if (char === "~" && nextChar === "~") {
|
|
3315
|
-
const existingIndex = openElements.findIndex((el) => el.type === "strike");
|
|
3316
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3317
|
-
else openElements.push({
|
|
3318
|
-
type: "strike",
|
|
3319
|
-
marker: "~~",
|
|
3320
|
-
position: i
|
|
3321
|
-
});
|
|
3322
|
-
i++;
|
|
3323
|
-
} else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
|
|
3324
|
-
const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
|
|
3325
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3326
|
-
else openElements.push({
|
|
3327
|
-
type: "italic_star",
|
|
3328
|
-
marker: "*",
|
|
3329
|
-
position: i
|
|
3330
|
-
});
|
|
3331
|
-
} else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
|
|
3332
|
-
const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
|
|
3333
|
-
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
3334
|
-
else openElements.push({
|
|
3335
|
-
type: "italic_underscore",
|
|
3336
|
-
marker: "_",
|
|
3337
|
-
position: i
|
|
3338
|
-
});
|
|
3339
|
-
}
|
|
4252
|
+
latestStateParams = params;
|
|
4253
|
+
throttler.maybeExecute();
|
|
4254
|
+
};
|
|
4255
|
+
const subscription = agent.subscribe(wrappedSubscriber);
|
|
4256
|
+
return { unsubscribe: () => {
|
|
4257
|
+
active = false;
|
|
4258
|
+
throttler.cancel();
|
|
4259
|
+
subscription.unsubscribe();
|
|
4260
|
+
} };
|
|
3340
4261
|
}
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
switch (el.type) {
|
|
3347
|
-
case "bracket": return "]";
|
|
3348
|
-
case "bold_star": return "**";
|
|
3349
|
-
case "bold_underscore": return "__";
|
|
3350
|
-
case "strike": return "~~";
|
|
3351
|
-
case "italic_star": return "*";
|
|
3352
|
-
case "italic_underscore": return "_";
|
|
3353
|
-
default: return "";
|
|
3354
|
-
}
|
|
3355
|
-
});
|
|
3356
|
-
let result = s + closers.join("");
|
|
3357
|
-
const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
|
|
3358
|
-
const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
|
|
3359
|
-
const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
|
|
3360
|
-
let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
|
|
3361
|
-
if (shouldCloseParens) {
|
|
3362
|
-
const lastOpenParen = result.lastIndexOf("(");
|
|
3363
|
-
if (lastOpenParen !== -1) {
|
|
3364
|
-
if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
|
|
3365
|
-
}
|
|
4262
|
+
/**
|
|
4263
|
+
* Agent connectivity (delegated to RunHandler)
|
|
4264
|
+
*/
|
|
4265
|
+
async connectAgent(params) {
|
|
4266
|
+
return this.runHandler.connectAgent(params);
|
|
3366
4267
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
|
|
4268
|
+
stopAgent(params) {
|
|
4269
|
+
this.runHandler.abortCurrentRun();
|
|
4270
|
+
params.agent.abortRun();
|
|
3371
4271
|
}
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
//#endregion
|
|
3376
|
-
//#region src/utils/micro-redux.ts
|
|
3377
|
-
const INTERNAL_ACTION_TYPES = {
|
|
3378
|
-
boot: "@@micro-redux/boot",
|
|
3379
|
-
init: "@@micro-redux/init",
|
|
3380
|
-
stop: "@@micro-redux/stop"
|
|
3381
|
-
};
|
|
3382
|
-
const INTERNAL_BOOT_ACTION = { type: INTERNAL_ACTION_TYPES.boot };
|
|
3383
|
-
/**
|
|
3384
|
-
* Builds a typed action creator from a type string and payload factory.
|
|
3385
|
-
*/
|
|
3386
|
-
function createTypedActionCreator(type, factory) {
|
|
3387
|
-
const creator = ((...args) => ({
|
|
3388
|
-
...factory(...args),
|
|
3389
|
-
type
|
|
3390
|
-
}));
|
|
3391
|
-
creator.type = type;
|
|
3392
|
-
creator.match = (action) => action.type === type;
|
|
3393
|
-
return creator;
|
|
3394
|
-
}
|
|
3395
|
-
/**
|
|
3396
|
-
* Declares a payload-based action config for `createActionGroup`.
|
|
3397
|
-
*
|
|
3398
|
-
* @example
|
|
3399
|
-
* ```ts
|
|
3400
|
-
* const actions = createActionGroup("User", {
|
|
3401
|
-
* loaded: props<{ id: string }>(),
|
|
3402
|
-
* });
|
|
3403
|
-
* ```
|
|
3404
|
-
*/
|
|
3405
|
-
function props() {
|
|
3406
|
-
return { kind: "props" };
|
|
3407
|
-
}
|
|
3408
|
-
/**
|
|
3409
|
-
* Declares a no-payload action config for `createActionGroup`.
|
|
3410
|
-
*
|
|
3411
|
-
* @example
|
|
3412
|
-
* ```ts
|
|
3413
|
-
* const actions = createActionGroup("User", {
|
|
3414
|
-
* reset: empty(),
|
|
3415
|
-
* });
|
|
3416
|
-
* ```
|
|
3417
|
-
*/
|
|
3418
|
-
function empty() {
|
|
3419
|
-
return { kind: "empty" };
|
|
3420
|
-
}
|
|
3421
|
-
/**
|
|
3422
|
-
* Creates a namespaced group of typed action creators.
|
|
3423
|
-
*
|
|
3424
|
-
* Action types are formatted as: `[Source] actionName`.
|
|
3425
|
-
*/
|
|
3426
|
-
function createActionGroup(source, config) {
|
|
3427
|
-
const group = {};
|
|
3428
|
-
for (const eventName of Object.keys(config)) {
|
|
3429
|
-
const eventConfig = config[eventName];
|
|
3430
|
-
if (!eventConfig) continue;
|
|
3431
|
-
const actionType = `[${source}] ${eventName}`;
|
|
3432
|
-
if (eventConfig.kind === "props") {
|
|
3433
|
-
group[eventName] = createTypedActionCreator(actionType, (payload) => ({ ...payload }));
|
|
3434
|
-
continue;
|
|
3435
|
-
}
|
|
3436
|
-
group[eventName] = createTypedActionCreator(actionType, () => ({}));
|
|
4272
|
+
async runAgent(params) {
|
|
4273
|
+
return this.runHandler.runAgent(params);
|
|
3437
4274
|
}
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
*
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
function on(...args) {
|
|
3446
|
-
if (args.length < 2) throw new Error("on requires at least one action creator and one reducer");
|
|
3447
|
-
const reducer = args[args.length - 1];
|
|
3448
|
-
return {
|
|
3449
|
-
creators: args.slice(0, -1),
|
|
3450
|
-
reducer
|
|
3451
|
-
};
|
|
3452
|
-
}
|
|
3453
|
-
/**
|
|
3454
|
-
* Creates a reducer from an initial state and `on(...)` handler entries.
|
|
3455
|
-
*
|
|
3456
|
-
* Unknown action types return the current state unchanged.
|
|
3457
|
-
*/
|
|
3458
|
-
function createReducer(initialState, ...entries) {
|
|
3459
|
-
const reducerMap = /* @__PURE__ */ new Map();
|
|
3460
|
-
for (const entry of entries) for (const creator of entry.creators) {
|
|
3461
|
-
const handlers = reducerMap.get(creator.type) ?? [];
|
|
3462
|
-
handlers.push(entry.reducer);
|
|
3463
|
-
reducerMap.set(creator.type, handlers);
|
|
4275
|
+
/**
|
|
4276
|
+
* Programmatically execute a registered frontend tool without going through an LLM turn.
|
|
4277
|
+
* The handler runs, render components show up in the UI, and both the tool call and
|
|
4278
|
+
* result messages are added to `agent.messages`.
|
|
4279
|
+
*/
|
|
4280
|
+
async runTool(params) {
|
|
4281
|
+
return this.runHandler.runTool(params);
|
|
3464
4282
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
for (const handler of handlers) nextState = handler(nextState, action);
|
|
3471
|
-
return nextState;
|
|
3472
|
-
};
|
|
3473
|
-
}
|
|
3474
|
-
/**
|
|
3475
|
-
* Creates a selector that caches and reuses the last computed result
|
|
3476
|
-
* when all input references are unchanged.
|
|
3477
|
-
*/
|
|
3478
|
-
function createSelector(...args) {
|
|
3479
|
-
if (args.length === 1) {
|
|
3480
|
-
const projector = args[0];
|
|
3481
|
-
let hasCached = false;
|
|
3482
|
-
let lastState;
|
|
3483
|
-
let lastResult;
|
|
3484
|
-
return (state) => {
|
|
3485
|
-
if (hasCached && state === lastState) return lastResult;
|
|
3486
|
-
lastState = state;
|
|
3487
|
-
lastResult = projector(state);
|
|
3488
|
-
hasCached = true;
|
|
3489
|
-
return lastResult;
|
|
3490
|
-
};
|
|
4283
|
+
/**
|
|
4284
|
+
* State management (delegated to StateManager)
|
|
4285
|
+
*/
|
|
4286
|
+
getStateByRun(agentId, threadId, runId) {
|
|
4287
|
+
return this.stateManager.getStateByRun(agentId, threadId, runId);
|
|
3491
4288
|
}
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
*
|
|
3508
|
-
*
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
*
|
|
3515
|
-
*
|
|
3516
|
-
*
|
|
3517
|
-
*
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
};
|
|
3527
|
-
}
|
|
3528
|
-
/**
|
|
3529
|
-
* Creates an effect descriptor consumed by `createStore`.
|
|
3530
|
-
*/
|
|
3531
|
-
function createEffect(factory, options = {}) {
|
|
3532
|
-
if (options.dispatch === false) return {
|
|
3533
|
-
run: factory,
|
|
3534
|
-
dispatch: false
|
|
3535
|
-
};
|
|
3536
|
-
return {
|
|
3537
|
-
run: factory,
|
|
3538
|
-
dispatch: true
|
|
3539
|
-
};
|
|
3540
|
-
}
|
|
4289
|
+
getRunIdForMessage(agentId, threadId, messageId) {
|
|
4290
|
+
return this.stateManager.getRunIdForMessage(agentId, threadId, messageId);
|
|
4291
|
+
}
|
|
4292
|
+
getRunIdsForThread(agentId, threadId) {
|
|
4293
|
+
return this.stateManager.getRunIdsForThread(agentId, threadId);
|
|
4294
|
+
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Internal method used by RunHandler to build frontend tools
|
|
4297
|
+
*/
|
|
4298
|
+
buildFrontendTools(agentId) {
|
|
4299
|
+
return this.runHandler.buildFrontendTools(agentId);
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* Called before each follow-up agent run (after tool execution).
|
|
4303
|
+
*
|
|
4304
|
+
* When a frontend tool handler calls framework state setters (e.g. React's
|
|
4305
|
+
* setState), those updates are batched and deferred — they do not take effect
|
|
4306
|
+
* until the framework's scheduler runs (React uses MessageChannel).
|
|
4307
|
+
* useAgentContext registers context via useLayoutEffect, which runs
|
|
4308
|
+
* synchronously after React commits that deferred batch.
|
|
4309
|
+
*
|
|
4310
|
+
* Without yielding here, the follow-up runAgent reads the context store
|
|
4311
|
+
* synchronously while the deferred updates are still pending, producing stale
|
|
4312
|
+
* context for the next agent turn.
|
|
4313
|
+
*
|
|
4314
|
+
* Override in framework-specific subclasses to yield to the framework
|
|
4315
|
+
* scheduler before the follow-up run. The base implementation is a no-op
|
|
4316
|
+
* because non-React environments have no deferred state to flush.
|
|
4317
|
+
*/
|
|
4318
|
+
async waitForPendingFrameworkUpdates() {}
|
|
4319
|
+
};
|
|
4320
|
+
|
|
4321
|
+
//#endregion
|
|
4322
|
+
//#region src/types.ts
|
|
3541
4323
|
/**
|
|
3542
|
-
*
|
|
3543
|
-
*
|
|
3544
|
-
* Behavior:
|
|
3545
|
-
* - `init()` starts effects and dispatches `@@micro-redux/init`.
|
|
3546
|
-
* - `stop()` dispatches `@@micro-redux/stop` and unsubscribes all effects.
|
|
3547
|
-
* - Effect action observation is scheduled on `asapScheduler` to avoid
|
|
3548
|
-
* synchronous re-entrancy in the effect loop.
|
|
3549
|
-
* - Any effect error triggers fail-fast teardown and errors both `actions$`
|
|
3550
|
-
* and `state$`.
|
|
4324
|
+
* Status of a tool call execution
|
|
3551
4325
|
*/
|
|
3552
|
-
function
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
if (
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
dispatchInternal(effectAction);
|
|
3585
|
-
},
|
|
3586
|
-
error: (error) => {
|
|
3587
|
-
failFast(error);
|
|
3588
|
-
}
|
|
4326
|
+
let ToolCallStatus = /* @__PURE__ */ function(ToolCallStatus) {
|
|
4327
|
+
ToolCallStatus["InProgress"] = "inProgress";
|
|
4328
|
+
ToolCallStatus["Executing"] = "executing";
|
|
4329
|
+
ToolCallStatus["Complete"] = "complete";
|
|
4330
|
+
return ToolCallStatus;
|
|
4331
|
+
}({});
|
|
4332
|
+
|
|
4333
|
+
//#endregion
|
|
4334
|
+
//#region src/utils/markdown.ts
|
|
4335
|
+
function completePartialMarkdown(input) {
|
|
4336
|
+
let s = input;
|
|
4337
|
+
const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
|
|
4338
|
+
if (fenceMatches.length % 2 === 1) {
|
|
4339
|
+
const [, indent, fence] = fenceMatches[0];
|
|
4340
|
+
s += `\n${indent}${fence}`;
|
|
4341
|
+
}
|
|
4342
|
+
if (s.match(/\[([^\]]*)\]\(([^)]*)$/)) s += ")";
|
|
4343
|
+
const openElements = [];
|
|
4344
|
+
const chars = Array.from(s);
|
|
4345
|
+
const codeBlockRanges = [];
|
|
4346
|
+
const inlineCodeRanges = [];
|
|
4347
|
+
let tempCodeFenceCount = 0;
|
|
4348
|
+
let currentCodeBlockStart = -1;
|
|
4349
|
+
for (let i = 0; i < chars.length; i++) if (i === 0 || chars[i - 1] === "\n") {
|
|
4350
|
+
const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/);
|
|
4351
|
+
if (lineMatch) {
|
|
4352
|
+
tempCodeFenceCount++;
|
|
4353
|
+
if (tempCodeFenceCount % 2 === 1) currentCodeBlockStart = i;
|
|
4354
|
+
else if (currentCodeBlockStart !== -1) {
|
|
4355
|
+
codeBlockRanges.push({
|
|
4356
|
+
start: currentCodeBlockStart,
|
|
4357
|
+
end: i + lineMatch[0].length
|
|
3589
4358
|
});
|
|
3590
|
-
|
|
3591
|
-
continue;
|
|
4359
|
+
currentCodeBlockStart = -1;
|
|
3592
4360
|
}
|
|
3593
|
-
|
|
3594
|
-
failFast(error);
|
|
3595
|
-
} });
|
|
3596
|
-
effectSubscriptions.add(subscription);
|
|
4361
|
+
i += lineMatch[0].length - 1;
|
|
3597
4362
|
}
|
|
3598
|
-
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
init() {
|
|
3616
|
-
if (hasFatalError || isRunning) return;
|
|
3617
|
-
isRunning = true;
|
|
3618
|
-
startEffects();
|
|
3619
|
-
if (hasFatalError) return;
|
|
3620
|
-
dispatchInternal({ type: INTERNAL_ACTION_TYPES.init });
|
|
3621
|
-
},
|
|
3622
|
-
stop() {
|
|
3623
|
-
if (hasFatalError || !isRunning) return;
|
|
3624
|
-
dispatchInternal({ type: INTERNAL_ACTION_TYPES.stop });
|
|
3625
|
-
effectSubscriptions.unsubscribe();
|
|
3626
|
-
effectSubscriptions = new Subscription();
|
|
3627
|
-
isRunning = false;
|
|
4363
|
+
}
|
|
4364
|
+
for (let i = 0; i < chars.length; i++) if (chars[i] === "`") {
|
|
4365
|
+
let backslashCount = 0;
|
|
4366
|
+
for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) backslashCount++;
|
|
4367
|
+
if (backslashCount % 2 === 0) {
|
|
4368
|
+
for (let j = i + 1; j < chars.length; j++) if (chars[j] === "`") {
|
|
4369
|
+
let closingBackslashCount = 0;
|
|
4370
|
+
for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) closingBackslashCount++;
|
|
4371
|
+
if (closingBackslashCount % 2 === 0) {
|
|
4372
|
+
inlineCodeRanges.push({
|
|
4373
|
+
start: i,
|
|
4374
|
+
end: j + 1
|
|
4375
|
+
});
|
|
4376
|
+
i = j;
|
|
4377
|
+
break;
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
3628
4380
|
}
|
|
4381
|
+
}
|
|
4382
|
+
const isInCode = (pos) => {
|
|
4383
|
+
return codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || inlineCodeRanges.some((range) => pos >= range.start && pos < range.end);
|
|
3629
4384
|
};
|
|
4385
|
+
for (let i = 0; i < chars.length; i++) {
|
|
4386
|
+
const char = chars[i];
|
|
4387
|
+
const nextChar = chars[i + 1];
|
|
4388
|
+
const prevChar = chars[i - 1];
|
|
4389
|
+
if (isInCode(i)) continue;
|
|
4390
|
+
if (char === "[") {
|
|
4391
|
+
let isCompleteLink = false;
|
|
4392
|
+
let bracketDepth = 1;
|
|
4393
|
+
let j = i + 1;
|
|
4394
|
+
while (j < chars.length && bracketDepth > 0) {
|
|
4395
|
+
if (chars[j] === "[" && !isInCode(j)) bracketDepth++;
|
|
4396
|
+
if (chars[j] === "]" && !isInCode(j)) bracketDepth--;
|
|
4397
|
+
j++;
|
|
4398
|
+
}
|
|
4399
|
+
if (bracketDepth === 0 && chars[j] === "(") {
|
|
4400
|
+
let parenDepth = 1;
|
|
4401
|
+
j++;
|
|
4402
|
+
while (j < chars.length && parenDepth > 0) {
|
|
4403
|
+
if (chars[j] === "(" && !isInCode(j)) parenDepth++;
|
|
4404
|
+
if (chars[j] === ")" && !isInCode(j)) parenDepth--;
|
|
4405
|
+
j++;
|
|
4406
|
+
}
|
|
4407
|
+
if (parenDepth === 0) {
|
|
4408
|
+
isCompleteLink = true;
|
|
4409
|
+
i = j - 1;
|
|
4410
|
+
continue;
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
if (!isCompleteLink) {
|
|
4414
|
+
const existingIndex = openElements.findIndex((el) => el.type === "bracket");
|
|
4415
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4416
|
+
else openElements.push({
|
|
4417
|
+
type: "bracket",
|
|
4418
|
+
marker: "[",
|
|
4419
|
+
position: i
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4422
|
+
} else if (char === "*" && nextChar === "*") {
|
|
4423
|
+
const existingIndex = openElements.findIndex((el) => el.type === "bold_star");
|
|
4424
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4425
|
+
else openElements.push({
|
|
4426
|
+
type: "bold_star",
|
|
4427
|
+
marker: "**",
|
|
4428
|
+
position: i
|
|
4429
|
+
});
|
|
4430
|
+
i++;
|
|
4431
|
+
} else if (char === "_" && nextChar === "_") {
|
|
4432
|
+
const existingIndex = openElements.findIndex((el) => el.type === "bold_underscore");
|
|
4433
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4434
|
+
else openElements.push({
|
|
4435
|
+
type: "bold_underscore",
|
|
4436
|
+
marker: "__",
|
|
4437
|
+
position: i
|
|
4438
|
+
});
|
|
4439
|
+
i++;
|
|
4440
|
+
} else if (char === "~" && nextChar === "~") {
|
|
4441
|
+
const existingIndex = openElements.findIndex((el) => el.type === "strike");
|
|
4442
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4443
|
+
else openElements.push({
|
|
4444
|
+
type: "strike",
|
|
4445
|
+
marker: "~~",
|
|
4446
|
+
position: i
|
|
4447
|
+
});
|
|
4448
|
+
i++;
|
|
4449
|
+
} else if (char === "*" && prevChar !== "*" && nextChar !== "*") {
|
|
4450
|
+
const existingIndex = openElements.findIndex((el) => el.type === "italic_star");
|
|
4451
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4452
|
+
else openElements.push({
|
|
4453
|
+
type: "italic_star",
|
|
4454
|
+
marker: "*",
|
|
4455
|
+
position: i
|
|
4456
|
+
});
|
|
4457
|
+
} else if (char === "_" && prevChar !== "_" && nextChar !== "_") {
|
|
4458
|
+
const existingIndex = openElements.findIndex((el) => el.type === "italic_underscore");
|
|
4459
|
+
if (existingIndex !== -1) openElements.splice(existingIndex, 1);
|
|
4460
|
+
else openElements.push({
|
|
4461
|
+
type: "italic_underscore",
|
|
4462
|
+
marker: "_",
|
|
4463
|
+
position: i
|
|
4464
|
+
});
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
let backtickCount = 0;
|
|
4468
|
+
for (let i = 0; i < chars.length; i++) if (chars[i] === "`" && !isInCode(i)) backtickCount++;
|
|
4469
|
+
if (backtickCount % 2 === 1) s += "`";
|
|
4470
|
+
openElements.sort((a, b) => b.position - a.position);
|
|
4471
|
+
const closers = openElements.map((el) => {
|
|
4472
|
+
switch (el.type) {
|
|
4473
|
+
case "bracket": return "]";
|
|
4474
|
+
case "bold_star": return "**";
|
|
4475
|
+
case "bold_underscore": return "__";
|
|
4476
|
+
case "strike": return "~~";
|
|
4477
|
+
case "italic_star": return "*";
|
|
4478
|
+
case "italic_underscore": return "_";
|
|
4479
|
+
default: return "";
|
|
4480
|
+
}
|
|
4481
|
+
});
|
|
4482
|
+
let result = s + closers.join("");
|
|
4483
|
+
const finalFenceMatches = Array.from(result.matchAll(/^(\s*)(`{3,}|~{3,})/gm));
|
|
4484
|
+
const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1;
|
|
4485
|
+
const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1;
|
|
4486
|
+
let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence;
|
|
4487
|
+
if (shouldCloseParens) {
|
|
4488
|
+
const lastOpenParen = result.lastIndexOf("(");
|
|
4489
|
+
if (lastOpenParen !== -1) {
|
|
4490
|
+
if ((result.substring(0, lastOpenParen).match(/`/g) || []).length % 2 === 1) shouldCloseParens = false;
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
if (shouldCloseParens) {
|
|
4494
|
+
const openParens = (result.match(/\(/g) || []).length;
|
|
4495
|
+
const closeParens = (result.match(/\)/g) || []).length;
|
|
4496
|
+
if (openParens > closeParens) result += ")".repeat(openParens - closeParens);
|
|
4497
|
+
}
|
|
4498
|
+
return result;
|
|
3630
4499
|
}
|
|
3631
4500
|
|
|
3632
4501
|
//#endregion
|
|
@@ -3641,6 +4510,7 @@ const initialThreadState = {
|
|
|
3641
4510
|
isLoading: false,
|
|
3642
4511
|
isFetchingNextPage: false,
|
|
3643
4512
|
error: null,
|
|
4513
|
+
fetchMoreError: null,
|
|
3644
4514
|
context: null,
|
|
3645
4515
|
sessionId: 0,
|
|
3646
4516
|
metadataCredentialsRequested: false,
|
|
@@ -3726,6 +4596,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3726
4596
|
isLoading: Boolean(context),
|
|
3727
4597
|
isFetchingNextPage: false,
|
|
3728
4598
|
error: null,
|
|
4599
|
+
fetchMoreError: null,
|
|
3729
4600
|
metadataCredentialsRequested: false,
|
|
3730
4601
|
metadataJoinCode: null,
|
|
3731
4602
|
nextCursor: null,
|
|
@@ -3737,6 +4608,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3737
4608
|
isLoading: false,
|
|
3738
4609
|
isFetchingNextPage: false,
|
|
3739
4610
|
error: null,
|
|
4611
|
+
fetchMoreError: null,
|
|
3740
4612
|
metadataCredentialsRequested: false,
|
|
3741
4613
|
metadataJoinCode: null,
|
|
3742
4614
|
nextCursor: null,
|
|
@@ -3747,7 +4619,8 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3747
4619
|
return {
|
|
3748
4620
|
...state,
|
|
3749
4621
|
isLoading: true,
|
|
3750
|
-
error: null
|
|
4622
|
+
error: null,
|
|
4623
|
+
fetchMoreError: null
|
|
3751
4624
|
};
|
|
3752
4625
|
}), on(threadRestEvents.listSucceeded, (state, { sessionId, threads, joinCode, nextCursor }) => {
|
|
3753
4626
|
if (sessionId !== state.sessionId) return state;
|
|
@@ -3757,6 +4630,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3757
4630
|
threads: sortThreadsByRecency(threads),
|
|
3758
4631
|
isLoading: false,
|
|
3759
4632
|
error: null,
|
|
4633
|
+
fetchMoreError: null,
|
|
3760
4634
|
metadataJoinCode: joinCode,
|
|
3761
4635
|
metadataCredentialsRequested: joinCodeChanged ? false : state.metadataCredentialsRequested,
|
|
3762
4636
|
nextCursor
|
|
@@ -3776,6 +4650,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3776
4650
|
...state,
|
|
3777
4651
|
threads: merged,
|
|
3778
4652
|
isFetchingNextPage: false,
|
|
4653
|
+
fetchMoreError: null,
|
|
3779
4654
|
nextCursor
|
|
3780
4655
|
};
|
|
3781
4656
|
}), on(threadRestEvents.nextPageFailed, (state, { sessionId, error }) => {
|
|
@@ -3783,7 +4658,7 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3783
4658
|
return {
|
|
3784
4659
|
...state,
|
|
3785
4660
|
isFetchingNextPage: false,
|
|
3786
|
-
error
|
|
4661
|
+
fetchMoreError: error
|
|
3787
4662
|
};
|
|
3788
4663
|
}), on(threadRestEvents.metadataCredentialsFailed, (state, { sessionId }) => {
|
|
3789
4664
|
if (sessionId !== state.sessionId) return state;
|
|
@@ -3801,7 +4676,8 @@ const threadReducer = createReducer(initialThreadState, on(threadAdapterEvents.c
|
|
|
3801
4676
|
if (!state.nextCursor || state.isFetchingNextPage) return state;
|
|
3802
4677
|
return {
|
|
3803
4678
|
...state,
|
|
3804
|
-
isFetchingNextPage: true
|
|
4679
|
+
isFetchingNextPage: true,
|
|
4680
|
+
fetchMoreError: null
|
|
3805
4681
|
};
|
|
3806
4682
|
}), on(threadAdapterEvents.renameRequested, (state, { threadId, name }) => {
|
|
3807
4683
|
const existing = state.threads.find((thread) => thread.id === threadId);
|
|
@@ -3930,6 +4806,7 @@ function createThreadSelectors() {
|
|
|
3930
4806
|
threads: createSelector((state) => state.threads),
|
|
3931
4807
|
isLoading: createSelector((state) => state.isLoading),
|
|
3932
4808
|
error: createSelector((state) => state.error),
|
|
4809
|
+
fetchMoreError: createSelector((state) => state.fetchMoreError),
|
|
3933
4810
|
hasNextPage: createSelector((state) => state.nextCursor != null),
|
|
3934
4811
|
isFetchingNextPage: createSelector((state) => state.isFetchingNextPage),
|
|
3935
4812
|
isMutating: createSelector((state) => state.inFlightMutationCount > 0)
|
|
@@ -3939,6 +4816,7 @@ const standaloneSelectors = createThreadSelectors();
|
|
|
3939
4816
|
const selectThreads = standaloneSelectors.threads;
|
|
3940
4817
|
const selectThreadsIsLoading = standaloneSelectors.isLoading;
|
|
3941
4818
|
const selectThreadsError = standaloneSelectors.error;
|
|
4819
|
+
const selectFetchMoreError = standaloneSelectors.fetchMoreError;
|
|
3942
4820
|
const selectHasNextPage = standaloneSelectors.hasNextPage;
|
|
3943
4821
|
const selectIsFetchingNextPage = standaloneSelectors.isFetchingNextPage;
|
|
3944
4822
|
const selectIsMutating = standaloneSelectors.isMutating;
|
|
@@ -4312,6 +5190,7 @@ const ɵcreateThreadSelectors = createThreadSelectors;
|
|
|
4312
5190
|
const ɵselectThreads = selectThreads;
|
|
4313
5191
|
const ɵselectThreadsIsLoading = selectThreadsIsLoading;
|
|
4314
5192
|
const ɵselectThreadsError = selectThreadsError;
|
|
5193
|
+
const ɵselectFetchMoreError = selectFetchMoreError;
|
|
4315
5194
|
const ɵselectHasNextPage = selectHasNextPage;
|
|
4316
5195
|
const ɵselectIsFetchingNextPage = selectIsFetchingNextPage;
|
|
4317
5196
|
const ɵselectIsMutating = selectIsMutating;
|
|
@@ -4369,5 +5248,5 @@ function ɵisCopilotKitFeature(name) {
|
|
|
4369
5248
|
}
|
|
4370
5249
|
|
|
4371
5250
|
//#endregion
|
|
4372
|
-
export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, isRunCompletionAware, ofType, on, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵMAX_SOCKET_RETRIES, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, ɵjoinPhoenixChannel$, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
|
|
5251
|
+
export { AgentRegistry, AgentThreadLockedError, ContextStore, CopilotKitCore, CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, IntelligenceAgent, MEMORY_ERROR_REGISTRY, MemoryError, ProxiedCopilotRuntimeAgent, RunHandler, StateManager, SuggestionEngine, ToolCallStatus, completePartialMarkdown, createActionGroup, createEffect, createReducer, createSelector, createStore, empty, ensureObjectArgs, isAbortError, isRunCompletionAware, ofType, on, parseToolArguments, props, select, ɵCOPILOTKIT_FEATURES, ɵMAX_SOCKET_RETRIES, createMemoryStore as ɵcreateMemoryStore, ɵcreateThreadSelectors, createThreadStore as ɵcreateThreadStore, ɵisCopilotKitFeature, isRetryableStatus as ɵisRetryableMemoryStatus, ɵjoinPhoenixChannel$, ɵmapMemoryMetadataEvent, ɵmemoryAdapterEvents, ɵmemoryDomainEvents, ɵmemoryReducer, ɵmemoryRestEvents, ɵobservePhoenixEvent$, ɵobservePhoenixJoinOutcome$, ɵobservePhoenixSocketHealth$, ɵobservePhoenixSocketSignals$, ɵphoenixChannel$, ɵphoenixSocket$, ɵselectFetchMoreError, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectIsMutating, ɵselectMemories, ɵselectMemoriesAvailable, ɵselectMemoriesError, ɵselectMemoriesIsLoading, ɵselectMemoriesIsMutating, ɵselectMemoriesRealtimeStatus, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading, ɵthreadAdapterEvents };
|
|
4373
5252
|
//# sourceMappingURL=index.mjs.map
|