@csntgao/ai-employee-client 0.15.7 → 0.16.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.js CHANGED
@@ -221,9 +221,9 @@ function extractText(raw) {
221
221
  try {
222
222
  const parsed = JSON.parse(value);
223
223
  if (typeof parsed === "string") return parsed.trim();
224
- const record3 = asRecord(parsed);
224
+ const record4 = asRecord(parsed);
225
225
  for (const key of ["text", "result", "message", "content"]) {
226
- const candidate = stringValue(record3[key]).trim();
226
+ const candidate = stringValue(record4[key]).trim();
227
227
  if (candidate) return candidate;
228
228
  }
229
229
  } catch {
@@ -941,9 +941,9 @@ var AiEmployeeClient = class {
941
941
  for (const streamEvent of event.result.streamEvents) {
942
942
  this.applyAssistantStreamEvent(event.sessionId, streamEvent);
943
943
  }
944
- for (const text2 of event.result.activities) {
944
+ for (const text3 of event.result.activities) {
945
945
  if (!this.assistantReceivedInCurrentTurn) {
946
- this.emit("work-activity", { sessionId: event.sessionId, text: text2 });
946
+ this.emit("work-activity", { sessionId: event.sessionId, text: text3 });
947
947
  }
948
948
  }
949
949
  for (const message of event.result.messages) {
@@ -1085,20 +1085,20 @@ var AiEmployeeClient = class {
1085
1085
  if (!this.isCurrentSocket(socket, generation)) return;
1086
1086
  this.socket = null;
1087
1087
  this.clearHeartbeatTimer();
1088
- const authenticationFailure3 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
1088
+ const authenticationFailure4 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
1089
1089
  this.socketAuthenticationFailure = null;
1090
1090
  if (!this.shouldReconnect || this.destroyed) {
1091
1091
  if (!this.destroyed) this.setState("disconnected");
1092
1092
  return;
1093
1093
  }
1094
- if (authenticationFailure3?.retryMode === "never") {
1094
+ if (authenticationFailure4?.retryMode === "never") {
1095
1095
  this.shouldReconnect = false;
1096
1096
  this.setState("disconnected");
1097
1097
  return;
1098
1098
  }
1099
1099
  if (code === 4400 || code === 4403) {
1100
1100
  this.shouldReconnect = false;
1101
- if (!authenticationFailure3) {
1101
+ if (!authenticationFailure4) {
1102
1102
  const errorCode = code === 4403 ? "AUTHENTICATION_FAILED" : "PROTOCOL_ERROR";
1103
1103
  const message = code === 4403 ? "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u5B9E\u65F6\u8FDE\u63A5\u3002" : "\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002";
1104
1104
  this.emitError(errorCode, message, false);
@@ -1106,15 +1106,15 @@ var AiEmployeeClient = class {
1106
1106
  this.setState("disconnected");
1107
1107
  return;
1108
1108
  }
1109
- if (authenticationFailure3?.retryMode === "availability" || code === 1013) {
1110
- if (!authenticationFailure3) {
1109
+ if (authenticationFailure4?.retryMode === "availability" || code === 1013) {
1110
+ if (!authenticationFailure4) {
1111
1111
  this.emitError("REALTIME_UNAVAILABLE", "\u5B9E\u65F6\u9274\u6743\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
1112
1112
  }
1113
1113
  this.scheduleAvailabilityReconnect();
1114
1114
  return;
1115
1115
  }
1116
1116
  if (code === 4401) {
1117
- if (!authenticationFailure3) {
1117
+ if (!authenticationFailure4) {
1118
1118
  this.emitError("AUTHENTICATION_FAILED", "\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true);
1119
1119
  }
1120
1120
  }
@@ -2070,13 +2070,13 @@ var CustomerSupportClient = class {
2070
2070
  this.socket = null;
2071
2071
  if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
2072
2072
  this.heartbeatTimer = null;
2073
- const authenticationFailure3 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
2073
+ const authenticationFailure4 = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
2074
2074
  this.socketAuthenticationFailure = null;
2075
2075
  if (!this.shouldReconnect || this.destroyed) {
2076
2076
  if (!this.destroyed) this.setState("disconnected");
2077
2077
  return;
2078
2078
  }
2079
- if (authenticationFailure3?.retryMode === "never") {
2079
+ if (authenticationFailure4?.retryMode === "never") {
2080
2080
  this.shouldReconnect = false;
2081
2081
  this.stopFallbackHistoryRefresh();
2082
2082
  this.setState("disconnected");
@@ -2085,7 +2085,7 @@ var CustomerSupportClient = class {
2085
2085
  if (code === 4400 || code === 4403) {
2086
2086
  this.shouldReconnect = false;
2087
2087
  this.stopFallbackHistoryRefresh();
2088
- if (!authenticationFailure3) {
2088
+ if (!authenticationFailure4) {
2089
2089
  this.emitError(
2090
2090
  code === 4400 ? "PROTOCOL_ERROR" : "AUTHENTICATION_FAILED",
2091
2091
  code === 4400 ? "\u5BA2\u670D\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002" : "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u4F7F\u7528\u4EBA\u5DE5\u5BA2\u670D\u3002",
@@ -2095,15 +2095,15 @@ var CustomerSupportClient = class {
2095
2095
  this.setState("disconnected");
2096
2096
  return;
2097
2097
  }
2098
- if (authenticationFailure3?.retryMode === "availability" || code === 1013) {
2099
- if (!authenticationFailure3) {
2098
+ if (authenticationFailure4?.retryMode === "availability" || code === 1013) {
2099
+ if (!authenticationFailure4) {
2100
2100
  this.emitError("REALTIME_UNAVAILABLE", "\u5BA2\u670D\u5B9E\u65F6\u9274\u6743\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
2101
2101
  }
2102
2102
  this.scheduleAvailabilityReconnect();
2103
2103
  return;
2104
2104
  }
2105
- if (authenticationFailure3?.retryMode === "token" || code === 4401) {
2106
- if (!authenticationFailure3) {
2105
+ if (authenticationFailure4?.retryMode === "token" || code === 4401) {
2106
+ if (!authenticationFailure4) {
2107
2107
  this.emitError("AUTHENTICATION_FAILED", "\u5BA2\u670D\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true);
2108
2108
  }
2109
2109
  if (this.tokenReconnectAttempt >= MAX_TOKEN_RECONNECTS) {
@@ -2465,6 +2465,7 @@ var NotificationCenterClient = class {
2465
2465
  this.applyRead([id], result.readAt);
2466
2466
  this.setUnreadCount(result.unreadCount);
2467
2467
  this.emit("notification-read", { notificationId: id });
2468
+ if (this.readStatus === "unread") this.resyncUnreadInbox();
2468
2469
  } catch {
2469
2470
  this.emitError("MARK_READ_FAILED", "\u901A\u77E5\u6807\u8BB0\u5DF2\u8BFB\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002", true);
2470
2471
  }
@@ -2610,6 +2611,7 @@ var NotificationCenterClient = class {
2610
2611
  if (change.kind === "read") {
2611
2612
  this.applyRead(change.notificationIds, change.readAt ?? null);
2612
2613
  this.syncRealtimeUnreadCount(change.unreadCount);
2614
+ if (this.readStatus === "unread") this.resyncUnreadInbox();
2613
2615
  return;
2614
2616
  }
2615
2617
  const removed = new Set(change.notificationIds);
@@ -2621,6 +2623,15 @@ var NotificationCenterClient = class {
2621
2623
  }
2622
2624
  }
2623
2625
  this.syncRealtimeUnreadCount(change.unreadCount);
2626
+ if (this.readStatus === "unread") this.resyncUnreadInbox();
2627
+ }
2628
+ resyncUnreadInbox() {
2629
+ if (this.destroyed) return;
2630
+ if (this.loadingInbox) {
2631
+ this.pendingResync = true;
2632
+ return;
2633
+ }
2634
+ void this.reloadInbox();
2624
2635
  }
2625
2636
  syncRealtimeUnreadCount(value) {
2626
2637
  if (this.applicationName && typeof value === "number") this.setUnreadCount(value);
@@ -2684,6 +2695,640 @@ function normalizedText2(value) {
2684
2695
  return typeof value === "string" ? value.trim() : "";
2685
2696
  }
2686
2697
 
2687
- export { AiEmployeeClient, AiEmployeeClientError, AiEmployeeTransportError, CustomerSupportClient, CustomerSupportClientError, NotificationCenterClient, NotificationCenterClientError, createAuthenticateFrame, createPingFrame, createSubscriptionFrame, hashText, isUuidV4, parseAiEmployeeAssistantContent, parseAiEmployeeEvent, parseJsonRecord };
2698
+ // src/user-realtime-client.ts
2699
+ var DEFAULT_RECONNECT3 = {
2700
+ initialDelayMs: 1e3,
2701
+ maximumDelayMs: 3e4,
2702
+ factor: 2,
2703
+ jitter: 0.2
2704
+ };
2705
+ var DEFAULT_MAXIMUM_SEEN_EVENT_IDS = 2e3;
2706
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 25e3;
2707
+ var CHANNEL_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
2708
+ var CURSOR_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/;
2709
+ var UserRealtimeClientError = class extends Error {
2710
+ code;
2711
+ constructor(code, message) {
2712
+ super(message);
2713
+ this.name = "UserRealtimeClientError";
2714
+ this.code = code;
2715
+ }
2716
+ };
2717
+ var UserRealtimeClient = class {
2718
+ requestedChannels;
2719
+ realtimeUrl;
2720
+ getAccessToken;
2721
+ cursorStore;
2722
+ reconnectOptions;
2723
+ webSocketFactory;
2724
+ minimumHeartbeatMs;
2725
+ maximumSeenEventIds;
2726
+ listeners = /* @__PURE__ */ new Map();
2727
+ cursors = /* @__PURE__ */ new Map();
2728
+ cursorWriteChains = /* @__PURE__ */ new Map();
2729
+ seenEventIds = /* @__PURE__ */ new Map();
2730
+ channelSequences = /* @__PURE__ */ new Map();
2731
+ pendingResyncs = /* @__PURE__ */ new Map();
2732
+ resyncGenerations = /* @__PURE__ */ new Map();
2733
+ socket = null;
2734
+ messageChain = Promise.resolve();
2735
+ reconnectTimer = null;
2736
+ heartbeatTimer = null;
2737
+ startPromise = null;
2738
+ currentState = "idle";
2739
+ socketGeneration = 0;
2740
+ lifecycleGeneration = 0;
2741
+ reconnectAttempt = 0;
2742
+ deliverySequence = 0;
2743
+ shouldReconnect = false;
2744
+ destroyed = false;
2745
+ socketAuthenticationFailure = null;
2746
+ constructor(options) {
2747
+ if (!options || typeof options !== "object") {
2748
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "\u7F3A\u5C11\u7EDF\u4E00\u5B9E\u65F6\u5BA2\u6237\u7AEF\u914D\u7F6E\u3002");
2749
+ }
2750
+ this.realtimeUrl = validateRealtimeUrl3(options.realtimeUrl);
2751
+ if (typeof options.getAccessToken !== "function") {
2752
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "\u7F3A\u5C11\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u63D0\u4F9B\u5668\u3002");
2753
+ }
2754
+ this.getAccessToken = options.getAccessToken;
2755
+ this.requestedChannels = normalizeChannels(options.requestedChannels);
2756
+ if (options.cursorStore !== void 0 && options.cursorStore !== null && (typeof options.cursorStore.load !== "function" || typeof options.cursorStore.save !== "function" || typeof options.cursorStore.clear !== "function")) {
2757
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "cursorStore \u914D\u7F6E\u65E0\u6548\u3002");
2758
+ }
2759
+ this.cursorStore = options.cursorStore ?? null;
2760
+ this.reconnectOptions = normalizeReconnect2(options.reconnect);
2761
+ this.minimumHeartbeatMs = positiveNumber2(options.minimumHeartbeatMs, 5e3);
2762
+ this.maximumSeenEventIds = positiveInteger(
2763
+ options.maximumSeenEventIds,
2764
+ DEFAULT_MAXIMUM_SEEN_EVENT_IDS
2765
+ );
2766
+ this.webSocketFactory = options.webSocketFactory ?? ((url) => {
2767
+ if (typeof WebSocket === "undefined") {
2768
+ throw new UserRealtimeClientError(
2769
+ "WEBSOCKET_UNAVAILABLE",
2770
+ "\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u4E0D\u652F\u6301\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002"
2771
+ );
2772
+ }
2773
+ return new WebSocket(url);
2774
+ });
2775
+ }
2776
+ get connectionState() {
2777
+ return this.currentState;
2778
+ }
2779
+ get eventSequence() {
2780
+ return this.deliverySequence;
2781
+ }
2782
+ get isDestroyed() {
2783
+ return this.destroyed;
2784
+ }
2785
+ on(type, listener) {
2786
+ this.assertUsable();
2787
+ const listeners = this.listeners.get(type) ?? /* @__PURE__ */ new Set();
2788
+ const untyped = listener;
2789
+ listeners.add(untyped);
2790
+ this.listeners.set(type, listeners);
2791
+ return () => listeners.delete(untyped);
2792
+ }
2793
+ start() {
2794
+ this.assertUsable();
2795
+ if (this.shouldReconnect) return this.startPromise ?? Promise.resolve();
2796
+ this.shouldReconnect = true;
2797
+ this.reconnectAttempt = 0;
2798
+ const lifecycle = ++this.lifecycleGeneration;
2799
+ this.setState("connecting");
2800
+ const pending = this.loadCursors(lifecycle).then(() => {
2801
+ if (!this.isCurrentLifecycle(lifecycle) || !this.shouldReconnect) return;
2802
+ this.openSocket(false);
2803
+ });
2804
+ this.startPromise = pending;
2805
+ const clear = () => {
2806
+ if (this.startPromise === pending) this.startPromise = null;
2807
+ };
2808
+ void pending.then(clear, clear);
2809
+ return pending;
2810
+ }
2811
+ stop() {
2812
+ if (this.destroyed) return;
2813
+ this.stopConnection();
2814
+ this.setState("stopped");
2815
+ }
2816
+ async restart(options = {}) {
2817
+ this.assertUsable();
2818
+ this.stopConnection();
2819
+ this.clearDeliveryState();
2820
+ if (options.clearCursors) await this.clearCursors();
2821
+ await this.start();
2822
+ }
2823
+ async clearCursors() {
2824
+ this.assertUsable();
2825
+ this.cursors.clear();
2826
+ this.pendingResyncs.clear();
2827
+ await Promise.all(this.requestedChannels.map((channel) => this.clearStoredCursor(channel)));
2828
+ }
2829
+ /**
2830
+ * Confirm that Host HTTP state was restored for a previously emitted resync signal.
2831
+ * Stale confirmations never overwrite a cursor advanced by a newer realtime event.
2832
+ */
2833
+ async confirmResync(detail) {
2834
+ this.assertUsable();
2835
+ const pending = this.pendingResyncs.get(detail.channel)?.detail;
2836
+ if (!pending || pending.generation !== detail.generation || pending.latestEventId !== detail.latestEventId || pending.sequence !== detail.sequence || (this.channelSequences.get(detail.channel) ?? 0) !== detail.sequence || !validCursor(detail.latestEventId)) {
2837
+ return false;
2838
+ }
2839
+ this.pendingResyncs.delete(detail.channel);
2840
+ this.rememberCursor(detail.channel, detail.latestEventId);
2841
+ await this.flushCursorWrite(detail.channel);
2842
+ return true;
2843
+ }
2844
+ destroy() {
2845
+ if (this.destroyed) return;
2846
+ this.stopConnection();
2847
+ this.destroyed = true;
2848
+ this.setState("destroyed");
2849
+ this.listeners.clear();
2850
+ this.clearDeliveryState();
2851
+ this.cursors.clear();
2852
+ this.cursorWriteChains.clear();
2853
+ }
2854
+ async loadCursors(lifecycle) {
2855
+ if (!this.cursorStore) return;
2856
+ await Promise.all(
2857
+ this.requestedChannels.map(async (channel) => {
2858
+ try {
2859
+ const value = await this.cursorStore?.load(channel);
2860
+ if (!this.isCurrentLifecycle(lifecycle)) return;
2861
+ const cursor = normalizedCursor(value);
2862
+ if (cursor) this.cursors.set(channel, cursor);
2863
+ else this.cursors.delete(channel);
2864
+ } catch {
2865
+ if (this.isCurrentLifecycle(lifecycle)) {
2866
+ this.emitError(
2867
+ "CURSOR_STORE_FAILED",
2868
+ "\u5B9E\u65F6\u6E38\u6807\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u4ECE\u5F53\u524D\u670D\u52A1\u7AEF\u4F4D\u7F6E\u7EE7\u7EED\u3002",
2869
+ true,
2870
+ channel
2871
+ );
2872
+ }
2873
+ }
2874
+ })
2875
+ );
2876
+ }
2877
+ openSocket(reconnecting) {
2878
+ if (this.destroyed || !this.shouldReconnect) return;
2879
+ this.clearReconnectTimer();
2880
+ this.closeCurrentSocket();
2881
+ this.setState(reconnecting ? "reconnecting" : "connecting");
2882
+ let socket;
2883
+ try {
2884
+ socket = this.webSocketFactory(this.realtimeUrl);
2885
+ } catch {
2886
+ this.shouldReconnect = false;
2887
+ this.emitError("WEBSOCKET_UNAVAILABLE", "\u65E0\u6CD5\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002", false);
2888
+ this.setState("stopped");
2889
+ return;
2890
+ }
2891
+ const generation = ++this.socketGeneration;
2892
+ this.socket = socket;
2893
+ this.socketAuthenticationFailure = null;
2894
+ socket.onopen = () => void this.authenticate(socket, generation);
2895
+ socket.onmessage = (event) => {
2896
+ const next = this.messageChain.catch(() => void 0).then(() => this.handleSocketData(event.data, socket, generation));
2897
+ this.messageChain = next;
2898
+ void next.catch(() => {
2899
+ if (this.isCurrentSocket(socket, generation)) {
2900
+ this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u5904\u7406\u5931\u8D25\u3002", true);
2901
+ }
2902
+ });
2903
+ };
2904
+ socket.onerror = () => {
2905
+ if (!this.isCurrentSocket(socket, generation)) return;
2906
+ this.emitError("WEBSOCKET_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u53D1\u751F\u5F02\u5E38\u3002", true);
2907
+ };
2908
+ socket.onclose = (event) => this.handleSocketClose(socket, generation, event.code);
2909
+ }
2910
+ async authenticate(socket, generation) {
2911
+ if (!this.isCurrentSocket(socket, generation)) return;
2912
+ this.setState("authenticating");
2913
+ let accessToken = "";
2914
+ try {
2915
+ const supplied = await this.getAccessToken();
2916
+ accessToken = typeof supplied === "string" ? supplied.trim() : "";
2917
+ } catch {
2918
+ }
2919
+ if (!this.isCurrentSocket(socket, generation)) return;
2920
+ if (!accessToken) {
2921
+ this.shouldReconnect = false;
2922
+ this.emitError("TOKEN_UNAVAILABLE", "\u65E0\u6CD5\u83B7\u53D6\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u3002", false);
2923
+ this.closeCurrentSocket();
2924
+ this.setState("stopped");
2925
+ return;
2926
+ }
2927
+ const cursors = {};
2928
+ for (const channel of this.requestedChannels)
2929
+ cursors[channel] = this.cursors.get(channel) ?? null;
2930
+ socket.send(
2931
+ JSON.stringify({
2932
+ type: "authenticate",
2933
+ protocol_version: 1,
2934
+ access_token: accessToken,
2935
+ requested_channels: [...this.requestedChannels],
2936
+ cursors
2937
+ })
2938
+ );
2939
+ accessToken = "";
2940
+ }
2941
+ async handleSocketData(data, socket, generation) {
2942
+ if (!this.isCurrentSocket(socket, generation)) return;
2943
+ let raw = "";
2944
+ if (typeof data === "string") raw = data;
2945
+ else if (typeof Blob !== "undefined" && data instanceof Blob) raw = await data.text();
2946
+ else if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
2947
+ raw = new TextDecoder().decode(data);
2948
+ } else {
2949
+ this.emitError("PROTOCOL_ERROR", "\u6536\u5230\u65E0\u6CD5\u8BC6\u522B\u7684\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u3002", true);
2950
+ return;
2951
+ }
2952
+ if (!this.isCurrentSocket(socket, generation)) return;
2953
+ const message = parseJsonRecord(raw);
2954
+ if (!message) {
2955
+ this.emitError("PROTOCOL_ERROR", "\u6536\u5230\u65E0\u6CD5\u89E3\u6790\u7684\u7EDF\u4E00\u5B9E\u65F6\u6D88\u606F\u3002", true);
2956
+ return;
2957
+ }
2958
+ this.handleServerMessage(message, socket, generation);
2959
+ }
2960
+ handleServerMessage(message, socket, generation) {
2961
+ const type = text2(message.type);
2962
+ if (type === "realtime_ready") {
2963
+ this.handleReady(message, socket, generation);
2964
+ return;
2965
+ }
2966
+ if (type === "realtime_event") {
2967
+ this.handleRealtimeEvent(message);
2968
+ return;
2969
+ }
2970
+ if (type === "channel_resync_required") {
2971
+ this.handleResyncRequired(message);
2972
+ return;
2973
+ }
2974
+ if (type === "channel_error") {
2975
+ this.handleChannelError(message, socket);
2976
+ return;
2977
+ }
2978
+ if (type === "authentication_error") {
2979
+ this.handleAuthenticationError(message, socket, generation);
2980
+ return;
2981
+ }
2982
+ if (type === "protocol_error") {
2983
+ this.shouldReconnect = false;
2984
+ this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u5F02\u5E38\u3002", false);
2985
+ this.closeCurrentSocket();
2986
+ this.setState("stopped");
2987
+ }
2988
+ }
2989
+ handleReady(message, socket, generation) {
2990
+ const channels = record3(message.channels);
2991
+ const unavailable = this.requestedChannels.find(
2992
+ (channel) => record3(channels[channel]).available !== true
2993
+ );
2994
+ if (unavailable) {
2995
+ this.shouldReconnect = false;
2996
+ this.emitError("CHANNEL_UNAVAILABLE", "\u8BF7\u6C42\u7684\u5B9E\u65F6\u901A\u9053\u4E0D\u53EF\u7528\u3002", false, unavailable);
2997
+ this.closeCurrentSocket();
2998
+ this.setState("stopped");
2999
+ return;
3000
+ }
3001
+ this.reconnectAttempt = 0;
3002
+ this.startHeartbeat(numberValue3(message.heartbeat_interval_seconds), socket, generation);
3003
+ this.setState("connected");
3004
+ this.emit("ready", { channels: [...this.requestedChannels] });
3005
+ }
3006
+ handleRealtimeEvent(message) {
3007
+ const channel = text2(message.channel);
3008
+ if (!this.requestedChannels.includes(channel)) return;
3009
+ const event = record3(message.event);
3010
+ const eventId = normalizedEventId(event.event_id) ?? normalizedEventId(message.event_id) ?? "";
3011
+ if (!eventId) {
3012
+ this.emitError("PROTOCOL_ERROR", "\u5B9E\u65F6\u4E8B\u4EF6\u7F3A\u5C11\u7A33\u5B9A\u4E8B\u4EF6\u6807\u8BC6\u3002", true, channel);
3013
+ return;
3014
+ }
3015
+ const cursor = normalizedCursor(event.cursor) ?? normalizedCursor(message.cursor) ?? "";
3016
+ if (this.hasSeenEvent(channel, eventId)) return;
3017
+ const previousChannelSequence = this.channelSequences.get(channel) ?? 0;
3018
+ const sequence = previousChannelSequence + 1;
3019
+ this.channelSequences.set(channel, sequence);
3020
+ this.deliverySequence += 1;
3021
+ const detail = {
3022
+ channel,
3023
+ event: message.event,
3024
+ eventId,
3025
+ cursor,
3026
+ sequence
3027
+ };
3028
+ if (!this.emitSafely("channel-event", detail)) {
3029
+ this.channelSequences.set(channel, previousChannelSequence);
3030
+ this.deliverySequence -= 1;
3031
+ this.emitError("EVENT_HANDLER_FAILED", "\u5B9E\u65F6\u4E8B\u4EF6\u5904\u7406\u5931\u8D25\uFF0C\u5C06\u7B49\u5F85\u670D\u52A1\u7AEF\u91CD\u653E\u3002", true, channel);
3032
+ return;
3033
+ }
3034
+ this.rememberEventId(channel, eventId);
3035
+ if (cursor) this.rememberCursor(channel, cursor);
3036
+ else this.emitError("PROTOCOL_ERROR", "\u5B9E\u65F6\u4E8B\u4EF6\u7F3A\u5C11\u53EF\u7EED\u4F20\u6E38\u6807\u3002", true, channel);
3037
+ }
3038
+ handleResyncRequired(message) {
3039
+ const channel = text2(message.channel);
3040
+ if (!this.requestedChannels.includes(channel)) return;
3041
+ const generation = (this.resyncGenerations.get(channel) ?? 0) + 1;
3042
+ this.resyncGenerations.set(channel, generation);
3043
+ const detail = {
3044
+ channel,
3045
+ reason: text2(message.reason) || "cursor_expired",
3046
+ latestEventId: normalizedCursor(message.latest_event_id) ?? "",
3047
+ sequence: this.channelSequences.get(channel) ?? 0,
3048
+ generation
3049
+ };
3050
+ this.pendingResyncs.set(channel, { detail });
3051
+ this.emit("resync-required", detail);
3052
+ }
3053
+ handleChannelError(message, socket) {
3054
+ const channel = text2(message.channel);
3055
+ if (!this.requestedChannels.includes(channel)) return;
3056
+ this.emitError("CHANNEL_UNAVAILABLE", "\u5B9E\u65F6\u901A\u9053\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5\u3002", true, channel);
3057
+ socket.close();
3058
+ }
3059
+ handleAuthenticationError(message, socket, generation) {
3060
+ const failure = authenticationFailure3(text2(message.code));
3061
+ this.socketAuthenticationFailure = { generation, retryMode: failure.retryMode };
3062
+ this.emitError(failure.code, failure.message, failure.retryMode !== "never");
3063
+ socket.close();
3064
+ }
3065
+ startHeartbeat(intervalSeconds, socket, generation) {
3066
+ this.clearHeartbeatTimer();
3067
+ const requestedMs = Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds * 1e3 : DEFAULT_HEARTBEAT_INTERVAL_MS;
3068
+ const intervalMs = Math.max(this.minimumHeartbeatMs, requestedMs);
3069
+ this.heartbeatTimer = setInterval(() => {
3070
+ if (!this.isCurrentSocket(socket, generation) || socket.readyState !== 1) return;
3071
+ socket.send(JSON.stringify(createPingFrame()));
3072
+ }, intervalMs);
3073
+ }
3074
+ handleSocketClose(socket, generation, code) {
3075
+ if (!this.isCurrentSocket(socket, generation)) return;
3076
+ this.socket = null;
3077
+ this.clearHeartbeatTimer();
3078
+ const failure = this.socketAuthenticationFailure?.generation === generation ? this.socketAuthenticationFailure : null;
3079
+ this.socketAuthenticationFailure = null;
3080
+ if (!this.shouldReconnect || this.destroyed) return;
3081
+ if (failure?.retryMode === "never" || code === 4400 || code === 4401 || code === 4403) {
3082
+ this.shouldReconnect = false;
3083
+ if (!failure) {
3084
+ if (code === 4401) {
3085
+ this.emitError("AUTHENTICATION_FAILED", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\u3002", false);
3086
+ } else if (code === 4403) {
3087
+ this.emitError("AUTHENTICATION_FAILED", "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002", false);
3088
+ } else {
3089
+ this.emitError("PROTOCOL_ERROR", "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002", false);
3090
+ }
3091
+ }
3092
+ this.setState("stopped");
3093
+ return;
3094
+ }
3095
+ if (code === 1013 && !failure) {
3096
+ this.emitError("REALTIME_UNAVAILABLE", "\u7EDF\u4E00\u5B9E\u65F6\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002", true);
3097
+ }
3098
+ this.scheduleReconnect();
3099
+ }
3100
+ scheduleReconnect() {
3101
+ if (this.destroyed || !this.shouldReconnect || this.reconnectTimer) return;
3102
+ this.setState("reconnecting");
3103
+ const base = Math.min(
3104
+ this.reconnectOptions.initialDelayMs * this.reconnectOptions.factor ** this.reconnectAttempt,
3105
+ this.reconnectOptions.maximumDelayMs
3106
+ );
3107
+ const jitter = base * this.reconnectOptions.jitter * (Math.random() * 2 - 1);
3108
+ this.reconnectAttempt += 1;
3109
+ const lifecycle = this.lifecycleGeneration;
3110
+ this.reconnectTimer = setTimeout(
3111
+ () => {
3112
+ this.reconnectTimer = null;
3113
+ if (!this.isCurrentLifecycle(lifecycle) || !this.shouldReconnect) return;
3114
+ this.openSocket(true);
3115
+ },
3116
+ Math.max(0, base + jitter)
3117
+ );
3118
+ }
3119
+ rememberEventId(channel, eventId) {
3120
+ const seen = this.seenEventIds.get(channel) ?? /* @__PURE__ */ new Set();
3121
+ seen.add(eventId);
3122
+ while (seen.size > this.maximumSeenEventIds) {
3123
+ const oldest = seen.values().next().value;
3124
+ if (typeof oldest !== "string") break;
3125
+ seen.delete(oldest);
3126
+ }
3127
+ this.seenEventIds.set(channel, seen);
3128
+ }
3129
+ hasSeenEvent(channel, eventId) {
3130
+ return this.seenEventIds.get(channel)?.has(eventId) === true;
3131
+ }
3132
+ rememberCursor(channel, cursor) {
3133
+ this.cursors.set(channel, cursor);
3134
+ if (!this.cursorStore) return;
3135
+ const previous = this.cursorWriteChains.get(channel) ?? Promise.resolve();
3136
+ const next = previous.catch(() => void 0).then(() => this.cursorStore?.save(channel, cursor)).then(() => void 0).catch(() => {
3137
+ this.emitError("CURSOR_STORE_FAILED", "\u5B9E\u65F6\u6E38\u6807\u4FDD\u5B58\u5931\u8D25\u3002", true, channel);
3138
+ });
3139
+ this.cursorWriteChains.set(channel, next);
3140
+ void next.finally(() => {
3141
+ if (this.cursorWriteChains.get(channel) === next) this.cursorWriteChains.delete(channel);
3142
+ });
3143
+ }
3144
+ async flushCursorWrite(channel) {
3145
+ await this.cursorWriteChains.get(channel);
3146
+ }
3147
+ async clearStoredCursor(channel) {
3148
+ try {
3149
+ await this.cursorWriteChains.get(channel);
3150
+ await this.cursorStore?.clear(channel);
3151
+ } catch {
3152
+ this.emitError("CURSOR_STORE_FAILED", "\u5B9E\u65F6\u6E38\u6807\u6E05\u7406\u5931\u8D25\u3002", true, channel);
3153
+ }
3154
+ }
3155
+ stopConnection() {
3156
+ this.shouldReconnect = false;
3157
+ this.startPromise = null;
3158
+ this.lifecycleGeneration += 1;
3159
+ this.clearReconnectTimer();
3160
+ this.clearHeartbeatTimer();
3161
+ this.closeCurrentSocket();
3162
+ this.socketAuthenticationFailure = null;
3163
+ }
3164
+ closeCurrentSocket() {
3165
+ const socket = this.socket;
3166
+ this.socket = null;
3167
+ this.socketGeneration += 1;
3168
+ if (socket && socket.readyState < 2) socket.close();
3169
+ }
3170
+ clearReconnectTimer() {
3171
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
3172
+ this.reconnectTimer = null;
3173
+ }
3174
+ clearHeartbeatTimer() {
3175
+ if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
3176
+ this.heartbeatTimer = null;
3177
+ }
3178
+ clearDeliveryState() {
3179
+ this.seenEventIds.clear();
3180
+ this.channelSequences.clear();
3181
+ this.pendingResyncs.clear();
3182
+ this.resyncGenerations.clear();
3183
+ this.deliverySequence = 0;
3184
+ }
3185
+ isCurrentSocket(socket, generation) {
3186
+ return !this.destroyed && this.shouldReconnect && this.socket === socket && this.socketGeneration === generation;
3187
+ }
3188
+ isCurrentLifecycle(generation) {
3189
+ return !this.destroyed && this.lifecycleGeneration === generation;
3190
+ }
3191
+ setState(current) {
3192
+ if (this.currentState === current) return;
3193
+ const previous = this.currentState;
3194
+ this.currentState = current;
3195
+ this.emit("state-change", { previous, current });
3196
+ }
3197
+ emitError(code, message, recoverable, channel) {
3198
+ const detail = {
3199
+ code,
3200
+ message,
3201
+ recoverable,
3202
+ ...channel ? { channel } : {}
3203
+ };
3204
+ this.emit("error", detail);
3205
+ return detail;
3206
+ }
3207
+ emit(type, detail) {
3208
+ for (const listener of this.listeners.get(type) ?? []) {
3209
+ try {
3210
+ listener(detail);
3211
+ } catch {
3212
+ }
3213
+ }
3214
+ }
3215
+ emitSafely(type, detail) {
3216
+ let success = true;
3217
+ for (const listener of this.listeners.get(type) ?? []) {
3218
+ try {
3219
+ listener(detail);
3220
+ } catch {
3221
+ success = false;
3222
+ }
3223
+ }
3224
+ return success;
3225
+ }
3226
+ assertUsable() {
3227
+ if (this.destroyed) {
3228
+ throw new UserRealtimeClientError("CLIENT_DESTROYED", "\u7EDF\u4E00\u5B9E\u65F6\u5BA2\u6237\u7AEF\u5DF2\u7ECF\u9500\u6BC1\u3002");
3229
+ }
3230
+ }
3231
+ };
3232
+ function validateRealtimeUrl3(rawUrl) {
3233
+ let url;
3234
+ try {
3235
+ url = new URL(rawUrl);
3236
+ } catch {
3237
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "realtimeUrl \u4E0D\u662F\u6709\u6548\u5730\u5740\u3002");
3238
+ }
3239
+ if (!["ws:", "wss:"].includes(url.protocol)) {
3240
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "realtimeUrl \u53EA\u5141\u8BB8 ws: \u6216 wss:\u3002");
3241
+ }
3242
+ if (url.search || url.hash || url.username || url.password) {
3243
+ throw new UserRealtimeClientError(
3244
+ "INVALID_CONFIGURATION",
3245
+ "realtimeUrl \u4E0D\u5141\u8BB8\u5305\u542B\u67E5\u8BE2\u53C2\u6570\u3001\u7247\u6BB5\u6216\u7528\u6237\u51ED\u636E\u3002"
3246
+ );
3247
+ }
3248
+ return url.toString();
3249
+ }
3250
+ function normalizeChannels(channels) {
3251
+ if (!Array.isArray(channels)) {
3252
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "requestedChannels \u4E0D\u80FD\u4E3A\u7A7A\u3002");
3253
+ }
3254
+ const normalized = [...new Set(channels.map((channel) => text2(channel).trim()))];
3255
+ if (!normalized.length || normalized.some((channel) => !CHANNEL_PATTERN.test(channel))) {
3256
+ throw new UserRealtimeClientError("INVALID_CONFIGURATION", "requestedChannels \u5305\u542B\u65E0\u6548\u901A\u9053\u3002");
3257
+ }
3258
+ return Object.freeze(normalized);
3259
+ }
3260
+ function normalizeReconnect2(value) {
3261
+ const jitter = value?.jitter;
3262
+ return {
3263
+ initialDelayMs: positiveNumber2(value?.initialDelayMs, DEFAULT_RECONNECT3.initialDelayMs),
3264
+ maximumDelayMs: positiveNumber2(value?.maximumDelayMs, DEFAULT_RECONNECT3.maximumDelayMs),
3265
+ factor: Math.max(1, positiveNumber2(value?.factor, DEFAULT_RECONNECT3.factor)),
3266
+ jitter: typeof jitter === "number" && Number.isFinite(jitter) ? Math.min(1, Math.max(0, jitter)) : DEFAULT_RECONNECT3.jitter
3267
+ };
3268
+ }
3269
+ function authenticationFailure3(code) {
3270
+ if (isRealtimeAvailabilityFailure2(code)) {
3271
+ return {
3272
+ code: "REALTIME_UNAVAILABLE",
3273
+ message: "\u7EDF\u4E00\u5B9E\u65F6\u670D\u52A1\u6682\u4E0D\u53EF\u7528\uFF0C\u6B63\u5728\u91CD\u8BD5\u3002",
3274
+ retryMode: "availability"
3275
+ };
3276
+ }
3277
+ if (code === "INVALID_ARGUMENT") {
3278
+ return {
3279
+ code: "PROTOCOL_ERROR",
3280
+ message: "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u534F\u8BAE\u4E0D\u53D7\u652F\u6301\u3002",
3281
+ retryMode: "never"
3282
+ };
3283
+ }
3284
+ if (code === "ACCESS_DENIED") {
3285
+ return {
3286
+ code: "AUTHENTICATION_FAILED",
3287
+ message: "\u5F53\u524D\u8EAB\u4EFD\u65E0\u6743\u5EFA\u7ACB\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u3002",
3288
+ retryMode: "never"
3289
+ };
3290
+ }
3291
+ return {
3292
+ code: "AUTHENTICATION_FAILED",
3293
+ message: "\u7EDF\u4E00\u5B9E\u65F6\u8FDE\u63A5\u51ED\u8BC1\u5DF2\u5931\u6548\u3002",
3294
+ retryMode: "never"
3295
+ };
3296
+ }
3297
+ function isRealtimeAvailabilityFailure2(code) {
3298
+ return code === "REALTIME_UNAVAILABLE" || code === "KANBAN_AUTH_FAILED" || code.startsWith("SERVICE_");
3299
+ }
3300
+ function normalizedCursor(value) {
3301
+ const cursor = text2(value).trim();
3302
+ return validCursor(cursor) ? cursor : null;
3303
+ }
3304
+ function normalizedEventId(value) {
3305
+ const eventId = text2(value).trim();
3306
+ if (!eventId || eventId.length > 256) return null;
3307
+ for (const character of eventId) {
3308
+ const code = character.charCodeAt(0);
3309
+ if (code < 32 || code === 127) return null;
3310
+ }
3311
+ return eventId;
3312
+ }
3313
+ function validCursor(value) {
3314
+ return CURSOR_PATTERN.test(value);
3315
+ }
3316
+ function positiveNumber2(value, fallback) {
3317
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
3318
+ }
3319
+ function positiveInteger(value, fallback) {
3320
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.max(1, Math.floor(value)) : fallback;
3321
+ }
3322
+ function text2(value) {
3323
+ return typeof value === "string" ? value : "";
3324
+ }
3325
+ function record3(value) {
3326
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
3327
+ }
3328
+ function numberValue3(value) {
3329
+ return typeof value === "number" ? value : Number.NaN;
3330
+ }
3331
+
3332
+ export { AiEmployeeClient, AiEmployeeClientError, AiEmployeeTransportError, CustomerSupportClient, CustomerSupportClientError, NotificationCenterClient, NotificationCenterClientError, UserRealtimeClient, UserRealtimeClientError, createAuthenticateFrame, createPingFrame, createSubscriptionFrame, hashText, isUuidV4, parseAiEmployeeAssistantContent, parseAiEmployeeEvent, parseJsonRecord };
2688
3333
  //# sourceMappingURL=index.js.map
2689
3334
  //# sourceMappingURL=index.js.map