@agentick/client-multiplexer 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Agentick Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # @agentick/client-multiplexer
2
+
3
+ Multi-tab connection multiplexer for Agentick client. Reduces server connections by sharing a single SSE connection across all browser tabs.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @agentick/client-multiplexer
9
+ # or
10
+ pnpm add @agentick/client-multiplexer
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createClient } from '@agentick/client';
17
+ import { createSharedTransport } from '@agentick/client-multiplexer';
18
+
19
+ // Create client with shared transport
20
+ const client = createClient({
21
+ baseUrl: '/api',
22
+ transport: createSharedTransport({ baseUrl: '/api', token: 'your-token' }),
23
+ });
24
+
25
+ // Use exactly like a regular client
26
+ const session = client.session('main');
27
+ session.subscribe();
28
+ session.onEvent((event) => console.log(event));
29
+
30
+ const handle = session.send('Hello!');
31
+ await handle.result;
32
+ ```
33
+
34
+ ## How It Works
35
+
36
+ The multiplexer uses browser tab leader election to ensure only one tab maintains the actual server connection:
37
+
38
+ 1. **Leader Election**: Uses Web Locks API (instant, reliable) with BroadcastChannel fallback for older browsers
39
+ 2. **Connection Sharing**: Only the leader tab opens the SSE connection to the server
40
+ 3. **Message Forwarding**: Follower tabs send requests via BroadcastChannel to the leader
41
+ 4. **Event Broadcasting**: Leader broadcasts server events to all tabs
42
+ 5. **Automatic Failover**: When leader tab closes, a new leader is elected and re-establishes subscriptions
43
+
44
+ ## Features
45
+
46
+ - **Resource Efficient**: Single server connection regardless of tab count
47
+ - **Transparent**: Works with existing Agentick client code
48
+ - **Automatic Failover**: Seamless recovery when leader tab closes
49
+ - **Subscription Aggregation**: Leader maintains union of all tabs' subscriptions
50
+ - **Per-Tab Filtering**: Each tab only receives events for its own sessions
51
+
52
+ ## API
53
+
54
+ ### createSharedTransport(config)
55
+
56
+ Creates a shared transport instance. Supports both SSE and WebSocket transports.
57
+
58
+ ```typescript
59
+ import { createSharedTransport, type SharedTransportConfig } from '@agentick/client-multiplexer';
60
+
61
+ // SSE transport (default for http:// URLs)
62
+ const sseTransport = createSharedTransport({
63
+ baseUrl: 'https://api.example.com',
64
+ token: 'your-auth-token', // Optional
65
+ timeout: 30000, // Optional
66
+ withCredentials: true, // Optional
67
+ });
68
+
69
+ // WebSocket transport (default for ws:// URLs)
70
+ const wsTransport = createSharedTransport({
71
+ baseUrl: 'wss://api.example.com',
72
+ token: 'your-auth-token',
73
+ clientId: 'my-client', // Optional
74
+ reconnect: { // Optional
75
+ enabled: true,
76
+ maxAttempts: 5,
77
+ delay: 1000,
78
+ },
79
+ });
80
+
81
+ // Explicit transport selection
82
+ const explicitTransport = createSharedTransport({
83
+ baseUrl: 'https://api.example.com',
84
+ transport: 'websocket', // Force WebSocket even with http:// URL
85
+ });
86
+ ```
87
+
88
+ ### SharedTransport
89
+
90
+ The transport implements `ClientTransport` from `@agentick/client` plus additional properties:
91
+
92
+ ```typescript
93
+ // Check leadership status
94
+ transport.isLeader; // boolean
95
+
96
+ // Get unique tab identifier
97
+ transport.tabId; // string
98
+
99
+ // Listen for leadership changes
100
+ transport.onLeadershipChange((isLeader) => {
101
+ console.log(isLeader ? 'This tab is now the leader' : 'Leadership transferred');
102
+ });
103
+ ```
104
+
105
+ ### Accessing Transport from Client
106
+
107
+ ```typescript
108
+ import { createClient, type ClientTransport } from '@agentick/client';
109
+ import { createSharedTransport, type SharedTransport } from '@agentick/client-multiplexer';
110
+
111
+ const client = createClient({
112
+ baseUrl: '/api',
113
+ transport: createSharedTransport({ baseUrl: '/api' }),
114
+ });
115
+
116
+ // Access the transport for leadership info
117
+ const transport = client.getTransport() as SharedTransport | undefined;
118
+ console.log('Is leader:', transport?.isLeader);
119
+ ```
120
+
121
+ ## Architecture
122
+
123
+ ```
124
+ ┌─────────────────────────────────────────────────────────────────────┐
125
+ │ Browser Tabs │
126
+ ├──────────────────┬──────────────────┬──────────────────────────────┤
127
+ │ Tab 1 │ Tab 2 │ Tab 3 │
128
+ │ (Leader) │ (Follower) │ (Follower) │
129
+ │ │ │ │
130
+ │ SharedTransport │ SharedTransport │ SharedTransport │
131
+ │ │ │ │ │ │ │
132
+ │ │ │ │ │ │ │
133
+ │ ┌───▼───┐ │ ┌───▼───┐ │ ┌───▼───┐ │
134
+ │ │ SSE │ │ │Bridge │ │ │Bridge │ │
135
+ │ │ Conn │ │ │ Only │ │ │ Only │ │
136
+ │ └───┬───┘ │ └───┬───┘ │ └───┬───┘ │
137
+ │ │ │ │ │ │ │
138
+ └──────┼───────────┴──────┼───────────┴──────┼───────────────────────┘
139
+ │ │ │
140
+ │ ◄─────────────┴──────────────────┘
141
+ │ BroadcastChannel
142
+
143
+
144
+ ┌───────┐
145
+ │Server │
146
+ └───────┘
147
+ ```
148
+
149
+ ## Failover
150
+
151
+ When the leader tab closes:
152
+
153
+ 1. Other tabs detect leadership vacancy (via Web Locks or heartbeat timeout)
154
+ 2. New leader is elected (fastest tab to acquire lock)
155
+ 3. New leader broadcasts `leader:ready` message
156
+ 4. Follower tabs respond with their current subscriptions
157
+ 5. New leader aggregates subscriptions and re-subscribes on the server
158
+ 6. Events flow again to all tabs
159
+
160
+ ## Browser Support
161
+
162
+ - **Web Locks API**: Chrome 69+, Firefox 96+, Safari 15.4+, Edge 79+
163
+ - **BroadcastChannel**: All modern browsers
164
+ - **Fallback**: Heartbeat-based election for browsers without Web Locks
165
+
166
+ ## License
167
+
168
+ ISC
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Broadcast Bridge
3
+ *
4
+ * Handles inter-tab communication via BroadcastChannel.
5
+ * Used for forwarding requests from followers to leader and events back.
6
+ */
7
+ import type { TransportEventData } from "@agentick/client";
8
+ import type { SendInput, ChannelEvent, ToolConfirmationResponse } from "@agentick/client";
9
+ export type BridgeMessage = {
10
+ type: "leader:collecting_subscriptions";
11
+ tabId: string;
12
+ } | {
13
+ type: "leader:transport_ready";
14
+ tabId: string;
15
+ } | {
16
+ type: "ping:leader";
17
+ tabId: string;
18
+ } | {
19
+ type: "pong:leader";
20
+ tabId: string;
21
+ } | {
22
+ type: "subscriptions:announce";
23
+ tabId: string;
24
+ sessions: string[];
25
+ channels: string[];
26
+ } | {
27
+ type: "request:send";
28
+ requestId: string;
29
+ tabId: string;
30
+ sessionId: string;
31
+ input: SendInput;
32
+ } | {
33
+ type: "request:subscribe";
34
+ requestId: string;
35
+ tabId: string;
36
+ sessionId: string;
37
+ } | {
38
+ type: "request:unsubscribe";
39
+ requestId: string;
40
+ tabId: string;
41
+ sessionId: string;
42
+ } | {
43
+ type: "request:abort";
44
+ requestId: string;
45
+ tabId: string;
46
+ sessionId: string;
47
+ reason?: string;
48
+ } | {
49
+ type: "request:close";
50
+ requestId: string;
51
+ tabId: string;
52
+ sessionId: string;
53
+ } | {
54
+ type: "request:toolResult";
55
+ requestId: string;
56
+ tabId: string;
57
+ sessionId: string;
58
+ toolUseId: string;
59
+ result: ToolConfirmationResponse;
60
+ } | {
61
+ type: "request:channelSubscribe";
62
+ requestId: string;
63
+ tabId: string;
64
+ sessionId: string;
65
+ channel: string;
66
+ } | {
67
+ type: "request:channelPublish";
68
+ requestId: string;
69
+ tabId: string;
70
+ sessionId: string;
71
+ channel: string;
72
+ event: ChannelEvent;
73
+ } | {
74
+ type: "response";
75
+ requestId: string;
76
+ ok: true;
77
+ result?: unknown;
78
+ } | {
79
+ type: "response";
80
+ requestId: string;
81
+ ok: false;
82
+ error: string;
83
+ } | {
84
+ type: "event";
85
+ event: TransportEventData;
86
+ } | {
87
+ type: "stream:event";
88
+ requestId: string;
89
+ event: TransportEventData;
90
+ } | {
91
+ type: "stream:end";
92
+ requestId: string;
93
+ } | {
94
+ type: "stream:error";
95
+ requestId: string;
96
+ error: string;
97
+ };
98
+ export type MessageHandler = (message: BridgeMessage) => void;
99
+ export interface BroadcastBridge {
100
+ readonly tabId: string;
101
+ /** Send a message to all tabs */
102
+ broadcast(message: BridgeMessage): void;
103
+ /** Register handler for incoming messages */
104
+ onMessage(handler: MessageHandler): () => void;
105
+ /**
106
+ * Collect responses from all tabs within a timeout.
107
+ * Useful for gathering subscription announcements during failover.
108
+ */
109
+ collectResponses<T extends BridgeMessage>(messageType: T["type"], timeout: number): Promise<T[]>;
110
+ /** Close the bridge */
111
+ close(): void;
112
+ }
113
+ export declare function createBroadcastBridge(channelName: string, tabId: string): BroadcastBridge;
114
+ export declare function generateRequestId(tabId: string): string;
115
+ //# sourceMappingURL=broadcast-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broadcast-bridge.d.ts","sourceRoot":"","sources":["../src/broadcast-bridge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAM1F,MAAM,MAAM,aAAa,GAErB;IAAE,IAAI,EAAE,iCAAiC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAGzF;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC9E;IACE,IAAI,EAAE,oBAAoB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,wBAAwB,CAAC;CAClC,GACD;IACE,IAAI,EAAE,0BAA0B,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,wBAAwB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;CACrB,GAGD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAGjE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,GAG5C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;AAM9D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,iCAAiC;IACjC,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAExC,6CAA6C;IAC7C,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CAAC;IAE/C;;;OAGG;IACH,gBAAgB,CAAC,CAAC,SAAS,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAEjG,uBAAuB;IACvB,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAsDzF;AAQD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Broadcast Bridge
3
+ *
4
+ * Handles inter-tab communication via BroadcastChannel.
5
+ * Used for forwarding requests from followers to leader and events back.
6
+ */
7
+ export function createBroadcastBridge(channelName, tabId) {
8
+ const channel = new BroadcastChannel(`agentick:bridge:${channelName}`);
9
+ const handlers = new Set();
10
+ channel.onmessage = (event) => {
11
+ const message = event.data;
12
+ for (const handler of handlers) {
13
+ try {
14
+ handler(message);
15
+ }
16
+ catch (e) {
17
+ console.error("Error in bridge message handler:", e);
18
+ }
19
+ }
20
+ };
21
+ return {
22
+ get tabId() {
23
+ return tabId;
24
+ },
25
+ broadcast(message) {
26
+ channel.postMessage(message);
27
+ },
28
+ onMessage(handler) {
29
+ handlers.add(handler);
30
+ return () => handlers.delete(handler);
31
+ },
32
+ collectResponses(messageType, timeout) {
33
+ return new Promise((resolve) => {
34
+ const responses = [];
35
+ const cleanup = this.onMessage((msg) => {
36
+ if (msg.type === messageType) {
37
+ responses.push(msg);
38
+ }
39
+ });
40
+ setTimeout(() => {
41
+ cleanup();
42
+ resolve(responses);
43
+ }, timeout);
44
+ });
45
+ },
46
+ close() {
47
+ channel.close();
48
+ handlers.clear();
49
+ },
50
+ };
51
+ }
52
+ // ============================================================================
53
+ // Request Helper
54
+ // ============================================================================
55
+ let requestIdCounter = 0;
56
+ export function generateRequestId(tabId) {
57
+ return `${tabId}-${++requestIdCounter}`;
58
+ }
59
+ //# sourceMappingURL=broadcast-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broadcast-bridge.js","sourceRoot":"","sources":["../src/broadcast-bridge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoFH,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,KAAa;IACtE,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAAC,mBAAmB,WAAW,EAAE,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3C,OAAO,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAqB,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,CAAC,CAAC;YACnB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,KAAK;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,SAAS,CAAC,OAAsB;YAC9B,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAED,SAAS,CAAC,OAAuB;YAC/B,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAED,gBAAgB,CACd,WAAsB,EACtB,OAAe;YAEf,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,MAAM,SAAS,GAAQ,EAAE,CAAC;gBAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;oBACrC,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;wBAC7B,SAAS,CAAC,IAAI,CAAC,GAAQ,CAAC,CAAC;oBAC3B,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,UAAU,CAAC,GAAG,EAAE;oBACd,OAAO,EAAE,CAAC;oBACV,OAAO,CAAC,SAAS,CAAC,CAAC;gBACrB,CAAC,EAAE,OAAO,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK;YACH,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,IAAI,gBAAgB,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,OAAO,GAAG,KAAK,IAAI,EAAE,gBAAgB,EAAE,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @agentick/client-multiplexer
3
+ *
4
+ * Multi-tab connection multiplexer for Agentick client.
5
+ *
6
+ * Reduces server connections by electing a leader tab that maintains
7
+ * the SSE connection while other tabs communicate via BroadcastChannel.
8
+ *
9
+ * Features:
10
+ * - Leader election using Web Locks API (instant, reliable)
11
+ * - BroadcastChannel fallback for older browsers
12
+ * - Automatic failover when leader tab closes
13
+ * - Per-tab subscription filtering (each tab only receives its events)
14
+ * - Subscription aggregation (leader subscribes to union of all tabs' sessions)
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { createClient } from '@agentick/client';
19
+ * import { createSharedTransport } from '@agentick/client-multiplexer';
20
+ *
21
+ * // Create client with shared transport
22
+ * const client = createClient({
23
+ * baseUrl: '/api',
24
+ * transport: createSharedTransport({ baseUrl: '/api', token: 'your-token' }),
25
+ * });
26
+ *
27
+ * // Use exactly like a regular client
28
+ * const session = client.session('main');
29
+ * session.subscribe(); // Subscribe to events
30
+ * session.onEvent((event) => console.log(event));
31
+ *
32
+ * // Send a message
33
+ * const handle = session.send('Hello!');
34
+ * await handle.result;
35
+ *
36
+ * // Check leadership status (optional, for debugging/UI)
37
+ * const transport = client.getTransport() as SharedTransport | undefined;
38
+ * console.log('Is leader:', transport?.isLeader);
39
+ * ```
40
+ */
41
+ export { SharedTransport, createSharedTransport, type SharedTransportConfig, } from "./shared-transport.js";
42
+ export { createLeaderElector, type LeaderElector } from "./leader-elector.js";
43
+ export { createBroadcastBridge, type BroadcastBridge, type BridgeMessage, } from "./broadcast-bridge.js";
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @agentick/client-multiplexer
3
+ *
4
+ * Multi-tab connection multiplexer for Agentick client.
5
+ *
6
+ * Reduces server connections by electing a leader tab that maintains
7
+ * the SSE connection while other tabs communicate via BroadcastChannel.
8
+ *
9
+ * Features:
10
+ * - Leader election using Web Locks API (instant, reliable)
11
+ * - BroadcastChannel fallback for older browsers
12
+ * - Automatic failover when leader tab closes
13
+ * - Per-tab subscription filtering (each tab only receives its events)
14
+ * - Subscription aggregation (leader subscribes to union of all tabs' sessions)
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { createClient } from '@agentick/client';
19
+ * import { createSharedTransport } from '@agentick/client-multiplexer';
20
+ *
21
+ * // Create client with shared transport
22
+ * const client = createClient({
23
+ * baseUrl: '/api',
24
+ * transport: createSharedTransport({ baseUrl: '/api', token: 'your-token' }),
25
+ * });
26
+ *
27
+ * // Use exactly like a regular client
28
+ * const session = client.session('main');
29
+ * session.subscribe(); // Subscribe to events
30
+ * session.onEvent((event) => console.log(event));
31
+ *
32
+ * // Send a message
33
+ * const handle = session.send('Hello!');
34
+ * await handle.result;
35
+ *
36
+ * // Check leadership status (optional, for debugging/UI)
37
+ * const transport = client.getTransport() as SharedTransport | undefined;
38
+ * console.log('Is leader:', transport?.isLeader);
39
+ * ```
40
+ */
41
+ export { SharedTransport, createSharedTransport, } from "./shared-transport.js";
42
+ export { createLeaderElector } from "./leader-elector.js";
43
+ export { createBroadcastBridge, } from "./broadcast-bridge.js";
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EACL,eAAe,EACf,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAsB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EACL,qBAAqB,GAGtB,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Leader Elector
3
+ *
4
+ * Uses Web Locks API for instant, reliable leader election across browser tabs.
5
+ * Falls back to BroadcastChannel-based election if Web Locks unavailable.
6
+ */
7
+ export interface LeaderElector {
8
+ readonly isLeader: boolean;
9
+ readonly tabId: string;
10
+ awaitLeadership(): Promise<void>;
11
+ resign(): void;
12
+ onLeadershipChange(callback: (isLeader: boolean) => void): () => void;
13
+ }
14
+ /**
15
+ * Create a leader elector for the given channel name.
16
+ * Uses Web Locks API (instant) with BroadcastChannel fallback.
17
+ */
18
+ export declare function createLeaderElector(channelName: string): LeaderElector;
19
+ //# sourceMappingURL=leader-elector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leader-elector.d.ts","sourceRoot":"","sources":["../src/leader-elector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;IACf,kBAAkB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACvE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAqNtE"}