@jini-ai/chat 0.3.10 → 0.3.11
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.
|
@@ -32,6 +32,9 @@ export interface UseChatPaneRuntimeInventoryResult {
|
|
|
32
32
|
* the daemon goes from offline to online, the window regains focus, or the tab becomes visible.
|
|
33
33
|
* A RESOLVED list — even an empty one — is a finished answer; hosts reject when theirs is not one.
|
|
34
34
|
*
|
|
35
|
+
* Health polling runs only while the tab is visible (hidden tabs made no-op status calls every
|
|
36
|
+
* `pollIntervalMs` forever), and never overlaps a status call still in flight.
|
|
37
|
+
*
|
|
35
38
|
* @complexity Time: O(n) per inventory response; space: O(n) for the snapshot.
|
|
36
39
|
* @overallScore 100/100
|
|
37
40
|
*/
|
|
@@ -17,6 +17,9 @@ const DEFAULT_RETRY_DELAYS_MS = [1_000, 2_000, 4_000, 8_000, 15_000, 30_000];
|
|
|
17
17
|
* the daemon goes from offline to online, the window regains focus, or the tab becomes visible.
|
|
18
18
|
* A RESOLVED list — even an empty one — is a finished answer; hosts reject when theirs is not one.
|
|
19
19
|
*
|
|
20
|
+
* Health polling runs only while the tab is visible (hidden tabs made no-op status calls every
|
|
21
|
+
* `pollIntervalMs` forever), and never overlaps a status call still in flight.
|
|
22
|
+
*
|
|
20
23
|
* @complexity Time: O(n) per inventory response; space: O(n) for the snapshot.
|
|
21
24
|
* @overallScore 100/100
|
|
22
25
|
*/
|
|
@@ -99,10 +102,42 @@ export function useChatPaneRuntimeInventory({ access, initialAgents = EMPTY_AGEN
|
|
|
99
102
|
return;
|
|
100
103
|
retry.reset();
|
|
101
104
|
void loadAgents(false);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
// Health polls only while the tab is visible: a hidden tab stops its timer, and showing it
|
|
106
|
+
// again checks once at once and restarts the interval. A tick is skipped while the previous
|
|
107
|
+
// status call is still out, so a slow host never piles up requests.
|
|
108
|
+
let timer;
|
|
109
|
+
let statusInFlight = false;
|
|
110
|
+
const pollStatus = () => {
|
|
111
|
+
if (statusInFlight)
|
|
112
|
+
return;
|
|
113
|
+
statusInFlight = true;
|
|
114
|
+
void refreshStatus().finally(() => {
|
|
115
|
+
statusInFlight = false;
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
const startPolling = () => {
|
|
119
|
+
if (timer === undefined)
|
|
120
|
+
timer = window.setInterval(pollStatus, pollIntervalMs);
|
|
121
|
+
};
|
|
122
|
+
const stopPolling = () => {
|
|
105
123
|
window.clearInterval(timer);
|
|
124
|
+
timer = undefined;
|
|
125
|
+
};
|
|
126
|
+
const onVisibilityChange = () => {
|
|
127
|
+
if (document.visibilityState !== 'visible') {
|
|
128
|
+
stopPolling();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
pollStatus();
|
|
132
|
+
startPolling();
|
|
133
|
+
};
|
|
134
|
+
pollStatus();
|
|
135
|
+
if (document.visibilityState === 'visible')
|
|
136
|
+
startPolling();
|
|
137
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
138
|
+
return () => {
|
|
139
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
140
|
+
stopPolling();
|
|
106
141
|
retry.cancel();
|
|
107
142
|
inventory.supersede();
|
|
108
143
|
health.supersede();
|
|
@@ -111,17 +146,24 @@ export function useChatPaneRuntimeInventory({ access, initialAgents = EMPTY_AGEN
|
|
|
111
146
|
useEffect(() => {
|
|
112
147
|
if (!access)
|
|
113
148
|
return;
|
|
114
|
-
|
|
149
|
+
// The status check on becoming visible belongs to the polling effect above; this one only
|
|
150
|
+
// retries an unanswered inventory load (and, on focus, checks status too).
|
|
151
|
+
const onFocus = () => {
|
|
115
152
|
if (document.visibilityState === 'hidden')
|
|
116
153
|
return;
|
|
117
154
|
void refreshStatus();
|
|
118
155
|
reloadIfUnanswered();
|
|
119
156
|
};
|
|
120
|
-
|
|
121
|
-
|
|
157
|
+
const onVisible = () => {
|
|
158
|
+
if (document.visibilityState === 'hidden')
|
|
159
|
+
return;
|
|
160
|
+
reloadIfUnanswered();
|
|
161
|
+
};
|
|
162
|
+
window.addEventListener('focus', onFocus);
|
|
163
|
+
document.addEventListener('visibilitychange', onVisible);
|
|
122
164
|
return () => {
|
|
123
|
-
window.removeEventListener('focus',
|
|
124
|
-
document.removeEventListener('visibilitychange',
|
|
165
|
+
window.removeEventListener('focus', onFocus);
|
|
166
|
+
document.removeEventListener('visibilitychange', onVisible);
|
|
125
167
|
};
|
|
126
168
|
}, [access, refreshStatus, reloadIfUnanswered]);
|
|
127
169
|
const rescanAgents = useCallback(() => loadAgents(true), [loadAgents]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/chat",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Jini's chat/artifact frontend, consolidated into one package with two subpaths: framework-free chat vocabulary and pure parsers in `/core`, headless hooks + presentational components + slots in `/react`. Import only the layer you need.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@mcp-ui/client": "7.1.1",
|
|
65
65
|
"@mcp-ui/server": "6.1.0",
|
|
66
66
|
"@jini-ai/agentic": "0.3.7",
|
|
67
|
-
"@jini-ai/protocol": "0.3.1",
|
|
68
67
|
"@jini-ai/platform": "0.3.0",
|
|
68
|
+
"@jini-ai/protocol": "0.3.1",
|
|
69
69
|
"@jini-ai/ui": "0.3.14"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|