@cadraos/sdk 0.3.12 → 0.3.14-p102.1
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/board-host/index.cjs +217 -129
- package/dist/board-host/index.cjs.map +1 -1
- package/dist/board-host/index.d.cts +268 -3
- package/dist/board-host/index.d.ts +268 -3
- package/dist/board-host/index.js +218 -131
- package/dist/board-host/index.js.map +1 -1
- package/dist/board-host/server/index.d.cts +218 -2
- package/dist/board-host/server/index.d.ts +218 -2
- package/dist/chat/index.cjs +528 -47
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +29 -0
- package/dist/chat/index.d.ts +29 -0
- package/dist/chat/index.js +528 -47
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +453 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +453 -38
- package/dist/index.js.map +1 -1
- package/dist/kanban/engine/index.cjs +1535 -434
- package/dist/kanban/engine/index.cjs.map +1 -1
- package/dist/kanban/engine/index.d.cts +628 -6
- package/dist/kanban/engine/index.d.ts +628 -6
- package/dist/kanban/engine/index.js +1520 -435
- package/dist/kanban/engine/index.js.map +1 -1
- package/dist/kanban/index.cjs +751 -60
- package/dist/kanban/index.cjs.map +1 -1
- package/dist/kanban/index.d.cts +555 -9
- package/dist/kanban/index.d.ts +555 -9
- package/dist/kanban/index.js +747 -61
- package/dist/kanban/index.js.map +1 -1
- package/dist/teams/index.cjs +453 -38
- package/dist/teams/index.cjs.map +1 -1
- package/dist/teams/index.d.cts +27 -0
- package/dist/teams/index.d.ts +27 -0
- package/dist/teams/index.js +453 -38
- package/dist/teams/index.js.map +1 -1
- package/package.json +1 -1
package/dist/chat/index.js
CHANGED
|
@@ -440,7 +440,8 @@ var SSEClient = class {
|
|
|
440
440
|
"Authorization": this.config.authHeader,
|
|
441
441
|
"X-Org-Id": String(this.config.orgId),
|
|
442
442
|
"Accept": "text/event-stream",
|
|
443
|
-
"Cache-Control": "no-cache"
|
|
443
|
+
"Cache-Control": "no-cache",
|
|
444
|
+
...this.config.lastEventId ? { "Last-Event-ID": this.config.lastEventId } : {}
|
|
444
445
|
},
|
|
445
446
|
signal: this.abortController.signal
|
|
446
447
|
});
|
|
@@ -666,13 +667,167 @@ function normalizeUrl(url) {
|
|
|
666
667
|
return url;
|
|
667
668
|
}
|
|
668
669
|
|
|
670
|
+
// src/streaming/poll-policy.ts
|
|
671
|
+
var UNNAMED_RATE_LIMIT_WAIT_MS = 6e4;
|
|
672
|
+
function asDict(value) {
|
|
673
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
674
|
+
}
|
|
675
|
+
var RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
676
|
+
"rate_limit_error",
|
|
677
|
+
"rate_limited",
|
|
678
|
+
"rate_limit_exceeded",
|
|
679
|
+
"too_many_requests"
|
|
680
|
+
]);
|
|
681
|
+
function isRateLimited(error) {
|
|
682
|
+
if (typeof error === "string") return /\b429\b/.test(error);
|
|
683
|
+
const err = asDict(error);
|
|
684
|
+
if (!err) return false;
|
|
685
|
+
for (const key of ["status", "statusCode", "httpStatus", "code"]) {
|
|
686
|
+
if (err[key] === 429 || err[key] === "429") return true;
|
|
687
|
+
}
|
|
688
|
+
const response = asDict(err.response);
|
|
689
|
+
if (response && (response.status === 429 || response.statusCode === 429)) return true;
|
|
690
|
+
if (typeof err.code === "string" && RATE_LIMIT_CODES.has(err.code.toLowerCase())) return true;
|
|
691
|
+
if (err.name === "RateLimitError") return true;
|
|
692
|
+
const message = typeof err.message === "string" ? err.message : "";
|
|
693
|
+
return /\b429\b/.test(message) || /rate[\s_-]?limit/i.test(message) || /too many requests/i.test(message);
|
|
694
|
+
}
|
|
695
|
+
function readHeader(bag, name) {
|
|
696
|
+
if (bag === null || bag === void 0) return null;
|
|
697
|
+
const getter = bag.get;
|
|
698
|
+
if (typeof getter === "function") {
|
|
699
|
+
try {
|
|
700
|
+
const value = getter.call(bag, name);
|
|
701
|
+
if (value !== null && value !== void 0) return String(value);
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const dict = asDict(bag);
|
|
706
|
+
if (!dict) return null;
|
|
707
|
+
const wanted = name.toLowerCase();
|
|
708
|
+
for (const [key, value] of Object.entries(dict)) {
|
|
709
|
+
if (key.toLowerCase() === wanted && value !== null && value !== void 0) {
|
|
710
|
+
return String(value);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
function parseRetryAfter(raw, now) {
|
|
716
|
+
if (raw === null || raw === void 0) return null;
|
|
717
|
+
if (typeof raw === "number") {
|
|
718
|
+
return Number.isFinite(raw) && raw >= 0 ? raw * 1e3 : null;
|
|
719
|
+
}
|
|
720
|
+
if (typeof raw !== "string") return null;
|
|
721
|
+
const trimmed = raw.trim();
|
|
722
|
+
if (trimmed === "") return null;
|
|
723
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed) * 1e3;
|
|
724
|
+
const at = Date.parse(trimmed);
|
|
725
|
+
return Number.isNaN(at) ? null : Math.max(0, at - now);
|
|
726
|
+
}
|
|
727
|
+
function parseResetAt(raw, now) {
|
|
728
|
+
if (raw === null || raw === void 0) return null;
|
|
729
|
+
const numeric = typeof raw === "number" ? raw : typeof raw === "string" && /^\d+(\.\d+)?$/.test(raw.trim()) ? Number(raw.trim()) : null;
|
|
730
|
+
if (numeric !== null) {
|
|
731
|
+
if (!Number.isFinite(numeric) || numeric < 0) return null;
|
|
732
|
+
if (numeric > 1e11) return Math.max(0, numeric - now);
|
|
733
|
+
if (numeric > 1e9) return Math.max(0, numeric * 1e3 - now);
|
|
734
|
+
return numeric * 1e3;
|
|
735
|
+
}
|
|
736
|
+
if (typeof raw !== "string") return null;
|
|
737
|
+
const at = Date.parse(raw.trim());
|
|
738
|
+
return Number.isNaN(at) ? null : Math.max(0, at - now);
|
|
739
|
+
}
|
|
740
|
+
function getRetryAfterMs(error, now = Date.now()) {
|
|
741
|
+
const err = asDict(error);
|
|
742
|
+
if (!err) return null;
|
|
743
|
+
const details = asDict(err.details);
|
|
744
|
+
const response = asDict(err.response);
|
|
745
|
+
const body = asDict(err.body) ?? asDict(response?.data);
|
|
746
|
+
const bodyError = asDict(body?.error);
|
|
747
|
+
const bodyDetails = asDict(bodyError?.details) ?? asDict(body?.details);
|
|
748
|
+
for (const source of [err, details, bodyDetails]) {
|
|
749
|
+
const ms = source?.retryAfterMs;
|
|
750
|
+
if (typeof ms === "number" && Number.isFinite(ms) && ms >= 0) return ms;
|
|
751
|
+
}
|
|
752
|
+
for (const source of [err, details, bodyDetails, bodyError]) {
|
|
753
|
+
const parsed = parseRetryAfter(source?.retryAfter, now);
|
|
754
|
+
if (parsed !== null) return parsed;
|
|
755
|
+
}
|
|
756
|
+
const headerBags = [err.headers, response?.headers, details?.headers, body?.headers];
|
|
757
|
+
for (const bag of headerBags) {
|
|
758
|
+
const parsed = parseRetryAfter(readHeader(bag, "retry-after"), now);
|
|
759
|
+
if (parsed !== null) return parsed;
|
|
760
|
+
}
|
|
761
|
+
for (const source of [details, bodyDetails, err]) {
|
|
762
|
+
for (const key of ["resetAt", "reset", "rateLimitReset"]) {
|
|
763
|
+
const parsed = parseResetAt(source?.[key], now);
|
|
764
|
+
if (parsed !== null) return parsed;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
for (const bag of headerBags) {
|
|
768
|
+
for (const header of ["x-ratelimit-reset", "ratelimit-reset"]) {
|
|
769
|
+
const parsed = parseResetAt(readHeader(bag, header), now);
|
|
770
|
+
if (parsed !== null) return parsed;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
function getHttpStatus(error) {
|
|
776
|
+
const err = asDict(error);
|
|
777
|
+
if (!err) return null;
|
|
778
|
+
for (const key of ["status", "statusCode", "httpStatus"]) {
|
|
779
|
+
const value = err[key];
|
|
780
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
781
|
+
if (typeof value === "string" && /^\d{3}$/.test(value)) return Number(value);
|
|
782
|
+
}
|
|
783
|
+
const response = asDict(err.response);
|
|
784
|
+
for (const key of ["status", "statusCode"]) {
|
|
785
|
+
const value = response?.[key];
|
|
786
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
787
|
+
}
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
function isTerminalClientError(error) {
|
|
791
|
+
const status = getHttpStatus(error);
|
|
792
|
+
if (status === null) return false;
|
|
793
|
+
return status >= 400 && status < 500 && status !== 429 && status !== 408;
|
|
794
|
+
}
|
|
795
|
+
var FAILURE_GROWTH_FACTOR = 2;
|
|
796
|
+
var IDLE_GROWTH_FACTOR = 1.5;
|
|
797
|
+
function nextFailureDelayMs(currentMs, maxMs) {
|
|
798
|
+
return Math.min(Math.max(currentMs, 1) * FAILURE_GROWTH_FACTOR, maxMs);
|
|
799
|
+
}
|
|
800
|
+
function nextIdleDelayMs(currentMs, maxMs) {
|
|
801
|
+
return Math.min(Math.ceil(Math.max(currentMs, 1) * IDLE_GROWTH_FACTOR), maxMs);
|
|
802
|
+
}
|
|
803
|
+
function applyJitter(delayMs, ratio, random = Math.random) {
|
|
804
|
+
if (ratio <= 0) return delayMs;
|
|
805
|
+
return Math.round(delayMs * (1 + random() * ratio));
|
|
806
|
+
}
|
|
807
|
+
|
|
669
808
|
// src/streaming/stream-controller.ts
|
|
670
809
|
var DEFAULT_STREAMING_CONFIG = {
|
|
671
810
|
maxReconnectAttempts: 5,
|
|
672
811
|
reconnectBaseDelayMs: 1e3,
|
|
673
812
|
reconnectMaxDelayMs: 1e4,
|
|
813
|
+
// NOTE (unchanged, deliberately): this watchdog force-disconnects a HEALTHY
|
|
814
|
+
// SSE connection after 30s of silence and routes it through the same
|
|
815
|
+
// handleConnectionError path as a real transport failure. Ordinary LLM
|
|
816
|
+
// latency exceeds 30s routinely, so it fires against working streams — that
|
|
817
|
+
// is what drives sessions into the polling fallback in the first place. The
|
|
818
|
+
// fix is a server-side SSE heartbeat, not a bigger number here; raising this
|
|
819
|
+
// is a guess against a moving target. Everything below only bounds what the
|
|
820
|
+
// fallback costs once it has been entered.
|
|
674
821
|
activityTimeoutMs: 3e4,
|
|
675
|
-
pollingIntervalMs:
|
|
822
|
+
pollingIntervalMs: 5e3,
|
|
823
|
+
pollingMaxIntervalMs: 3e4,
|
|
824
|
+
pollingMaxConsecutiveFailures: 10,
|
|
825
|
+
pollingMaxConsecutiveRateLimits: 3,
|
|
826
|
+
pollingMaxConsecutiveClientErrors: 3,
|
|
827
|
+
pollingMaxDurationMs: 18e5,
|
|
828
|
+
pollingMaxRetryAfterMs: 3e5,
|
|
829
|
+
pollingJitterRatio: 0.1,
|
|
830
|
+
sseRetryIntervalMs: 6e4
|
|
676
831
|
};
|
|
677
832
|
var DEFAULT_POLLING_ENABLED = true;
|
|
678
833
|
var ExecutionStreamControllerImpl = class {
|
|
@@ -685,9 +840,24 @@ var ExecutionStreamControllerImpl = class {
|
|
|
685
840
|
seenEventIds = /* @__PURE__ */ new Set();
|
|
686
841
|
_lastEventId = null;
|
|
687
842
|
activityTimeoutId = null;
|
|
688
|
-
pollingIntervalId = null;
|
|
689
843
|
finalExecution = null;
|
|
690
844
|
finalError = null;
|
|
845
|
+
// --- Fallback polling state ---
|
|
846
|
+
// A self-rescheduling timeout, not setInterval: the delay has to change
|
|
847
|
+
// between ticks (that is what backoff IS), and a timeout chain also cannot
|
|
848
|
+
// stack overlapping requests the way a fixed interval does when the API is
|
|
849
|
+
// slow to answer.
|
|
850
|
+
pollTimeoutId = null;
|
|
851
|
+
pollDelayMs = 0;
|
|
852
|
+
pollConsecutiveFailures = 0;
|
|
853
|
+
pollConsecutiveRateLimits = 0;
|
|
854
|
+
pollConsecutiveClientErrors = 0;
|
|
855
|
+
/** When the loop last observed anything new — the clock the give-up bound runs on. */
|
|
856
|
+
pollLastProgressAt = 0;
|
|
857
|
+
pollInFlight = false;
|
|
858
|
+
lastPolledStatus = null;
|
|
859
|
+
sseRetryTimeoutId = null;
|
|
860
|
+
sseRetryInFlight = false;
|
|
691
861
|
// Event callbacks
|
|
692
862
|
listeners = {
|
|
693
863
|
log: [],
|
|
@@ -716,6 +886,39 @@ var ExecutionStreamControllerImpl = class {
|
|
|
716
886
|
this.config = { ...DEFAULT_STREAMING_CONFIG, ...options.config };
|
|
717
887
|
this.pollingEnabled = options.pollingEnabled ?? DEFAULT_POLLING_ENABLED;
|
|
718
888
|
}
|
|
889
|
+
// Resolved fallback-poll settings. Read through `??` rather than off the
|
|
890
|
+
// spread config so an explicit `undefined` in a caller's partial config still
|
|
891
|
+
// lands on the default instead of NaN-ing the arithmetic.
|
|
892
|
+
get pollBaseIntervalMs() {
|
|
893
|
+
return this.config.pollingIntervalMs ?? DEFAULT_STREAMING_CONFIG.pollingIntervalMs;
|
|
894
|
+
}
|
|
895
|
+
get pollMaxIntervalMs() {
|
|
896
|
+
return Math.max(
|
|
897
|
+
this.config.pollingMaxIntervalMs ?? DEFAULT_STREAMING_CONFIG.pollingMaxIntervalMs,
|
|
898
|
+
this.pollBaseIntervalMs
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
get pollMaxConsecutiveFailures() {
|
|
902
|
+
return this.config.pollingMaxConsecutiveFailures ?? DEFAULT_STREAMING_CONFIG.pollingMaxConsecutiveFailures;
|
|
903
|
+
}
|
|
904
|
+
get pollMaxConsecutiveRateLimits() {
|
|
905
|
+
return this.config.pollingMaxConsecutiveRateLimits ?? DEFAULT_STREAMING_CONFIG.pollingMaxConsecutiveRateLimits;
|
|
906
|
+
}
|
|
907
|
+
get pollMaxConsecutiveClientErrors() {
|
|
908
|
+
return this.config.pollingMaxConsecutiveClientErrors ?? DEFAULT_STREAMING_CONFIG.pollingMaxConsecutiveClientErrors;
|
|
909
|
+
}
|
|
910
|
+
get pollMaxDurationMs() {
|
|
911
|
+
return this.config.pollingMaxDurationMs ?? DEFAULT_STREAMING_CONFIG.pollingMaxDurationMs;
|
|
912
|
+
}
|
|
913
|
+
get pollMaxRetryAfterMs() {
|
|
914
|
+
return this.config.pollingMaxRetryAfterMs ?? DEFAULT_STREAMING_CONFIG.pollingMaxRetryAfterMs;
|
|
915
|
+
}
|
|
916
|
+
get pollJitterRatio() {
|
|
917
|
+
return this.config.pollingJitterRatio ?? DEFAULT_STREAMING_CONFIG.pollingJitterRatio;
|
|
918
|
+
}
|
|
919
|
+
get sseRetryIntervalMs() {
|
|
920
|
+
return this.config.sseRetryIntervalMs ?? DEFAULT_STREAMING_CONFIG.sseRetryIntervalMs;
|
|
921
|
+
}
|
|
719
922
|
/**
|
|
720
923
|
* Get the last event ID (for resume support)
|
|
721
924
|
*/
|
|
@@ -806,28 +1009,45 @@ var ExecutionStreamControllerImpl = class {
|
|
|
806
1009
|
console.log("[SDK:StreamController] executionId:", this.options.executionId);
|
|
807
1010
|
console.log("[SDK:StreamController] orgId:", this.options.orgId);
|
|
808
1011
|
console.log("[SDK:StreamController] apiKey:", authToken ? `${authToken.substring(0, 20)}...` : "MISSING");
|
|
809
|
-
this.sseClient
|
|
1012
|
+
if (this.sseClient) {
|
|
1013
|
+
this.sseClient.disconnect();
|
|
1014
|
+
this.sseClient = null;
|
|
1015
|
+
}
|
|
1016
|
+
const client = new SSEClient({
|
|
810
1017
|
url,
|
|
811
1018
|
authHeader: `Bearer ${authToken}`,
|
|
812
1019
|
orgId: this.options.orgId,
|
|
813
1020
|
fetch: this.options.fetch,
|
|
814
|
-
|
|
1021
|
+
// Resume from where this session left off. On a first connect this is
|
|
1022
|
+
// null and the header is omitted.
|
|
1023
|
+
lastEventId: this._lastEventId,
|
|
1024
|
+
onEvent: (event) => {
|
|
1025
|
+
if (this.sseClient !== client) return;
|
|
1026
|
+
this.handleEvent(event);
|
|
1027
|
+
},
|
|
815
1028
|
onError: (error) => {
|
|
1029
|
+
if (this.sseClient !== client) return;
|
|
816
1030
|
console.error("[SDK:StreamController] SSE error:", error);
|
|
817
1031
|
this.handleConnectionError(error);
|
|
818
1032
|
},
|
|
819
1033
|
onConnectionChange: (connected) => {
|
|
1034
|
+
if (this.sseClient !== client) return;
|
|
820
1035
|
console.log("[SDK:StreamController] SSE connection change:", connected);
|
|
821
1036
|
if (connected) {
|
|
822
1037
|
this.status = "connected";
|
|
823
1038
|
this.reconnectAttempts = 0;
|
|
1039
|
+
this.stopPolling();
|
|
824
1040
|
this.resetActivityTimeout();
|
|
825
1041
|
}
|
|
826
|
-
this.emit("connectionChange", {
|
|
1042
|
+
this.emit("connectionChange", {
|
|
1043
|
+
connected,
|
|
1044
|
+
usingFallback: this.status === "fallback_polling"
|
|
1045
|
+
});
|
|
827
1046
|
}
|
|
828
1047
|
});
|
|
1048
|
+
this.sseClient = client;
|
|
829
1049
|
try {
|
|
830
|
-
await
|
|
1050
|
+
await client.connect();
|
|
831
1051
|
console.log("[SDK:StreamController] SSE connect() completed");
|
|
832
1052
|
} catch (error) {
|
|
833
1053
|
console.error("[SDK:StreamController] SSE connect() threw:", error);
|
|
@@ -1066,6 +1286,7 @@ var ExecutionStreamControllerImpl = class {
|
|
|
1066
1286
|
handleComplete(data) {
|
|
1067
1287
|
this.status = "closed";
|
|
1068
1288
|
this.stopActivityTimeout();
|
|
1289
|
+
this.stopPolling();
|
|
1069
1290
|
const sessionId = data.sessionId ?? this.options.sessionId;
|
|
1070
1291
|
const execution = {
|
|
1071
1292
|
uuid: this.options.executionId,
|
|
@@ -1092,6 +1313,7 @@ var ExecutionStreamControllerImpl = class {
|
|
|
1092
1313
|
handleError(error) {
|
|
1093
1314
|
this.status = "closed";
|
|
1094
1315
|
this.stopActivityTimeout();
|
|
1316
|
+
this.stopPolling();
|
|
1095
1317
|
this.finalError = error;
|
|
1096
1318
|
this.emit("error", error);
|
|
1097
1319
|
if (this.waitReject) {
|
|
@@ -1118,6 +1340,10 @@ var ExecutionStreamControllerImpl = class {
|
|
|
1118
1340
|
this.handleError({ error: msg, errorCode: "stream_unrecoverable" });
|
|
1119
1341
|
return;
|
|
1120
1342
|
}
|
|
1343
|
+
if (this.status === "fallback_polling") {
|
|
1344
|
+
this.startPolling();
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1121
1347
|
if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
1122
1348
|
this.status = "reconnecting";
|
|
1123
1349
|
this.reconnectAttempts++;
|
|
@@ -1187,41 +1413,215 @@ var ExecutionStreamControllerImpl = class {
|
|
|
1187
1413
|
}
|
|
1188
1414
|
}
|
|
1189
1415
|
/**
|
|
1190
|
-
* Start polling fallback
|
|
1416
|
+
* Start polling fallback.
|
|
1417
|
+
*
|
|
1418
|
+
* Idempotent — calling it while a loop is already running is a no-op, so no
|
|
1419
|
+
* path can end up with two loops racing the same execution.
|
|
1191
1420
|
*/
|
|
1192
1421
|
startPolling() {
|
|
1193
|
-
this.
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1422
|
+
if (this.pollTimeoutId !== null || this.pollInFlight) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
this.pollLastProgressAt = Date.now();
|
|
1426
|
+
this.pollDelayMs = this.pollBaseIntervalMs;
|
|
1427
|
+
this.pollConsecutiveFailures = 0;
|
|
1428
|
+
this.pollConsecutiveRateLimits = 0;
|
|
1429
|
+
this.pollConsecutiveClientErrors = 0;
|
|
1430
|
+
this.lastPolledStatus = null;
|
|
1431
|
+
this.stopActivityTimeout();
|
|
1432
|
+
this.scheduleSSERetry();
|
|
1433
|
+
this.schedulePoll(this.pollDelayMs);
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Queue the next poll tick.
|
|
1437
|
+
*/
|
|
1438
|
+
schedulePoll(delayMs) {
|
|
1439
|
+
if (this.status !== "fallback_polling") {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
this.pollTimeoutId = setTimeout(() => {
|
|
1443
|
+
this.pollTimeoutId = null;
|
|
1444
|
+
void this.runPoll();
|
|
1445
|
+
}, Math.max(0, Math.round(delayMs)));
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* One poll tick: fetch state, emit what is new, then decide how long to wait
|
|
1449
|
+
* before the next one — or whether there should be a next one at all.
|
|
1450
|
+
*/
|
|
1451
|
+
async runPoll() {
|
|
1452
|
+
if (this.status !== "fallback_polling") {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
if (Date.now() - this.pollLastProgressAt >= this.pollMaxDurationMs) {
|
|
1456
|
+
this.failPolling(
|
|
1457
|
+
`Streaming fallback gave up: no execution progress for ${Math.round(this.pollMaxDurationMs / 1e3)}s`,
|
|
1458
|
+
"polling_timeout"
|
|
1459
|
+
);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
this.pollInFlight = true;
|
|
1463
|
+
try {
|
|
1464
|
+
const execution = await this.options.getExecution(this.options.executionId);
|
|
1465
|
+
this.pollConsecutiveFailures = 0;
|
|
1466
|
+
this.pollConsecutiveRateLimits = 0;
|
|
1467
|
+
this.pollConsecutiveClientErrors = 0;
|
|
1468
|
+
let sawNewInfo = false;
|
|
1469
|
+
for (const log of execution.logs ?? []) {
|
|
1470
|
+
if (!this.seenEventIds.has(log.uuid)) {
|
|
1471
|
+
this.seenEventIds.add(log.uuid);
|
|
1472
|
+
this.emit("log", log);
|
|
1473
|
+
sawNewInfo = true;
|
|
1211
1474
|
}
|
|
1212
|
-
} catch (error) {
|
|
1213
|
-
console.error("Polling error:", error);
|
|
1214
1475
|
}
|
|
1215
|
-
|
|
1476
|
+
if (execution.status !== this.lastPolledStatus) {
|
|
1477
|
+
this.lastPolledStatus = execution.status;
|
|
1478
|
+
sawNewInfo = true;
|
|
1479
|
+
}
|
|
1480
|
+
if (sawNewInfo) {
|
|
1481
|
+
this.pollLastProgressAt = Date.now();
|
|
1482
|
+
}
|
|
1483
|
+
if (execution.status === "completed") {
|
|
1484
|
+
this.stopPolling();
|
|
1485
|
+
this.handleComplete(execution);
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
if (execution.status === "failed" || execution.status === "cancelled") {
|
|
1489
|
+
this.stopPolling();
|
|
1490
|
+
this.handleError({
|
|
1491
|
+
error: execution.error ?? `Execution ${execution.status}`,
|
|
1492
|
+
errorCode: execution.status
|
|
1493
|
+
});
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
this.pollDelayMs = sawNewInfo ? this.pollBaseIntervalMs : nextIdleDelayMs(this.pollDelayMs, this.pollMaxIntervalMs);
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
if (!this.handlePollError(error)) {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
} finally {
|
|
1502
|
+
this.pollInFlight = false;
|
|
1503
|
+
}
|
|
1504
|
+
this.schedulePoll(applyJitter(this.pollDelayMs, this.pollJitterRatio));
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Apply backpressure for a failed poll.
|
|
1508
|
+
*
|
|
1509
|
+
* @returns `true` to keep polling (with `pollDelayMs` updated), `false` when
|
|
1510
|
+
* the loop has been terminated.
|
|
1511
|
+
*/
|
|
1512
|
+
handlePollError(error) {
|
|
1513
|
+
const retryAfterMs = getRetryAfterMs(error);
|
|
1514
|
+
const rateLimited = isRateLimited(error) || retryAfterMs !== null;
|
|
1515
|
+
if (rateLimited) {
|
|
1516
|
+
this.pollConsecutiveRateLimits++;
|
|
1517
|
+
console.warn(
|
|
1518
|
+
"[SDK:StreamController] Poll rate-limited",
|
|
1519
|
+
retryAfterMs !== null ? `(server asked for ${Math.round(retryAfterMs / 1e3)}s)` : "(no reset named)"
|
|
1520
|
+
);
|
|
1521
|
+
if (retryAfterMs !== null && retryAfterMs > this.pollMaxRetryAfterMs) {
|
|
1522
|
+
this.failPolling(
|
|
1523
|
+
`Rate limited: the API asked for ${Math.round(retryAfterMs / 1e3)}s, longer than this stream will wait`,
|
|
1524
|
+
"rate_limited"
|
|
1525
|
+
);
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
if (this.pollConsecutiveRateLimits >= this.pollMaxConsecutiveRateLimits) {
|
|
1529
|
+
this.failPolling(
|
|
1530
|
+
"Rate limited by the API; stopped the streaming fallback rather than keep spending the key quota",
|
|
1531
|
+
"rate_limited"
|
|
1532
|
+
);
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1535
|
+
this.pollDelayMs = Math.max(retryAfterMs ?? UNNAMED_RATE_LIMIT_WAIT_MS, this.pollDelayMs);
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
if (isTerminalClientError(error)) {
|
|
1539
|
+
this.pollConsecutiveClientErrors++;
|
|
1540
|
+
const status = getHttpStatus(error);
|
|
1541
|
+
console.warn(
|
|
1542
|
+
`[SDK:StreamController] Poll got HTTP ${status} (${this.pollConsecutiveClientErrors}/${this.pollMaxConsecutiveClientErrors})`
|
|
1543
|
+
);
|
|
1544
|
+
if (this.pollConsecutiveClientErrors >= this.pollMaxConsecutiveClientErrors) {
|
|
1545
|
+
this.failPolling(
|
|
1546
|
+
`Execution status endpoint returned HTTP ${status} ${this.pollConsecutiveClientErrors} times \u2014 cannot track this run`,
|
|
1547
|
+
"polling_unrecoverable"
|
|
1548
|
+
);
|
|
1549
|
+
return false;
|
|
1550
|
+
}
|
|
1551
|
+
this.pollDelayMs = nextFailureDelayMs(this.pollDelayMs, this.pollMaxIntervalMs);
|
|
1552
|
+
return true;
|
|
1553
|
+
}
|
|
1554
|
+
this.pollConsecutiveFailures++;
|
|
1555
|
+
console.warn(
|
|
1556
|
+
`[SDK:StreamController] Poll failed (${this.pollConsecutiveFailures}/${this.pollMaxConsecutiveFailures}):`,
|
|
1557
|
+
error
|
|
1558
|
+
);
|
|
1559
|
+
if (this.pollConsecutiveFailures >= this.pollMaxConsecutiveFailures) {
|
|
1560
|
+
this.failPolling(
|
|
1561
|
+
`Streaming fallback failed ${this.pollConsecutiveFailures} times in a row`,
|
|
1562
|
+
"polling_failed"
|
|
1563
|
+
);
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
this.pollDelayMs = nextFailureDelayMs(this.pollDelayMs, this.pollMaxIntervalMs);
|
|
1567
|
+
return true;
|
|
1216
1568
|
}
|
|
1217
1569
|
/**
|
|
1218
|
-
*
|
|
1570
|
+
* Terminate the fallback loop and surface the reason.
|
|
1571
|
+
*/
|
|
1572
|
+
failPolling(error, errorCode) {
|
|
1573
|
+
this.stopPolling();
|
|
1574
|
+
this.handleError({ error, errorCode });
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Stop polling, including the periodic SSE re-attempt that belongs to it.
|
|
1219
1578
|
*/
|
|
1220
1579
|
stopPolling() {
|
|
1221
|
-
if (this.
|
|
1222
|
-
|
|
1223
|
-
this.
|
|
1580
|
+
if (this.pollTimeoutId !== null) {
|
|
1581
|
+
clearTimeout(this.pollTimeoutId);
|
|
1582
|
+
this.pollTimeoutId = null;
|
|
1583
|
+
}
|
|
1584
|
+
if (this.sseRetryTimeoutId !== null) {
|
|
1585
|
+
clearTimeout(this.sseRetryTimeoutId);
|
|
1586
|
+
this.sseRetryTimeoutId = null;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Queue the next attempt to get the real stream back.
|
|
1591
|
+
*/
|
|
1592
|
+
scheduleSSERetry() {
|
|
1593
|
+
if (this.sseRetryTimeoutId !== null || this.status !== "fallback_polling") {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
this.sseRetryTimeoutId = setTimeout(() => {
|
|
1597
|
+
this.sseRetryTimeoutId = null;
|
|
1598
|
+
this.attemptSSERecovery();
|
|
1599
|
+
}, this.sseRetryIntervalMs);
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Re-attempt SSE from inside the fallback, so a transient outage costs the
|
|
1603
|
+
* session a few polls rather than its stream for the lifetime of the tab.
|
|
1604
|
+
*
|
|
1605
|
+
* Success is NOT observed here: `SSEClient.connect()` resolves when the
|
|
1606
|
+
* stream ENDS, not when it opens. The open is observed by
|
|
1607
|
+
* `onConnectionChange(true)`, which flips status and stops this loop. The
|
|
1608
|
+
* promise below only tells us the attempt is over so the next one can be
|
|
1609
|
+
* queued.
|
|
1610
|
+
*/
|
|
1611
|
+
attemptSSERecovery() {
|
|
1612
|
+
if (this.status !== "fallback_polling") {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (this.sseRetryInFlight) {
|
|
1616
|
+
this.scheduleSSERetry();
|
|
1617
|
+
return;
|
|
1224
1618
|
}
|
|
1619
|
+
this.sseRetryInFlight = true;
|
|
1620
|
+
void this.connectSSE().catch(() => {
|
|
1621
|
+
}).finally(() => {
|
|
1622
|
+
this.sseRetryInFlight = false;
|
|
1623
|
+
this.scheduleSSERetry();
|
|
1624
|
+
});
|
|
1225
1625
|
}
|
|
1226
1626
|
/**
|
|
1227
1627
|
* Reset activity timeout
|
|
@@ -1353,12 +1753,23 @@ async function collectImagesFromStream(stream, options = {}) {
|
|
|
1353
1753
|
});
|
|
1354
1754
|
}
|
|
1355
1755
|
|
|
1756
|
+
// src/utils/api-paths.ts
|
|
1757
|
+
var API_V1 = "/api/v1";
|
|
1758
|
+
var EXECUTION_PATH = `${API_V1}/executions`;
|
|
1759
|
+
function executionPath(executionId) {
|
|
1760
|
+
return `${EXECUTION_PATH}/${executionId}`;
|
|
1761
|
+
}
|
|
1762
|
+
function executionCancelPath(executionId) {
|
|
1763
|
+
return `${EXECUTION_PATH}/${executionId}/cancel`;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1356
1766
|
// src/resources/agents.ts
|
|
1357
1767
|
var AgentsAPI = class extends BaseResourceAPI {
|
|
1358
1768
|
constructor(http, getStreamConfig) {
|
|
1359
1769
|
super(http, "/api/v1/agents");
|
|
1360
1770
|
this.getStreamConfig = getStreamConfig;
|
|
1361
1771
|
}
|
|
1772
|
+
getStreamConfig;
|
|
1362
1773
|
// ===== LIST Operations =====
|
|
1363
1774
|
/**
|
|
1364
1775
|
* List all agents in the organization
|
|
@@ -1615,7 +2026,7 @@ var AgentsAPI = class extends BaseResourceAPI {
|
|
|
1615
2026
|
* ```
|
|
1616
2027
|
*/
|
|
1617
2028
|
async getExecution(executionId) {
|
|
1618
|
-
return this.http.get(
|
|
2029
|
+
return this.http.get(executionPath(executionId));
|
|
1619
2030
|
}
|
|
1620
2031
|
// ===== EDIT Operations =====
|
|
1621
2032
|
/**
|
|
@@ -1670,7 +2081,7 @@ var AgentsAPI = class extends BaseResourceAPI {
|
|
|
1670
2081
|
*/
|
|
1671
2082
|
async cancelExecution(executionId) {
|
|
1672
2083
|
return this.http.post(
|
|
1673
|
-
|
|
2084
|
+
executionCancelPath(executionId),
|
|
1674
2085
|
void 0,
|
|
1675
2086
|
{ skipRetry: true }
|
|
1676
2087
|
);
|
|
@@ -2171,6 +2582,7 @@ var ArtifactsAPI = class extends BaseResourceAPI {
|
|
|
2171
2582
|
}
|
|
2172
2583
|
return this.http.request({
|
|
2173
2584
|
method: "POST",
|
|
2585
|
+
// KNOWN BROKEN — same missing `/api/v1` prefix as listArtifacts above.
|
|
2174
2586
|
path: `/executions/${executionId}/artifacts`,
|
|
2175
2587
|
body: formData,
|
|
2176
2588
|
headers: {
|
|
@@ -2264,6 +2676,7 @@ var SessionsAPI = class extends BaseResourceAPI {
|
|
|
2264
2676
|
super(http, "/api/v1/sessions");
|
|
2265
2677
|
this.getStreamConfig = getStreamConfig;
|
|
2266
2678
|
}
|
|
2679
|
+
getStreamConfig;
|
|
2267
2680
|
// ===== LIST Operations =====
|
|
2268
2681
|
/**
|
|
2269
2682
|
* List all sessions in the organization
|
|
@@ -2443,7 +2856,7 @@ var SessionsAPI = class extends BaseResourceAPI {
|
|
|
2443
2856
|
* Get execution result
|
|
2444
2857
|
*/
|
|
2445
2858
|
async getExecution(executionId) {
|
|
2446
|
-
return this.http.get(
|
|
2859
|
+
return this.http.get(executionPath(executionId));
|
|
2447
2860
|
}
|
|
2448
2861
|
// ===== EDIT Operations =====
|
|
2449
2862
|
/**
|
|
@@ -2583,6 +2996,7 @@ var KanbanAPI = class extends BaseResourceAPI {
|
|
|
2583
2996
|
super(http, "/api/v1/kanban");
|
|
2584
2997
|
this.getStreamConfig = getStreamConfig;
|
|
2585
2998
|
}
|
|
2999
|
+
getStreamConfig;
|
|
2586
3000
|
// ===== BOARD =====
|
|
2587
3001
|
/**
|
|
2588
3002
|
* Get the current org's board (lanes in declared order). Auto-provisions
|
|
@@ -2717,7 +3131,7 @@ var KanbanAPI = class extends BaseResourceAPI {
|
|
|
2717
3131
|
* @returns Execution details.
|
|
2718
3132
|
*/
|
|
2719
3133
|
async getCardRun(executionId) {
|
|
2720
|
-
return this.http.get(
|
|
3134
|
+
return this.http.get(executionPath(executionId));
|
|
2721
3135
|
}
|
|
2722
3136
|
/**
|
|
2723
3137
|
* Create a stream controller for a card's run, keyed on the card's
|
|
@@ -2784,6 +3198,7 @@ var RemoteAgentsAPI = class extends BaseResourceAPI {
|
|
|
2784
3198
|
super(http, "/api/v1/remote-agents");
|
|
2785
3199
|
this.getStreamConfig = getStreamConfig;
|
|
2786
3200
|
}
|
|
3201
|
+
getStreamConfig;
|
|
2787
3202
|
/**
|
|
2788
3203
|
* List all remote agents in the organization
|
|
2789
3204
|
*
|
|
@@ -2917,7 +3332,7 @@ var RemoteAgentsAPI = class extends BaseResourceAPI {
|
|
|
2917
3332
|
apiKey: config.apiKey,
|
|
2918
3333
|
tokenProvider: config.tokenProvider,
|
|
2919
3334
|
orgId: config.orgId,
|
|
2920
|
-
getExecution: (id) => this.http.get(
|
|
3335
|
+
getExecution: (id) => this.http.get(executionPath(id))
|
|
2921
3336
|
});
|
|
2922
3337
|
return controller;
|
|
2923
3338
|
}
|
|
@@ -3254,6 +3669,7 @@ function createDefaultAdapter(config) {
|
|
|
3254
3669
|
filename: a.filename,
|
|
3255
3670
|
mimeType: a.mimeType,
|
|
3256
3671
|
url: a.url,
|
|
3672
|
+
...a.thumbnailUrl ? { thumbnailUrl: a.thumbnailUrl } : {},
|
|
3257
3673
|
createdAt: a.createdAt
|
|
3258
3674
|
}));
|
|
3259
3675
|
},
|
|
@@ -3998,6 +4414,13 @@ function initialState() {
|
|
|
3998
4414
|
|
|
3999
4415
|
// src/chat/hooks/useAgentChat.ts
|
|
4000
4416
|
var __DEV__ = typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
4417
|
+
var LEGACY_POLL_BASE_INTERVAL_MS = 5e3;
|
|
4418
|
+
var LEGACY_POLL_MAX_INTERVAL_MS = 3e4;
|
|
4419
|
+
var LEGACY_POLL_MAX_FAILURES = 10;
|
|
4420
|
+
var LEGACY_POLL_MAX_RATE_LIMITS = 3;
|
|
4421
|
+
var LEGACY_POLL_MAX_IDLE_MS = 18e5;
|
|
4422
|
+
var LEGACY_POLL_MAX_RETRY_AFTER_MS = 3e5;
|
|
4423
|
+
var LEGACY_POLL_JITTER_RATIO = 0.1;
|
|
4001
4424
|
var SILENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4002
4425
|
"check_delegations"
|
|
4003
4426
|
]);
|
|
@@ -4585,12 +5008,17 @@ function useAgentChat(options) {
|
|
|
4585
5008
|
}
|
|
4586
5009
|
}
|
|
4587
5010
|
});
|
|
4588
|
-
const
|
|
5011
|
+
const pollingTimeoutRef = useRef(null);
|
|
4589
5012
|
const pollingExecutionIdRef = useRef(null);
|
|
5013
|
+
const pollDelayRef = useRef(0);
|
|
5014
|
+
const pollFailuresRef = useRef(0);
|
|
5015
|
+
const pollRateLimitsRef = useRef(0);
|
|
5016
|
+
const pollLastProgressRef = useRef(0);
|
|
5017
|
+
const pollLastStatusRef = useRef(null);
|
|
4590
5018
|
const stopPolling = useCallback(() => {
|
|
4591
|
-
if (
|
|
4592
|
-
|
|
4593
|
-
|
|
5019
|
+
if (pollingTimeoutRef.current) {
|
|
5020
|
+
clearTimeout(pollingTimeoutRef.current);
|
|
5021
|
+
pollingTimeoutRef.current = null;
|
|
4594
5022
|
}
|
|
4595
5023
|
pollingExecutionIdRef.current = null;
|
|
4596
5024
|
}, []);
|
|
@@ -4598,26 +5026,77 @@ function useAgentChat(options) {
|
|
|
4598
5026
|
if (pollingExecutionIdRef.current === executionId) return;
|
|
4599
5027
|
stopPolling();
|
|
4600
5028
|
pollingExecutionIdRef.current = executionId;
|
|
5029
|
+
const baseDelayMs = pollingInterval ?? LEGACY_POLL_BASE_INTERVAL_MS;
|
|
5030
|
+
pollDelayRef.current = baseDelayMs;
|
|
5031
|
+
pollFailuresRef.current = 0;
|
|
5032
|
+
pollRateLimitsRef.current = 0;
|
|
5033
|
+
pollLastProgressRef.current = Date.now();
|
|
5034
|
+
pollLastStatusRef.current = null;
|
|
4601
5035
|
if (__DEV__) console.log("[useAgentChat] Starting polling fallback for:", executionId);
|
|
5036
|
+
const schedule = (delayMs) => {
|
|
5037
|
+
if (pollingExecutionIdRef.current !== executionId) return;
|
|
5038
|
+
pollingTimeoutRef.current = setTimeout(() => {
|
|
5039
|
+
pollingTimeoutRef.current = null;
|
|
5040
|
+
void poll();
|
|
5041
|
+
}, Math.max(0, Math.round(delayMs)));
|
|
5042
|
+
};
|
|
5043
|
+
const giveUp = (message) => {
|
|
5044
|
+
if (__DEV__) console.warn("[useAgentChat] Polling gave up:", message);
|
|
5045
|
+
stopPolling();
|
|
5046
|
+
handleError(message);
|
|
5047
|
+
};
|
|
4602
5048
|
const poll = async () => {
|
|
4603
5049
|
if (pollingExecutionIdRef.current !== executionId) return;
|
|
5050
|
+
if (Date.now() - pollLastProgressRef.current >= LEGACY_POLL_MAX_IDLE_MS) {
|
|
5051
|
+
giveUp("Lost the live connection and stopped receiving updates for this run");
|
|
5052
|
+
return;
|
|
5053
|
+
}
|
|
4604
5054
|
try {
|
|
4605
5055
|
const execution = await adapter.getExecution(executionId);
|
|
5056
|
+
pollFailuresRef.current = 0;
|
|
5057
|
+
pollRateLimitsRef.current = 0;
|
|
5058
|
+
if (execution.status !== pollLastStatusRef.current) {
|
|
5059
|
+
pollLastStatusRef.current = execution.status;
|
|
5060
|
+
pollLastProgressRef.current = Date.now();
|
|
5061
|
+
pollDelayRef.current = baseDelayMs;
|
|
5062
|
+
} else {
|
|
5063
|
+
pollDelayRef.current = nextIdleDelayMs(pollDelayRef.current, LEGACY_POLL_MAX_INTERVAL_MS);
|
|
5064
|
+
}
|
|
4606
5065
|
if (execution.status === "completed") {
|
|
4607
5066
|
if (__DEV__) console.log("[useAgentChat] Polling: execution completed");
|
|
4608
5067
|
stopPolling();
|
|
4609
5068
|
handleComplete(execution.result || void 0);
|
|
4610
|
-
|
|
5069
|
+
return;
|
|
5070
|
+
}
|
|
5071
|
+
if (execution.status === "failed") {
|
|
4611
5072
|
if (__DEV__) console.log("[useAgentChat] Polling: execution failed");
|
|
4612
5073
|
stopPolling();
|
|
4613
5074
|
handleError(execution.error || "Execution failed");
|
|
5075
|
+
return;
|
|
4614
5076
|
}
|
|
4615
5077
|
} catch (err) {
|
|
4616
|
-
|
|
5078
|
+
const retryAfterMs = getRetryAfterMs(err);
|
|
5079
|
+
if (isRateLimited(err) || retryAfterMs !== null) {
|
|
5080
|
+
pollRateLimitsRef.current++;
|
|
5081
|
+
const resetTooFarOut = retryAfterMs !== null && retryAfterMs > LEGACY_POLL_MAX_RETRY_AFTER_MS;
|
|
5082
|
+
if (resetTooFarOut || pollRateLimitsRef.current >= LEGACY_POLL_MAX_RATE_LIMITS) {
|
|
5083
|
+
giveUp("Rate limited by the API \u2014 stopped polling for updates on this run");
|
|
5084
|
+
return;
|
|
5085
|
+
}
|
|
5086
|
+
pollDelayRef.current = Math.max(retryAfterMs ?? UNNAMED_RATE_LIMIT_WAIT_MS, pollDelayRef.current);
|
|
5087
|
+
} else {
|
|
5088
|
+
pollFailuresRef.current++;
|
|
5089
|
+
if (__DEV__) console.warn("[useAgentChat] Polling error (will retry):", err);
|
|
5090
|
+
if (pollFailuresRef.current >= LEGACY_POLL_MAX_FAILURES) {
|
|
5091
|
+
giveUp("Lost connection to the agent \u2014 stopped polling for updates on this run");
|
|
5092
|
+
return;
|
|
5093
|
+
}
|
|
5094
|
+
pollDelayRef.current = nextFailureDelayMs(pollDelayRef.current, LEGACY_POLL_MAX_INTERVAL_MS);
|
|
5095
|
+
}
|
|
4617
5096
|
}
|
|
5097
|
+
schedule(applyJitter(pollDelayRef.current, LEGACY_POLL_JITTER_RATIO));
|
|
4618
5098
|
};
|
|
4619
|
-
poll();
|
|
4620
|
-
pollingIntervalRef.current = setInterval(poll, pollingInterval ?? 2e3);
|
|
5099
|
+
void poll();
|
|
4621
5100
|
}, [adapter, stopPolling, handleComplete, handleError, pollingInterval]);
|
|
4622
5101
|
useEffect(() => {
|
|
4623
5102
|
if (!useStreamController && isUsingFallback && currentExecutionId && isLoading && pollingEnabled !== false) {
|
|
@@ -5029,7 +5508,9 @@ function useAgentChat(options) {
|
|
|
5029
5508
|
args: {},
|
|
5030
5509
|
result: {
|
|
5031
5510
|
success: true,
|
|
5032
|
-
images: [
|
|
5511
|
+
images: [
|
|
5512
|
+
artifact.thumbnailUrl ? { url: artifact.url, thumbnailUrl: artifact.thumbnailUrl } : { url: artifact.url }
|
|
5513
|
+
]
|
|
5033
5514
|
},
|
|
5034
5515
|
status: "completed"
|
|
5035
5516
|
}));
|