@witqq/agent-sdk 0.7.0 → 0.9.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/{types-CqvUAYxt.d.ts → agent-C6H2CgJA.d.cts} +139 -102
- package/dist/{types-CqvUAYxt.d.cts → agent-F7oB6eKp.d.ts} +139 -102
- package/dist/auth/index.cjs +72 -1
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +21 -154
- package/dist/auth/index.d.ts +21 -154
- package/dist/auth/index.js +72 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/backends/claude.cjs +480 -261
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +3 -1
- package/dist/backends/claude.d.ts +3 -1
- package/dist/backends/claude.js +480 -261
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +337 -112
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +12 -4
- package/dist/backends/copilot.d.ts +12 -4
- package/dist/backends/copilot.js +337 -112
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/mock-llm.cjs +719 -0
- package/dist/backends/mock-llm.cjs.map +1 -0
- package/dist/backends/mock-llm.d.cts +37 -0
- package/dist/backends/mock-llm.d.ts +37 -0
- package/dist/backends/mock-llm.js +717 -0
- package/dist/backends/mock-llm.js.map +1 -0
- package/dist/backends/vercel-ai.cjs +301 -61
- package/dist/backends/vercel-ai.cjs.map +1 -1
- package/dist/backends/vercel-ai.d.cts +3 -1
- package/dist/backends/vercel-ai.d.ts +3 -1
- package/dist/backends/vercel-ai.js +301 -61
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/backends-Cno0gZjy.d.cts +114 -0
- package/dist/backends-Cno0gZjy.d.ts +114 -0
- package/dist/chat/accumulator.cjs +1 -1
- package/dist/chat/accumulator.cjs.map +1 -1
- package/dist/chat/accumulator.d.cts +5 -2
- package/dist/chat/accumulator.d.ts +5 -2
- package/dist/chat/accumulator.js +1 -1
- package/dist/chat/accumulator.js.map +1 -1
- package/dist/chat/backends.cjs +1084 -821
- package/dist/chat/backends.cjs.map +1 -1
- package/dist/chat/backends.d.cts +10 -6
- package/dist/chat/backends.d.ts +10 -6
- package/dist/chat/backends.js +1082 -800
- package/dist/chat/backends.js.map +1 -1
- package/dist/chat/context.cjs +50 -0
- package/dist/chat/context.cjs.map +1 -1
- package/dist/chat/context.d.cts +27 -3
- package/dist/chat/context.d.ts +27 -3
- package/dist/chat/context.js +50 -0
- package/dist/chat/context.js.map +1 -1
- package/dist/chat/core.cjs +60 -27
- package/dist/chat/core.cjs.map +1 -1
- package/dist/chat/core.d.cts +41 -382
- package/dist/chat/core.d.ts +41 -382
- package/dist/chat/core.js +58 -28
- package/dist/chat/core.js.map +1 -1
- package/dist/chat/errors.cjs +48 -26
- package/dist/chat/errors.cjs.map +1 -1
- package/dist/chat/errors.d.cts +6 -31
- package/dist/chat/errors.d.ts +6 -31
- package/dist/chat/errors.js +48 -25
- package/dist/chat/errors.js.map +1 -1
- package/dist/chat/events.cjs.map +1 -1
- package/dist/chat/events.d.cts +6 -2
- package/dist/chat/events.d.ts +6 -2
- package/dist/chat/events.js.map +1 -1
- package/dist/chat/index.cjs +1612 -1125
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +35 -10
- package/dist/chat/index.d.ts +35 -10
- package/dist/chat/index.js +1600 -1097
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +2212 -1158
- package/dist/chat/react.cjs.map +1 -1
- package/dist/chat/react.d.cts +665 -122
- package/dist/chat/react.d.ts +665 -122
- package/dist/chat/react.js +2191 -1156
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/runtime.cjs +405 -186
- package/dist/chat/runtime.cjs.map +1 -1
- package/dist/chat/runtime.d.cts +92 -28
- package/dist/chat/runtime.d.ts +92 -28
- package/dist/chat/runtime.js +405 -186
- package/dist/chat/runtime.js.map +1 -1
- package/dist/chat/server.cjs +2247 -212
- package/dist/chat/server.cjs.map +1 -1
- package/dist/chat/server.d.cts +451 -90
- package/dist/chat/server.d.ts +451 -90
- package/dist/chat/server.js +2234 -213
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/sessions.cjs +64 -66
- package/dist/chat/sessions.cjs.map +1 -1
- package/dist/chat/sessions.d.cts +37 -118
- package/dist/chat/sessions.d.ts +37 -118
- package/dist/chat/sessions.js +65 -67
- package/dist/chat/sessions.js.map +1 -1
- package/dist/chat/sqlite.cjs +536 -0
- package/dist/chat/sqlite.cjs.map +1 -0
- package/dist/chat/sqlite.d.cts +164 -0
- package/dist/chat/sqlite.d.ts +164 -0
- package/dist/chat/sqlite.js +527 -0
- package/dist/chat/sqlite.js.map +1 -0
- package/dist/chat/state.cjs +14 -1
- package/dist/chat/state.cjs.map +1 -1
- package/dist/chat/state.d.cts +5 -2
- package/dist/chat/state.d.ts +5 -2
- package/dist/chat/state.js +14 -1
- package/dist/chat/state.js.map +1 -1
- package/dist/chat/storage.cjs +58 -33
- package/dist/chat/storage.cjs.map +1 -1
- package/dist/chat/storage.d.cts +18 -8
- package/dist/chat/storage.d.ts +18 -8
- package/dist/chat/storage.js +59 -34
- package/dist/chat/storage.js.map +1 -1
- package/dist/errors-C-so0M4t.d.cts +33 -0
- package/dist/errors-C-so0M4t.d.ts +33 -0
- package/dist/errors-CmVvczxZ.d.cts +28 -0
- package/dist/errors-CmVvczxZ.d.ts +28 -0
- package/dist/{in-process-transport-C2oPTYs6.d.ts → in-process-transport-7EIit9Xk.d.ts} +72 -33
- package/dist/{in-process-transport-DG-w5G6k.d.cts → in-process-transport-Ct9YcX8I.d.cts} +72 -33
- package/dist/index.cjs +354 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +294 -123
- package/dist/index.d.ts +294 -123
- package/dist/index.js +347 -60
- package/dist/index.js.map +1 -1
- package/dist/provider-types-PTSlRPNB.d.cts +39 -0
- package/dist/provider-types-PTSlRPNB.d.ts +39 -0
- package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
- package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
- package/dist/testing.cjs +1107 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +144 -0
- package/dist/testing.d.ts +144 -0
- package/dist/testing.js +1101 -0
- package/dist/testing.js.map +1 -0
- package/dist/token-store-CSUBgYwn.d.ts +48 -0
- package/dist/token-store-CuC4hB9Z.d.cts +48 -0
- package/dist/{transport-DX1Nhm4N.d.cts → transport-DLWCN18G.d.cts} +5 -4
- package/dist/{transport-D1OaUgRk.d.ts → transport-DsuS-GeM.d.ts} +5 -4
- package/dist/{types-CGF7AEX1.d.cts → types-4vbcmPTp.d.cts} +4 -2
- package/dist/{types-Bh5AhqD-.d.ts → types-BxggH0Yh.d.ts} +4 -2
- package/dist/types-DgtI1hzh.d.ts +364 -0
- package/dist/types-DkSXALKg.d.cts +364 -0
- package/package.json +41 -5
- package/LICENSE +0 -21
- package/README.md +0 -948
- package/dist/errors-BDLbNu9w.d.cts +0 -13
- package/dist/errors-BDLbNu9w.d.ts +0 -13
- package/dist/types-DLZzlJxt.d.ts +0 -39
- package/dist/types-tE0CXwBl.d.cts +0 -39
package/dist/chat/runtime.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/chat/
|
|
1
|
+
// src/chat/types.ts
|
|
2
2
|
function createChatId() {
|
|
3
3
|
return crypto.randomUUID();
|
|
4
4
|
}
|
|
@@ -9,6 +9,8 @@ function toChatId(value) {
|
|
|
9
9
|
}
|
|
10
10
|
return value;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
// src/chat/bridge.ts
|
|
12
14
|
function chatEventToAgentEvent(event) {
|
|
13
15
|
switch (event.type) {
|
|
14
16
|
case "message:delta":
|
|
@@ -34,7 +36,7 @@ function chatEventToAgentEvent(event) {
|
|
|
34
36
|
result: event.result
|
|
35
37
|
};
|
|
36
38
|
case "error":
|
|
37
|
-
return { type: "error", error: event.error, recoverable: event.recoverable };
|
|
39
|
+
return { type: "error", error: event.error, recoverable: event.recoverable, code: event.code };
|
|
38
40
|
default:
|
|
39
41
|
return null;
|
|
40
42
|
}
|
|
@@ -148,6 +150,56 @@ var ContextWindowManager = class {
|
|
|
148
150
|
});
|
|
149
151
|
return { ...result, messages: updatedMessages };
|
|
150
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Trim messages using real token usage data from the previous API call.
|
|
155
|
+
* Uses average-based algorithm: `avgTokensPerMessage = lastPromptTokens / messageCount`.
|
|
156
|
+
* Removes oldest non-system messages until freed budget brings usage under modelContextWindow.
|
|
157
|
+
*
|
|
158
|
+
* @param messages - All messages in the session
|
|
159
|
+
* @param lastPromptTokens - Real prompt tokens from the last API response
|
|
160
|
+
* @param modelContextWindow - Model's total context window size in tokens
|
|
161
|
+
* @returns Result with fitted messages and metadata
|
|
162
|
+
*/
|
|
163
|
+
fitMessagesWithUsage(messages, lastPromptTokens, modelContextWindow) {
|
|
164
|
+
if (messages.length === 0) {
|
|
165
|
+
return { messages: [], totalTokens: 0, removedCount: 0, wasTruncated: false };
|
|
166
|
+
}
|
|
167
|
+
const budget = modelContextWindow - this.config.reservedTokens;
|
|
168
|
+
if (budget <= 0 || lastPromptTokens <= budget) {
|
|
169
|
+
return {
|
|
170
|
+
messages: [...messages],
|
|
171
|
+
totalTokens: lastPromptTokens,
|
|
172
|
+
removedCount: 0,
|
|
173
|
+
wasTruncated: false
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const avgTokensPerMessage = lastPromptTokens / messages.length;
|
|
177
|
+
const tokensToFree = lastPromptTokens - budget;
|
|
178
|
+
const messagesToRemove = Math.ceil(tokensToFree / avgTokensPerMessage);
|
|
179
|
+
const nonSystemIndices = [];
|
|
180
|
+
for (let i = 0; i < messages.length; i++) {
|
|
181
|
+
if (messages[i].role === "system") ; else {
|
|
182
|
+
nonSystemIndices.push(i);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const removableCount = Math.min(messagesToRemove, nonSystemIndices.length);
|
|
186
|
+
const removedIndices = new Set(nonSystemIndices.slice(0, removableCount));
|
|
187
|
+
const result = [];
|
|
188
|
+
for (let i = 0; i < messages.length; i++) {
|
|
189
|
+
if (!removedIndices.has(i)) {
|
|
190
|
+
result.push(messages[i]);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const estimatedTokens = Math.round(
|
|
194
|
+
lastPromptTokens * (result.length / messages.length)
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
messages: result,
|
|
198
|
+
totalTokens: estimatedTokens,
|
|
199
|
+
removedCount: removableCount,
|
|
200
|
+
wasTruncated: removableCount > 0
|
|
201
|
+
};
|
|
202
|
+
}
|
|
151
203
|
/**
|
|
152
204
|
* Truncate oldest: keeps system messages, removes oldest non-system messages first.
|
|
153
205
|
* Always keeps the most recent user message.
|
|
@@ -268,9 +320,18 @@ var ContextWindowManager = class {
|
|
|
268
320
|
var AgentSDKError = class extends Error {
|
|
269
321
|
/** @internal Marker for cross-bundle identity checks */
|
|
270
322
|
_agentSDKError = true;
|
|
323
|
+
/** Machine-readable error code. Prefer values from the ErrorCode enum. */
|
|
324
|
+
code;
|
|
325
|
+
/** Whether this error is safe to retry */
|
|
326
|
+
retryable;
|
|
327
|
+
/** HTTP status code hint for error classification */
|
|
328
|
+
httpStatus;
|
|
271
329
|
constructor(message, options) {
|
|
272
330
|
super(message, options);
|
|
273
331
|
this.name = "AgentSDKError";
|
|
332
|
+
this.code = options?.code;
|
|
333
|
+
this.retryable = options?.retryable ?? false;
|
|
334
|
+
this.httpStatus = options?.httpStatus;
|
|
274
335
|
}
|
|
275
336
|
/** Check if an error is an AgentSDKError (works across bundled copies) */
|
|
276
337
|
static is(error) {
|
|
@@ -285,7 +346,11 @@ var ChatError = class extends AgentSDKError {
|
|
|
285
346
|
retryAfter;
|
|
286
347
|
timestamp;
|
|
287
348
|
constructor(message, options) {
|
|
288
|
-
super(message, {
|
|
349
|
+
super(message, {
|
|
350
|
+
cause: options.cause,
|
|
351
|
+
code: options.code,
|
|
352
|
+
retryable: options.retryable
|
|
353
|
+
});
|
|
289
354
|
this.name = "ChatError";
|
|
290
355
|
this.code = options.code;
|
|
291
356
|
this.retryable = options.retryable ?? false;
|
|
@@ -606,6 +671,35 @@ var CancellableTimeout = class {
|
|
|
606
671
|
}
|
|
607
672
|
};
|
|
608
673
|
|
|
674
|
+
// src/chat/listener-set.ts
|
|
675
|
+
var ListenerSet = class {
|
|
676
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
677
|
+
/** Add a listener. Returns an unsubscribe function. */
|
|
678
|
+
add(callback) {
|
|
679
|
+
this._listeners.add(callback);
|
|
680
|
+
return () => {
|
|
681
|
+
this._listeners.delete(callback);
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
/** Notify all listeners with the given arguments. Errors are isolated per listener. */
|
|
685
|
+
notify(...args) {
|
|
686
|
+
for (const cb of this._listeners) {
|
|
687
|
+
try {
|
|
688
|
+
cb(...args);
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/** Remove all listeners. */
|
|
694
|
+
clear() {
|
|
695
|
+
this._listeners.clear();
|
|
696
|
+
}
|
|
697
|
+
/** Current number of listeners. */
|
|
698
|
+
get size() {
|
|
699
|
+
return this._listeners.size;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
609
703
|
// src/chat/runtime.ts
|
|
610
704
|
var ChatRuntime = class {
|
|
611
705
|
_state;
|
|
@@ -613,26 +707,29 @@ var ChatRuntime = class {
|
|
|
613
707
|
_backends;
|
|
614
708
|
_sessionStore;
|
|
615
709
|
_contextConfig;
|
|
710
|
+
_ctxManager;
|
|
616
711
|
_middleware;
|
|
617
712
|
_tools = /* @__PURE__ */ new Map();
|
|
618
713
|
_retryConfig;
|
|
619
714
|
_contextStats = /* @__PURE__ */ new Map();
|
|
715
|
+
_sessionUsage = /* @__PURE__ */ new Map();
|
|
716
|
+
_modelContextWindows = /* @__PURE__ */ new Map();
|
|
620
717
|
_onContextTrimmed;
|
|
621
718
|
_streamTimeoutMs;
|
|
622
|
-
_sessionListeners =
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
_currentModel;
|
|
626
|
-
_activeSessionId = null;
|
|
719
|
+
_sessionListeners = new ListenerSet();
|
|
720
|
+
_adapterPool = /* @__PURE__ */ new Map();
|
|
721
|
+
_defaultBackend;
|
|
627
722
|
_abortController = null;
|
|
628
723
|
constructor(options) {
|
|
629
724
|
this._state = new StateMachine("idle", RUNTIME_TRANSITIONS);
|
|
630
725
|
this._guard = new ChatReentrancyGuard();
|
|
631
726
|
this._backends = options.backends;
|
|
632
|
-
this.
|
|
633
|
-
this._currentModel = options.defaultModel;
|
|
727
|
+
this._defaultBackend = options.defaultBackend;
|
|
634
728
|
this._sessionStore = options.sessionStore;
|
|
635
729
|
this._contextConfig = options.context;
|
|
730
|
+
if (this._contextConfig) {
|
|
731
|
+
this._ctxManager = new ContextWindowManager(this._contextConfig);
|
|
732
|
+
}
|
|
636
733
|
this._middleware = [...options.middleware ?? []];
|
|
637
734
|
this._retryConfig = options.retryConfig;
|
|
638
735
|
this._onContextTrimmed = options.onContextTrimmed;
|
|
@@ -643,6 +740,11 @@ var ChatRuntime = class {
|
|
|
643
740
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
644
741
|
);
|
|
645
742
|
}
|
|
743
|
+
if (options.tools) {
|
|
744
|
+
for (const tool of options.tools) {
|
|
745
|
+
this._tools.set(tool.name, tool);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
646
748
|
}
|
|
647
749
|
// ── Lifecycle ──────────────────────────────────────────────
|
|
648
750
|
get status() {
|
|
@@ -654,24 +756,23 @@ var ChatRuntime = class {
|
|
|
654
756
|
this._abortController?.dispose();
|
|
655
757
|
this._abortController = null;
|
|
656
758
|
this._state.transition("disposed");
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
759
|
+
for (const adapter of this._adapterPool.values()) {
|
|
760
|
+
try {
|
|
761
|
+
await adapter.dispose();
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
660
764
|
}
|
|
765
|
+
this._adapterPool.clear();
|
|
661
766
|
}
|
|
662
767
|
// ── Sessions ───────────────────────────────────────────────
|
|
663
|
-
get activeSessionId() {
|
|
664
|
-
return this._activeSessionId;
|
|
665
|
-
}
|
|
666
768
|
async createSession(options) {
|
|
667
769
|
this.assertNotDisposed();
|
|
668
770
|
const config = {
|
|
669
|
-
model: options.config?.model ??
|
|
670
|
-
backend: options.config?.backend ?? this.
|
|
771
|
+
model: options.config?.model ?? "",
|
|
772
|
+
backend: options.config?.backend ?? this._defaultBackend,
|
|
671
773
|
...options.config
|
|
672
774
|
};
|
|
673
775
|
const session = await this._sessionStore.createSession({ ...options, config });
|
|
674
|
-
this._activeSessionId = session.id;
|
|
675
776
|
this._notifySessionChange();
|
|
676
777
|
return session;
|
|
677
778
|
}
|
|
@@ -691,36 +792,12 @@ var ChatRuntime = class {
|
|
|
691
792
|
if (!session) return;
|
|
692
793
|
await this._sessionStore.deleteSession(cid);
|
|
693
794
|
this._contextStats.delete(cid);
|
|
694
|
-
|
|
695
|
-
this._activeSessionId = null;
|
|
696
|
-
}
|
|
795
|
+
this._sessionUsage.delete(cid);
|
|
697
796
|
this._notifySessionChange();
|
|
698
797
|
}
|
|
699
|
-
async archiveSession(id) {
|
|
700
|
-
this.assertNotDisposed();
|
|
701
|
-
const cid = toChatId(id);
|
|
702
|
-
await this._sessionStore.archiveSession(cid);
|
|
703
|
-
this._notifySessionChange();
|
|
704
|
-
}
|
|
705
|
-
async switchSession(id) {
|
|
706
|
-
this.assertNotDisposed();
|
|
707
|
-
const cid = toChatId(id);
|
|
708
|
-
const session = await this._sessionStore.getSession(cid);
|
|
709
|
-
if (!session) {
|
|
710
|
-
throw new ChatError(
|
|
711
|
-
`Session "${id}" not found`,
|
|
712
|
-
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
this._activeSessionId = session.id;
|
|
716
|
-
return session;
|
|
717
|
-
}
|
|
718
798
|
// ── Messaging ──────────────────────────────────────────────
|
|
719
799
|
async *send(sessionId, message, options) {
|
|
720
|
-
this.
|
|
721
|
-
if (!message || message.trim().length === 0) {
|
|
722
|
-
throw new ChatError("Message cannot be empty", { code: "INVALID_INPUT" /* INVALID_INPUT */ });
|
|
723
|
-
}
|
|
800
|
+
this.validateSendInput(message, options);
|
|
724
801
|
this._guard.acquire();
|
|
725
802
|
const cid = toChatId(sessionId);
|
|
726
803
|
this._abortController = new ChatAbortController(options?.signal);
|
|
@@ -729,150 +806,274 @@ var ChatRuntime = class {
|
|
|
729
806
|
this._state.transition("idle");
|
|
730
807
|
}
|
|
731
808
|
this._state.transition("streaming");
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
throw new ChatError(
|
|
735
|
-
`Session "${cid}" not found`,
|
|
736
|
-
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
const middlewareContext = {
|
|
809
|
+
await this.loadSession(cid);
|
|
810
|
+
const mwCtx = {
|
|
740
811
|
sessionId: cid,
|
|
741
812
|
signal: this._abortController.signal
|
|
742
813
|
};
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const updatedSession = await this._sessionStore.getSession(cid);
|
|
751
|
-
let messagesToSend = updatedSession.messages;
|
|
752
|
-
if (this._contextConfig) {
|
|
753
|
-
const ctxManager = new ContextWindowManager(this._contextConfig);
|
|
754
|
-
const result = await ctxManager.fitMessagesAsync(messagesToSend);
|
|
755
|
-
this._contextStats.set(cid, {
|
|
756
|
-
totalTokens: result.totalTokens,
|
|
757
|
-
removedCount: result.removedCount,
|
|
758
|
-
wasTruncated: result.wasTruncated,
|
|
759
|
-
availableBudget: ctxManager.availableBudget
|
|
760
|
-
});
|
|
761
|
-
if (result.wasTruncated && this._onContextTrimmed) {
|
|
762
|
-
const keptIds = new Set(result.messages.map((m) => m.id));
|
|
763
|
-
const removed = messagesToSend.filter((m) => !keptIds.has(m.id));
|
|
764
|
-
if (removed.length > 0) {
|
|
765
|
-
try {
|
|
766
|
-
this._onContextTrimmed(cid, removed);
|
|
767
|
-
} catch {
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
messagesToSend = result.messages;
|
|
814
|
+
const userMessage = await this.applyBeforeSendMiddleware(
|
|
815
|
+
this.createUserMessage(message),
|
|
816
|
+
mwCtx
|
|
817
|
+
);
|
|
818
|
+
if (userMessage === null) {
|
|
819
|
+
this._state.transition("idle");
|
|
820
|
+
return;
|
|
772
821
|
}
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
822
|
+
const updatedSession = await this.persistAndReload(cid, userMessage);
|
|
823
|
+
const sessionForAdapter = await this.trimSessionContext(cid, updatedSession, options.model);
|
|
824
|
+
const stream = await this.prepareEventStream(
|
|
825
|
+
cid,
|
|
826
|
+
sessionForAdapter,
|
|
827
|
+
updatedSession,
|
|
828
|
+
message,
|
|
829
|
+
options
|
|
830
|
+
);
|
|
778
831
|
const accumulator = new MessageAccumulator();
|
|
779
|
-
const runtimeTools = this._tools.size > 0 ? this.injectToolContext([...this._tools.values()], {
|
|
780
|
-
sessionId: cid,
|
|
781
|
-
custom: updatedSession.metadata?.custom
|
|
782
|
-
}) : void 0;
|
|
783
|
-
const streamOptions = {
|
|
784
|
-
...options,
|
|
785
|
-
signal: this._abortController.signal,
|
|
786
|
-
model: options?.model ?? this._currentModel,
|
|
787
|
-
tools: runtimeTools
|
|
788
|
-
};
|
|
789
|
-
const stream = await this.createStreamWithRetry(adapter, sessionForAdapter, message, streamOptions);
|
|
790
832
|
const eventSource = this._streamTimeoutMs ? withStreamWatchdog(stream, { timeoutMs: this._streamTimeoutMs, signal: this._abortController.signal }) : stream;
|
|
791
833
|
for await (const event of eventSource) {
|
|
792
834
|
if (this._abortController.isAborted) break;
|
|
793
835
|
this.feedAccumulator(accumulator, event);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (processedEvent) {
|
|
801
|
-
yield processedEvent;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
if (this._state.current === "disposed") {
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
let assistantMessage = accumulator.finalize();
|
|
808
|
-
for (const mw of this._middleware) {
|
|
809
|
-
if (mw.onAfterReceive) {
|
|
810
|
-
assistantMessage = await mw.onAfterReceive(assistantMessage, middlewareContext);
|
|
836
|
+
if (event.type === "usage") {
|
|
837
|
+
this._sessionUsage.set(cid, {
|
|
838
|
+
promptTokens: event.promptTokens,
|
|
839
|
+
completionTokens: event.completionTokens
|
|
840
|
+
});
|
|
841
|
+
this.updateContextStatsWithUsage(cid, event.promptTokens, event.completionTokens, options);
|
|
811
842
|
}
|
|
843
|
+
const processed = await this.applyOnEventMiddleware(event, mwCtx);
|
|
844
|
+
if (processed) yield processed;
|
|
812
845
|
}
|
|
813
|
-
|
|
814
|
-
this.
|
|
846
|
+
if (this._state.current === "disposed") return;
|
|
847
|
+
await this.finalizeAssistantMessage(cid, accumulator, mwCtx);
|
|
815
848
|
this._state.transition("idle");
|
|
816
849
|
} catch (error) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
sessionId: cid,
|
|
820
|
-
signal: this._abortController?.signal ?? new AbortController().signal
|
|
821
|
-
};
|
|
822
|
-
for (const mw of this._middleware) {
|
|
823
|
-
if (mw.onError) {
|
|
824
|
-
const result = await mw.onError(processedError, middlewareContext);
|
|
825
|
-
if (result === null) {
|
|
826
|
-
if (this._state.canTransition("idle")) {
|
|
827
|
-
this._state.transition("idle");
|
|
828
|
-
}
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
processedError = result;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (this._state.canTransition("error")) {
|
|
835
|
-
this._state.transition("error");
|
|
836
|
-
}
|
|
837
|
-
throw processedError;
|
|
850
|
+
const result = await this.handleSendError(error, cid);
|
|
851
|
+
if (result !== null) throw result;
|
|
838
852
|
} finally {
|
|
839
853
|
this._guard.release();
|
|
840
854
|
this._abortController?.dispose();
|
|
841
855
|
this._abortController = null;
|
|
842
856
|
}
|
|
843
857
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
// ── Backend / Model ────────────────────────────────────────
|
|
848
|
-
get currentBackend() {
|
|
849
|
-
return this._currentBackend;
|
|
850
|
-
}
|
|
851
|
-
get currentModel() {
|
|
852
|
-
return this._currentModel;
|
|
853
|
-
}
|
|
854
|
-
async switchBackend(name) {
|
|
858
|
+
// ── Send Pipeline Stages ──────────────────────────────────────
|
|
859
|
+
/** Stage 1: Validate send inputs (message content + required fields). */
|
|
860
|
+
validateSendInput(message, options) {
|
|
855
861
|
this.assertNotDisposed();
|
|
856
|
-
if (!
|
|
862
|
+
if (!message || message.trim().length === 0) {
|
|
863
|
+
throw new ChatError("Message cannot be empty", { code: "INVALID_INPUT" /* INVALID_INPUT */ });
|
|
864
|
+
}
|
|
865
|
+
if (!options.model) {
|
|
857
866
|
throw new ChatError(
|
|
858
|
-
|
|
867
|
+
"options.model is required \u2014 caller must specify which model to use",
|
|
859
868
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
860
869
|
);
|
|
861
870
|
}
|
|
862
|
-
if (
|
|
863
|
-
|
|
864
|
-
|
|
871
|
+
if (!options.backend) {
|
|
872
|
+
throw new ChatError(
|
|
873
|
+
"options.backend is required \u2014 caller must specify which backend to use",
|
|
874
|
+
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
if (!options.credentials) {
|
|
878
|
+
throw new ChatError(
|
|
879
|
+
"options.credentials is required \u2014 caller must provide authentication credentials",
|
|
880
|
+
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
881
|
+
);
|
|
865
882
|
}
|
|
866
|
-
this._currentBackend = name;
|
|
867
883
|
}
|
|
868
|
-
|
|
884
|
+
/** Stage 2: Load session from store. */
|
|
885
|
+
async loadSession(cid) {
|
|
886
|
+
const session = await this._sessionStore.getSession(cid);
|
|
887
|
+
if (!session) {
|
|
888
|
+
throw new ChatError(
|
|
889
|
+
`Session "${cid}" not found`,
|
|
890
|
+
{ code: "SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */ }
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
return session;
|
|
894
|
+
}
|
|
895
|
+
/** Stage 3: Apply onBeforeSend middleware pipeline. Returns null if middleware rejected the send. */
|
|
896
|
+
async applyBeforeSendMiddleware(userMessage, ctx) {
|
|
897
|
+
let msg = userMessage;
|
|
898
|
+
for (const mw of this._middleware) {
|
|
899
|
+
if (mw.onBeforeSend && msg) {
|
|
900
|
+
msg = await mw.onBeforeSend(msg, ctx);
|
|
901
|
+
if (msg === null) return null;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
return msg;
|
|
905
|
+
}
|
|
906
|
+
/** Stage 4: Persist user message and reload session with full history. */
|
|
907
|
+
async persistAndReload(cid, userMessage) {
|
|
908
|
+
await this._sessionStore.appendMessage(cid, userMessage);
|
|
909
|
+
return await this._sessionStore.getSession(cid);
|
|
910
|
+
}
|
|
911
|
+
/** Stage 5: Auto-trim context window if configured. Returns session snapshot for adapter. */
|
|
912
|
+
async trimSessionContext(cid, session, model) {
|
|
913
|
+
if (!this._ctxManager) return session;
|
|
914
|
+
const ctxManager = this._ctxManager;
|
|
915
|
+
const lastUsage = this._sessionUsage.get(cid);
|
|
916
|
+
const modelContextWindow = model ? this._modelContextWindows.get(model) : void 0;
|
|
917
|
+
if (lastUsage && modelContextWindow) {
|
|
918
|
+
const result2 = ctxManager.fitMessagesWithUsage(
|
|
919
|
+
session.messages,
|
|
920
|
+
lastUsage.promptTokens,
|
|
921
|
+
modelContextWindow
|
|
922
|
+
);
|
|
923
|
+
this._contextStats.set(cid, {
|
|
924
|
+
totalTokens: result2.totalTokens,
|
|
925
|
+
removedCount: result2.removedCount,
|
|
926
|
+
wasTruncated: result2.wasTruncated,
|
|
927
|
+
availableBudget: Math.max(0, modelContextWindow - result2.totalTokens),
|
|
928
|
+
realPromptTokens: lastUsage.promptTokens,
|
|
929
|
+
realCompletionTokens: lastUsage.completionTokens,
|
|
930
|
+
modelContextWindow
|
|
931
|
+
});
|
|
932
|
+
if (result2.wasTruncated && this._onContextTrimmed) {
|
|
933
|
+
const keptIds = new Set(result2.messages.map((m) => m.id));
|
|
934
|
+
const removed = session.messages.filter((m) => !keptIds.has(m.id));
|
|
935
|
+
if (removed.length > 0) {
|
|
936
|
+
try {
|
|
937
|
+
this._onContextTrimmed(cid, removed);
|
|
938
|
+
} catch {
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return { ...session, messages: result2.messages };
|
|
943
|
+
}
|
|
944
|
+
const result = await ctxManager.fitMessagesAsync(session.messages);
|
|
945
|
+
this._contextStats.set(cid, {
|
|
946
|
+
totalTokens: result.totalTokens,
|
|
947
|
+
removedCount: result.removedCount,
|
|
948
|
+
wasTruncated: result.wasTruncated,
|
|
949
|
+
availableBudget: ctxManager.availableBudget,
|
|
950
|
+
modelContextWindow
|
|
951
|
+
});
|
|
952
|
+
if (result.wasTruncated && this._onContextTrimmed) {
|
|
953
|
+
const keptIds = new Set(result.messages.map((m) => m.id));
|
|
954
|
+
const removed = session.messages.filter((m) => !keptIds.has(m.id));
|
|
955
|
+
if (removed.length > 0) {
|
|
956
|
+
try {
|
|
957
|
+
this._onContextTrimmed(cid, removed);
|
|
958
|
+
} catch {
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return { ...session, messages: result.messages };
|
|
963
|
+
}
|
|
964
|
+
/** Update context stats with real usage data from a usage event. */
|
|
965
|
+
updateContextStatsWithUsage(cid, promptTokens, completionTokens, options) {
|
|
966
|
+
const modelContextWindow = options.model ? this._modelContextWindows.get(options.model) : void 0;
|
|
967
|
+
const existing = this._contextStats.get(cid);
|
|
968
|
+
this._contextStats.set(cid, {
|
|
969
|
+
totalTokens: promptTokens,
|
|
970
|
+
removedCount: existing?.removedCount ?? 0,
|
|
971
|
+
wasTruncated: existing?.wasTruncated ?? false,
|
|
972
|
+
availableBudget: modelContextWindow ? Math.max(0, modelContextWindow - promptTokens) : existing?.availableBudget ?? 0,
|
|
973
|
+
realPromptTokens: promptTokens,
|
|
974
|
+
realCompletionTokens: completionTokens,
|
|
975
|
+
modelContextWindow
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
/** Stage 6: Prepare event stream — adapter with retry, tool injection. */
|
|
979
|
+
async prepareEventStream(cid, sessionForAdapter, fullSession, message, options) {
|
|
980
|
+
const adapter = await this.getOrCreateAdapterWithRetry(options.backend, options.credentials);
|
|
981
|
+
const runtimeTools = this._tools.size > 0 ? this.injectToolContext([...this._tools.values()], {
|
|
982
|
+
sessionId: cid,
|
|
983
|
+
custom: fullSession.metadata?.custom
|
|
984
|
+
}) : void 0;
|
|
985
|
+
const streamOptions = {
|
|
986
|
+
signal: this._abortController.signal,
|
|
987
|
+
model: options.model,
|
|
988
|
+
systemPrompt: options.systemPrompt,
|
|
989
|
+
tools: runtimeTools
|
|
990
|
+
};
|
|
991
|
+
return this.createStreamWithRetry(
|
|
992
|
+
adapter,
|
|
993
|
+
sessionForAdapter,
|
|
994
|
+
message,
|
|
995
|
+
streamOptions,
|
|
996
|
+
options.backend,
|
|
997
|
+
options.credentials
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
/** Stage 7: Apply onEvent middleware pipeline (sequential transform/suppress). */
|
|
1001
|
+
async applyOnEventMiddleware(event, ctx) {
|
|
1002
|
+
let processed = event;
|
|
1003
|
+
for (const mw of this._middleware) {
|
|
1004
|
+
if (mw.onEvent && processed) {
|
|
1005
|
+
processed = await mw.onEvent(processed, ctx);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return processed;
|
|
1009
|
+
}
|
|
1010
|
+
/** Stage 8: Finalize accumulator, apply afterReceive middleware, persist assistant message. */
|
|
1011
|
+
async finalizeAssistantMessage(cid, accumulator, ctx) {
|
|
1012
|
+
let assistantMessage = accumulator.finalize();
|
|
1013
|
+
for (const mw of this._middleware) {
|
|
1014
|
+
if (mw.onAfterReceive) {
|
|
1015
|
+
assistantMessage = await mw.onAfterReceive(assistantMessage, ctx);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
await this._sessionStore.appendMessage(cid, assistantMessage);
|
|
1019
|
+
this._notifySessionChange();
|
|
1020
|
+
}
|
|
1021
|
+
/** Stage 9: Error handling — apply onError middleware, transition state. Returns null if suppressed. */
|
|
1022
|
+
async handleSendError(error, cid) {
|
|
1023
|
+
let processedError = error instanceof Error ? error : new Error(String(error));
|
|
1024
|
+
const ctx = {
|
|
1025
|
+
sessionId: cid,
|
|
1026
|
+
signal: this._abortController?.signal ?? new AbortController().signal
|
|
1027
|
+
};
|
|
1028
|
+
for (const mw of this._middleware) {
|
|
1029
|
+
if (mw.onError) {
|
|
1030
|
+
const result = await mw.onError(processedError, ctx);
|
|
1031
|
+
if (result === null) {
|
|
1032
|
+
if (this._state.canTransition("idle")) {
|
|
1033
|
+
this._state.transition("idle");
|
|
1034
|
+
}
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
processedError = result;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (this._state.canTransition("error")) {
|
|
1041
|
+
this._state.transition("error");
|
|
1042
|
+
}
|
|
1043
|
+
return processedError;
|
|
1044
|
+
}
|
|
1045
|
+
abort() {
|
|
1046
|
+
this._abortController?.abort("User abort");
|
|
1047
|
+
}
|
|
1048
|
+
// ── Backend / Model ────────────────────────────────────────
|
|
1049
|
+
async listModels(options) {
|
|
869
1050
|
this.assertNotDisposed();
|
|
870
|
-
|
|
1051
|
+
let models = [];
|
|
1052
|
+
const firstAdapter = [...this._adapterPool.values()][0];
|
|
1053
|
+
if (firstAdapter) {
|
|
1054
|
+
try {
|
|
1055
|
+
models = await firstAdapter.listModels();
|
|
1056
|
+
} catch {
|
|
1057
|
+
return [];
|
|
1058
|
+
}
|
|
1059
|
+
} else if (options?.backend && options?.credentials) {
|
|
1060
|
+
try {
|
|
1061
|
+
const adapter = await this.getOrCreateAdapter(options.backend, options.credentials);
|
|
1062
|
+
models = await adapter.listModels();
|
|
1063
|
+
} catch {
|
|
1064
|
+
return [];
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
for (const model of models) {
|
|
1068
|
+
if (model.contextWindow != null) {
|
|
1069
|
+
this._modelContextWindows.set(model.id, model.contextWindow);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return models;
|
|
871
1073
|
}
|
|
872
|
-
async
|
|
1074
|
+
async listBackends() {
|
|
873
1075
|
this.assertNotDisposed();
|
|
874
|
-
|
|
875
|
-
return adapter.listModels();
|
|
1076
|
+
return Object.keys(this._backends).map((name) => ({ name }));
|
|
876
1077
|
}
|
|
877
1078
|
// ── Tools ──────────────────────────────────────────────────
|
|
878
1079
|
get registeredTools() {
|
|
@@ -897,37 +1098,46 @@ var ChatRuntime = class {
|
|
|
897
1098
|
if (idx >= 0) this._middleware.splice(idx, 1);
|
|
898
1099
|
}
|
|
899
1100
|
// ── Context Stats ─────────────────────────────────────────
|
|
900
|
-
getContextStats(sessionId) {
|
|
1101
|
+
async getContextStats(sessionId) {
|
|
901
1102
|
const cid = toChatId(sessionId);
|
|
902
1103
|
return this._contextStats.get(cid) ?? null;
|
|
903
1104
|
}
|
|
904
1105
|
// ── Session Subscription ──────────────────────────────────
|
|
905
1106
|
onSessionChange(callback) {
|
|
906
|
-
this._sessionListeners.add(callback);
|
|
907
|
-
return () => {
|
|
908
|
-
this._sessionListeners.delete(callback);
|
|
909
|
-
};
|
|
1107
|
+
return this._sessionListeners.add(callback);
|
|
910
1108
|
}
|
|
911
1109
|
_notifySessionChange() {
|
|
912
|
-
|
|
913
|
-
try {
|
|
914
|
-
cb();
|
|
915
|
-
} catch {
|
|
916
|
-
}
|
|
917
|
-
}
|
|
1110
|
+
this._sessionListeners.notify();
|
|
918
1111
|
}
|
|
919
1112
|
// ── Private Helpers ────────────────────────────────────────
|
|
920
|
-
async getOrCreateAdapter() {
|
|
921
|
-
|
|
922
|
-
const
|
|
1113
|
+
async getOrCreateAdapter(backend, credentials) {
|
|
1114
|
+
const key = this.getPoolKey(backend, credentials);
|
|
1115
|
+
const existing = this._adapterPool.get(key);
|
|
1116
|
+
if (existing) return existing;
|
|
1117
|
+
for (const [oldKey, oldAdapter] of this._adapterPool) {
|
|
1118
|
+
if (oldKey.startsWith(backend + ":")) {
|
|
1119
|
+
try {
|
|
1120
|
+
await oldAdapter.dispose();
|
|
1121
|
+
} catch {
|
|
1122
|
+
}
|
|
1123
|
+
this._adapterPool.delete(oldKey);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const factory = this._backends[backend];
|
|
923
1127
|
if (!factory) {
|
|
924
1128
|
throw new ChatError(
|
|
925
|
-
`Backend "${
|
|
1129
|
+
`Backend "${backend}" not found`,
|
|
926
1130
|
{ code: "INVALID_INPUT" /* INVALID_INPUT */ }
|
|
927
1131
|
);
|
|
928
1132
|
}
|
|
929
|
-
|
|
930
|
-
|
|
1133
|
+
const adapter = await factory(credentials);
|
|
1134
|
+
this._adapterPool.set(key, adapter);
|
|
1135
|
+
return adapter;
|
|
1136
|
+
}
|
|
1137
|
+
getPoolKey(backend, credentials) {
|
|
1138
|
+
const token = credentials.accessToken;
|
|
1139
|
+
const hash = token.length > 16 ? token.slice(0, 8) + token.slice(-8) : token;
|
|
1140
|
+
return `${backend}:${hash}`;
|
|
931
1141
|
}
|
|
932
1142
|
/** Wrap each tool's execute to inject ToolContext as 2nd argument */
|
|
933
1143
|
injectToolContext(tools, context) {
|
|
@@ -959,17 +1169,25 @@ var ChatRuntime = class {
|
|
|
959
1169
|
}
|
|
960
1170
|
}
|
|
961
1171
|
/** Get or create adapter with retry on connection errors */
|
|
962
|
-
async getOrCreateAdapterWithRetry() {
|
|
1172
|
+
async getOrCreateAdapterWithRetry(backend, credentials) {
|
|
963
1173
|
const maxAttempts = this._retryConfig?.maxAttempts ?? 1;
|
|
964
1174
|
const delayMs = this._retryConfig?.delayMs ?? 0;
|
|
965
1175
|
let lastError;
|
|
966
1176
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
967
1177
|
try {
|
|
968
|
-
return await this.getOrCreateAdapter();
|
|
1178
|
+
return await this.getOrCreateAdapter(backend, credentials);
|
|
969
1179
|
} catch (err) {
|
|
970
1180
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
971
1181
|
if (attempt < maxAttempts) {
|
|
972
|
-
this.
|
|
1182
|
+
const key = this.getPoolKey(backend, credentials);
|
|
1183
|
+
const old = this._adapterPool.get(key);
|
|
1184
|
+
if (old) {
|
|
1185
|
+
try {
|
|
1186
|
+
await old.dispose();
|
|
1187
|
+
} catch {
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
this._adapterPool.delete(key);
|
|
973
1191
|
await delay(delayMs);
|
|
974
1192
|
}
|
|
975
1193
|
}
|
|
@@ -982,7 +1200,7 @@ var ChatRuntime = class {
|
|
|
982
1200
|
* retries with a fresh adapter. Once first event is received,
|
|
983
1201
|
* the stream is committed (no more retries).
|
|
984
1202
|
*/
|
|
985
|
-
async createStreamWithRetry(adapter, session, message, options) {
|
|
1203
|
+
async createStreamWithRetry(adapter, session, message, options, backend, credentials) {
|
|
986
1204
|
const maxAttempts = this._retryConfig?.maxAttempts ?? 1;
|
|
987
1205
|
const delayMs = this._retryConfig?.delayMs ?? 0;
|
|
988
1206
|
let lastError;
|
|
@@ -1003,13 +1221,14 @@ var ChatRuntime = class {
|
|
|
1003
1221
|
} catch (err) {
|
|
1004
1222
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
1005
1223
|
if (attempt < maxAttempts) {
|
|
1006
|
-
|
|
1007
|
-
await
|
|
1008
|
-
|
|
1224
|
+
try {
|
|
1225
|
+
await currentAdapter.dispose();
|
|
1226
|
+
} catch {
|
|
1009
1227
|
}
|
|
1010
|
-
this.
|
|
1228
|
+
const key = this.getPoolKey(backend, credentials);
|
|
1229
|
+
this._adapterPool.delete(key);
|
|
1011
1230
|
await delay(delayMs);
|
|
1012
|
-
currentAdapter = await this.getOrCreateAdapter();
|
|
1231
|
+
currentAdapter = await this.getOrCreateAdapter(backend, credentials);
|
|
1013
1232
|
}
|
|
1014
1233
|
}
|
|
1015
1234
|
}
|