@nimblebrain/synapse 0.16.0 → 0.17.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/README.md +61 -111
- package/dist/{chunk-SN7YXLAV.cjs → chunk-EHLQWCYV.cjs} +6 -6
- package/dist/{chunk-SN7YXLAV.cjs.map → chunk-EHLQWCYV.cjs.map} +1 -1
- package/dist/{chunk-ZMNYUGSF.cjs → chunk-FDMRJZKT.cjs} +576 -662
- package/dist/chunk-FDMRJZKT.cjs.map +1 -0
- package/dist/{chunk-E7OOOYVR.js → chunk-SRQNQ26I.js} +3 -3
- package/dist/{chunk-E7OOOYVR.js.map → chunk-SRQNQ26I.js.map} +1 -1
- package/dist/{chunk-RYWW6EHB.cjs → chunk-TXJMGQYF.cjs} +3 -5
- package/dist/chunk-TXJMGQYF.cjs.map +1 -0
- package/dist/{chunk-J5ICAHK2.js → chunk-UPDBDXQB.js} +4 -5
- package/dist/chunk-UPDBDXQB.js.map +1 -0
- package/dist/{chunk-GNTQJSQX.js → chunk-VYR6V47N.js} +573 -663
- package/dist/chunk-VYR6V47N.js.map +1 -0
- package/dist/codegen/cli.cjs +1 -1
- package/dist/codegen/cli.js +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/connect.iife.global.js +28 -28
- package/dist/{detect-C_qmYsh9.d.cts → detect-BHYg26_d.d.cts} +1 -1
- package/dist/{detect-Dfd0upoz.d.ts → detect-Bf8Q_0dK.d.ts} +1 -1
- package/dist/host/index.cjs +14 -14
- package/dist/host/index.d.cts +3 -3
- package/dist/host/index.d.ts +3 -3
- package/dist/host/index.js +2 -2
- package/dist/index.cjs +28 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -26
- package/dist/index.d.ts +58 -26
- package/dist/index.js +3 -81
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +81 -140
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +66 -82
- package/dist/react/index.d.ts +66 -82
- package/dist/react/index.js +82 -136
- package/dist/react/index.js.map +1 -1
- package/dist/{server-ERRDGEZ7.js → server-5N76YCWC.js} +3 -3
- package/dist/{server-ERRDGEZ7.js.map → server-5N76YCWC.js.map} +1 -1
- package/dist/{server-P54IYX2G.cjs → server-QIYIJHD5.cjs} +3 -3
- package/dist/{server-P54IYX2G.cjs.map → server-QIYIJHD5.cjs.map} +1 -1
- package/dist/synapse-runtime.iife.global.js +28 -28
- package/dist/synapse-ui.iife.global.js +2 -2
- package/dist/{types-BSZasM8q.d.cts → types-uEO4VFJ2.d.cts} +87 -166
- package/dist/{types-BSZasM8q.d.ts → types-uEO4VFJ2.d.ts} +87 -166
- package/dist/ui/index.cjs +48 -35
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +48 -35
- package/dist/ui/index.js.map +1 -1
- package/dist/vite/index.cjs +1 -1
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GNTQJSQX.js.map +0 -1
- package/dist/chunk-J5ICAHK2.js.map +0 -1
- package/dist/chunk-RYWW6EHB.cjs.map +0 -1
- package/dist/chunk-ZMNYUGSF.cjs.map +0 -1
package/dist/react/index.cjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
3
|
+
var chunkFDMRJZKT_cjs = require('../chunk-FDMRJZKT.cjs');
|
|
4
|
+
require('../chunk-TXJMGQYF.cjs');
|
|
5
5
|
var react = require('react');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
7
|
|
|
8
8
|
var AppContext = react.createContext(null);
|
|
9
|
-
function AppProvider({ children,
|
|
9
|
+
function AppProvider({ children, ...options }) {
|
|
10
10
|
const [app, setApp] = react.useState(null);
|
|
11
|
+
const [error, setError] = react.useState(null);
|
|
11
12
|
const connectingRef = react.useRef(false);
|
|
12
13
|
react.useEffect(() => {
|
|
13
14
|
if (connectingRef.current) return;
|
|
14
15
|
connectingRef.current = true;
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
chunkFDMRJZKT_cjs.connect(options).then(setApp, (err) => {
|
|
17
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
17
18
|
});
|
|
18
19
|
}, []);
|
|
20
|
+
if (error) throw error;
|
|
19
21
|
if (!app) return null;
|
|
20
22
|
return /* @__PURE__ */ jsxRuntime.jsx(AppContext.Provider, { value: app, children });
|
|
21
23
|
}
|
|
@@ -28,18 +30,32 @@ function useAppContext() {
|
|
|
28
30
|
}
|
|
29
31
|
return ctx;
|
|
30
32
|
}
|
|
31
|
-
|
|
32
|
-
// src/react/connect-hooks.ts
|
|
33
33
|
function useApp() {
|
|
34
34
|
return useAppContext();
|
|
35
35
|
}
|
|
36
|
+
function useTheme() {
|
|
37
|
+
const app = useAppContext();
|
|
38
|
+
const [theme, setTheme] = react.useState(() => app.theme);
|
|
39
|
+
react.useEffect(() => {
|
|
40
|
+
setTheme(app.theme);
|
|
41
|
+
return app.on("theme-changed", setTheme);
|
|
42
|
+
}, [app]);
|
|
43
|
+
return theme;
|
|
44
|
+
}
|
|
45
|
+
function useHostContext() {
|
|
46
|
+
const app = useAppContext();
|
|
47
|
+
const [ctx, setCtx] = react.useState(() => app.hostContext);
|
|
48
|
+
react.useEffect(() => {
|
|
49
|
+
setCtx(app.hostContext);
|
|
50
|
+
return app.on("host-context-changed", (c) => setCtx(c));
|
|
51
|
+
}, [app]);
|
|
52
|
+
return ctx;
|
|
53
|
+
}
|
|
36
54
|
function useToolResult() {
|
|
37
55
|
const app = useAppContext();
|
|
38
56
|
const [data, setData] = react.useState(null);
|
|
39
57
|
react.useEffect(() => {
|
|
40
|
-
return app.on("tool-result",
|
|
41
|
-
setData(result);
|
|
42
|
-
});
|
|
58
|
+
return app.on("tool-result", setData);
|
|
43
59
|
}, [app]);
|
|
44
60
|
return data;
|
|
45
61
|
}
|
|
@@ -47,76 +63,27 @@ function useToolInput() {
|
|
|
47
63
|
const app = useAppContext();
|
|
48
64
|
const [input, setInput] = react.useState(null);
|
|
49
65
|
react.useEffect(() => {
|
|
50
|
-
return app.on("tool-input",
|
|
51
|
-
setInput(args);
|
|
52
|
-
});
|
|
66
|
+
return app.on("tool-input", setInput);
|
|
53
67
|
}, [app]);
|
|
54
68
|
return input;
|
|
55
69
|
}
|
|
56
|
-
function useConnectTheme() {
|
|
57
|
-
const app = useAppContext();
|
|
58
|
-
const [theme, setTheme] = react.useState(() => app.theme);
|
|
59
|
-
react.useEffect(() => {
|
|
60
|
-
setTheme(app.theme);
|
|
61
|
-
return app.on("theme-changed", (t) => {
|
|
62
|
-
setTheme(t);
|
|
63
|
-
});
|
|
64
|
-
}, [app]);
|
|
65
|
-
return theme;
|
|
66
|
-
}
|
|
67
70
|
function useResize() {
|
|
68
71
|
const app = useAppContext();
|
|
69
72
|
return react.useCallback((width, height) => app.resize(width, height), [app]);
|
|
70
73
|
}
|
|
71
|
-
var SynapseContext = react.createContext(null);
|
|
72
|
-
function SynapseProvider({ children, ...options }) {
|
|
73
|
-
const ref = react.useRef(null);
|
|
74
|
-
if (ref.current === null || ref.current.destroyed) {
|
|
75
|
-
ref.current = chunkZMNYUGSF_cjs.createSynapse(options);
|
|
76
|
-
}
|
|
77
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(SynapseContext.Provider, { value: ref.current, children: [
|
|
78
|
-
/* @__PURE__ */ jsxRuntime.jsx(ThemeInjector, { synapse: ref.current }),
|
|
79
|
-
children
|
|
80
|
-
] });
|
|
81
|
-
}
|
|
82
|
-
function ThemeInjector({ synapse }) {
|
|
83
|
-
const [theme, setTheme] = react.useState(() => synapse.getTheme());
|
|
84
|
-
react.useEffect(() => {
|
|
85
|
-
setTheme(synapse.getTheme());
|
|
86
|
-
return synapse.onThemeChanged(setTheme);
|
|
87
|
-
}, [synapse]);
|
|
88
|
-
react.useEffect(() => {
|
|
89
|
-
chunkRYWW6EHB_cjs.applyTheme(theme.mode, theme.tokens, theme.fontFaces);
|
|
90
|
-
}, [theme]);
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
function useSynapseContext() {
|
|
94
|
-
const ctx = react.useContext(SynapseContext);
|
|
95
|
-
if (!ctx) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"useSynapse must be used within a <SynapseProvider>. Wrap your app component tree with <SynapseProvider>."
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
return ctx;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// src/react/hooks.ts
|
|
104
|
-
function useSynapse() {
|
|
105
|
-
return useSynapseContext();
|
|
106
|
-
}
|
|
107
74
|
function useCallTool(toolName) {
|
|
108
|
-
const
|
|
75
|
+
const app = useAppContext();
|
|
109
76
|
const [isPending, setIsPending] = react.useState(false);
|
|
110
77
|
const [error, setError] = react.useState(null);
|
|
111
78
|
const [data, setData] = react.useState(null);
|
|
112
79
|
const callIdRef = react.useRef(0);
|
|
113
80
|
const call = react.useCallback(
|
|
114
|
-
async (args) => {
|
|
81
|
+
async (args, options) => {
|
|
115
82
|
const id = ++callIdRef.current;
|
|
116
83
|
setIsPending(true);
|
|
117
84
|
setError(null);
|
|
118
85
|
try {
|
|
119
|
-
const result = await
|
|
86
|
+
const result = await app.callTool(toolName, args, options);
|
|
120
87
|
if (id === callIdRef.current) {
|
|
121
88
|
setData(result.data);
|
|
122
89
|
setIsPending(false);
|
|
@@ -131,64 +98,37 @@ function useCallTool(toolName) {
|
|
|
131
98
|
throw err;
|
|
132
99
|
}
|
|
133
100
|
},
|
|
134
|
-
[
|
|
101
|
+
[app, toolName]
|
|
135
102
|
);
|
|
136
103
|
return { call, isPending, error, data };
|
|
137
104
|
}
|
|
138
105
|
function useDataSync(callback) {
|
|
139
|
-
const
|
|
140
|
-
const callbackRef = react.useRef(callback);
|
|
141
|
-
callbackRef.current = callback;
|
|
142
|
-
react.useEffect(() => {
|
|
143
|
-
return synapse.onDataChanged((event) => callbackRef.current(event));
|
|
144
|
-
}, [synapse]);
|
|
145
|
-
}
|
|
146
|
-
function useAgentAction(callback) {
|
|
147
|
-
const synapse = useSynapseContext();
|
|
106
|
+
const app = useAppContext();
|
|
148
107
|
const callbackRef = react.useRef(callback);
|
|
149
108
|
callbackRef.current = callback;
|
|
150
109
|
react.useEffect(() => {
|
|
151
|
-
return
|
|
152
|
-
}, [
|
|
153
|
-
}
|
|
154
|
-
function useHostContext() {
|
|
155
|
-
const synapse = useSynapseContext();
|
|
156
|
-
const [ctx, setCtx] = react.useState(() => synapse.getHostContext());
|
|
157
|
-
react.useEffect(() => {
|
|
158
|
-
setCtx(synapse.getHostContext());
|
|
159
|
-
return synapse.onHostContextChanged((c) => setCtx(c));
|
|
160
|
-
}, [synapse]);
|
|
161
|
-
return ctx;
|
|
162
|
-
}
|
|
163
|
-
function useTheme() {
|
|
164
|
-
const synapse = useSynapseContext();
|
|
165
|
-
const [theme, setTheme] = react.useState(() => synapse.getTheme());
|
|
166
|
-
react.useEffect(() => {
|
|
167
|
-
setTheme(synapse.getTheme());
|
|
168
|
-
return synapse.onThemeChanged(setTheme);
|
|
169
|
-
}, [synapse]);
|
|
170
|
-
return theme;
|
|
171
|
-
}
|
|
172
|
-
function useAction() {
|
|
173
|
-
const synapse = useSynapseContext();
|
|
174
|
-
return react.useCallback(
|
|
175
|
-
(action, params) => synapse.action(action, params),
|
|
176
|
-
[synapse]
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
function useChat() {
|
|
180
|
-
const synapse = useSynapseContext();
|
|
181
|
-
return react.useCallback(
|
|
182
|
-
(message, context) => synapse.chat(message, context),
|
|
183
|
-
[synapse]
|
|
184
|
-
);
|
|
110
|
+
return app.on("data-changed", (event) => callbackRef.current(event));
|
|
111
|
+
}, [app]);
|
|
185
112
|
}
|
|
186
|
-
|
|
187
|
-
|
|
113
|
+
var MODEL_CONTEXT_DEBOUNCE_MS = 250;
|
|
114
|
+
function useModelContext(factory, deps) {
|
|
115
|
+
const app = useAppContext();
|
|
116
|
+
const timerRef = react.useRef(null);
|
|
188
117
|
const push = react.useCallback(
|
|
189
|
-
(state, summary) =>
|
|
190
|
-
|
|
118
|
+
(state, summary) => {
|
|
119
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
120
|
+
timerRef.current = setTimeout(() => {
|
|
121
|
+
timerRef.current = null;
|
|
122
|
+
app.updateModelContext(state, summary);
|
|
123
|
+
}, MODEL_CONTEXT_DEBOUNCE_MS);
|
|
124
|
+
},
|
|
125
|
+
[app]
|
|
191
126
|
);
|
|
127
|
+
react.useEffect(() => {
|
|
128
|
+
return () => {
|
|
129
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
192
132
|
const factoryRef = react.useRef(factory);
|
|
193
133
|
factoryRef.current = factory;
|
|
194
134
|
react.useEffect(() => {
|
|
@@ -198,40 +138,46 @@ function useVisibleState(factory, deps) {
|
|
|
198
138
|
}, [...deps ?? [], push]);
|
|
199
139
|
if (!factory) return push;
|
|
200
140
|
}
|
|
141
|
+
function useSendMessage() {
|
|
142
|
+
const app = useAppContext();
|
|
143
|
+
return react.useCallback(
|
|
144
|
+
(text, context) => app.sendMessage(text, context),
|
|
145
|
+
[app]
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function useAction() {
|
|
149
|
+
const app = useAppContext();
|
|
150
|
+
return react.useCallback(
|
|
151
|
+
(name, params) => chunkFDMRJZKT_cjs.action(app, name, params),
|
|
152
|
+
[app]
|
|
153
|
+
);
|
|
154
|
+
}
|
|
201
155
|
function useFileUpload() {
|
|
202
|
-
const
|
|
156
|
+
const app = useAppContext();
|
|
203
157
|
const [isPending, setIsPending] = react.useState(false);
|
|
204
|
-
const
|
|
158
|
+
const one = react.useCallback(
|
|
205
159
|
async (options) => {
|
|
206
160
|
setIsPending(true);
|
|
207
161
|
try {
|
|
208
|
-
return await
|
|
162
|
+
return await chunkFDMRJZKT_cjs.pickFile(app, options);
|
|
209
163
|
} finally {
|
|
210
164
|
setIsPending(false);
|
|
211
165
|
}
|
|
212
166
|
},
|
|
213
|
-
[
|
|
167
|
+
[app]
|
|
214
168
|
);
|
|
215
|
-
const
|
|
169
|
+
const many = react.useCallback(
|
|
216
170
|
async (options) => {
|
|
217
171
|
setIsPending(true);
|
|
218
172
|
try {
|
|
219
|
-
return await
|
|
173
|
+
return await chunkFDMRJZKT_cjs.pickFiles(app, options);
|
|
220
174
|
} finally {
|
|
221
175
|
setIsPending(false);
|
|
222
176
|
}
|
|
223
177
|
},
|
|
224
|
-
[
|
|
178
|
+
[app]
|
|
225
179
|
);
|
|
226
|
-
return { pickFile, pickFiles, isPending };
|
|
227
|
-
}
|
|
228
|
-
function useStore(store) {
|
|
229
|
-
const state = react.useSyncExternalStore(
|
|
230
|
-
(onStoreChange) => store.subscribe(onStoreChange),
|
|
231
|
-
() => store.getState(),
|
|
232
|
-
() => store.getState()
|
|
233
|
-
);
|
|
234
|
-
return { state, dispatch: store.dispatch };
|
|
180
|
+
return { pickFile: one, pickFiles: many, isPending };
|
|
235
181
|
}
|
|
236
182
|
var COMPLETED_STATUS = "completed";
|
|
237
183
|
var FAILED_STATUS = "failed";
|
|
@@ -245,7 +191,7 @@ var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
|
245
191
|
var POLL_FALLBACK_MULTIPLIER = 1.5;
|
|
246
192
|
var MAX_REFRESH_FAILURES = 5;
|
|
247
193
|
function useCallToolAsTask(toolName) {
|
|
248
|
-
const
|
|
194
|
+
const app = useAppContext();
|
|
249
195
|
const [task, setTask] = react.useState(null);
|
|
250
196
|
const [result, setResult] = react.useState(null);
|
|
251
197
|
const [error, setError] = react.useState(null);
|
|
@@ -309,7 +255,7 @@ function useCallToolAsTask(toolName) {
|
|
|
309
255
|
refreshFailureCountRef.current = 0;
|
|
310
256
|
let handle;
|
|
311
257
|
try {
|
|
312
|
-
handle = await
|
|
258
|
+
handle = await chunkFDMRJZKT_cjs.callToolAsTask(app, toolName, args, options);
|
|
313
259
|
} catch (err) {
|
|
314
260
|
if (gen !== genRef.current) throw err;
|
|
315
261
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
@@ -340,7 +286,7 @@ function useCallToolAsTask(toolName) {
|
|
|
340
286
|
terminalRef.current = true;
|
|
341
287
|
clearPollTimer();
|
|
342
288
|
setTask((prev) => {
|
|
343
|
-
const status = res.isError ?
|
|
289
|
+
const status = res.isError ? FAILED_STATUS : COMPLETED_STATUS;
|
|
344
290
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
345
291
|
return prev ? { ...prev, status, lastUpdatedAt: now } : {
|
|
346
292
|
taskId: handle.task.taskId,
|
|
@@ -364,9 +310,9 @@ function useCallToolAsTask(toolName) {
|
|
|
364
310
|
clearPollTimer();
|
|
365
311
|
setTask((prev) => {
|
|
366
312
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
367
|
-
return prev ? { ...prev, status:
|
|
313
|
+
return prev ? { ...prev, status: FAILED_STATUS, lastUpdatedAt: now } : {
|
|
368
314
|
taskId: handle.task.taskId,
|
|
369
|
-
status:
|
|
315
|
+
status: FAILED_STATUS,
|
|
370
316
|
ttl: handle.task.ttl,
|
|
371
317
|
createdAt: handle.task.createdAt,
|
|
372
318
|
lastUpdatedAt: now
|
|
@@ -379,7 +325,7 @@ function useCallToolAsTask(toolName) {
|
|
|
379
325
|
if (!terminalRef.current) scheduleNextPoll(gen);
|
|
380
326
|
return handle;
|
|
381
327
|
},
|
|
382
|
-
[
|
|
328
|
+
[app, toolName, detachCurrent, clearPollTimer, scheduleNextPoll]
|
|
383
329
|
);
|
|
384
330
|
const cancel = react.useCallback(async () => {
|
|
385
331
|
const h = handleRef.current;
|
|
@@ -417,23 +363,18 @@ function useCallToolAsTask(toolName) {
|
|
|
417
363
|
}
|
|
418
364
|
|
|
419
365
|
exports.AppProvider = AppProvider;
|
|
420
|
-
exports.SynapseProvider = SynapseProvider;
|
|
421
366
|
exports.useAction = useAction;
|
|
422
|
-
exports.useAgentAction = useAgentAction;
|
|
423
367
|
exports.useApp = useApp;
|
|
424
368
|
exports.useCallTool = useCallTool;
|
|
425
369
|
exports.useCallToolAsTask = useCallToolAsTask;
|
|
426
|
-
exports.useChat = useChat;
|
|
427
|
-
exports.useConnectTheme = useConnectTheme;
|
|
428
370
|
exports.useDataSync = useDataSync;
|
|
429
371
|
exports.useFileUpload = useFileUpload;
|
|
430
372
|
exports.useHostContext = useHostContext;
|
|
373
|
+
exports.useModelContext = useModelContext;
|
|
431
374
|
exports.useResize = useResize;
|
|
432
|
-
exports.
|
|
433
|
-
exports.useSynapse = useSynapse;
|
|
375
|
+
exports.useSendMessage = useSendMessage;
|
|
434
376
|
exports.useTheme = useTheme;
|
|
435
377
|
exports.useToolInput = useToolInput;
|
|
436
378
|
exports.useToolResult = useToolResult;
|
|
437
|
-
exports.useVisibleState = useVisibleState;
|
|
438
379
|
//# sourceMappingURL=index.cjs.map
|
|
439
380
|
//# sourceMappingURL=index.cjs.map
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/app-provider.tsx","../../src/react/connect-hooks.ts","../../src/react/provider.tsx","../../src/react/hooks.ts"],"names":["createContext","useState","useRef","useEffect","connect","useContext","useCallback","createSynapse","jsx","applyTheme","useSyncExternalStore","isTerminal"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from \"react\";\nimport { connect } from \"../connect.js\";\nimport type { App, ConnectOptions } from \"../types.js\";\n\nconst AppContext = createContext<App | null>(null);\n\nexport interface AppProviderProps extends ConnectOptions {\n children: ReactNode;\n}\n\nexport function AppProvider({ children, name, version, autoResize }: AppProviderProps) {\n const [app, setApp] = useState<App | null>(null);\n const connectingRef = useRef(false);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: connect once on mount\n useEffect(() => {\n if (connectingRef.current) return;\n connectingRef.current = true;\n\n connect({ name, version, autoResize }).then((a) => {\n setApp(a);\n });\n }, []);\n\n if (!app) return null; // Don't render children until connected\n\n return <AppContext.Provider value={app}>{children}</AppContext.Provider>;\n}\n\nexport function useAppContext(): App {\n const ctx = useContext(AppContext);\n if (!ctx) {\n throw new Error(\n \"useApp must be used within an <AppProvider>. Wrap your component tree with <AppProvider>.\",\n );\n }\n return ctx;\n}\n","import { useCallback, useEffect, useState } from \"react\";\nimport type { App, Theme, ToolResultData } from \"../types.js\";\nimport { AppProvider, useAppContext } from \"./app-provider.js\";\n\nexport { AppProvider };\n\nexport function useApp(): App {\n return useAppContext();\n}\n\nexport function useToolResult(): ToolResultData | null {\n const app = useAppContext();\n const [data, setData] = useState<ToolResultData | null>(null);\n\n useEffect(() => {\n return app.on(\"tool-result\", (result: ToolResultData) => {\n setData(result);\n });\n }, [app]);\n\n return data;\n}\n\nexport function useToolInput(): Record<string, unknown> | null {\n const app = useAppContext();\n const [input, setInput] = useState<Record<string, unknown> | null>(null);\n\n useEffect(() => {\n return app.on(\"tool-input\", (args: Record<string, unknown>) => {\n setInput(args);\n });\n }, [app]);\n\n return input;\n}\n\nexport function useConnectTheme(): Theme {\n const app = useAppContext();\n const [theme, setTheme] = useState<Theme>(() => app.theme);\n\n useEffect(() => {\n setTheme(app.theme); // Sync in case it changed between render and effect\n return app.on(\"theme-changed\", (t: Theme) => {\n setTheme(t);\n });\n }, [app]);\n\n return theme;\n}\n\nexport function useResize(): (width?: number, height?: number) => void {\n const app = useAppContext();\n return useCallback((width?: number, height?: number) => app.resize(width, height), [app]);\n}\n","import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from \"react\";\nimport { createSynapse } from \"../core.js\";\nimport { applyTheme } from \"../theme-defaults.js\";\nimport type { Synapse, SynapseOptions, SynapseTheme } from \"../types.js\";\n\nconst SynapseContext = createContext<Synapse | null>(null);\n\nexport interface SynapseProviderProps extends SynapseOptions {\n children: ReactNode;\n}\n\nexport function SynapseProvider({ children, ...options }: SynapseProviderProps) {\n // Use a ref so the same Synapse instance survives StrictMode's\n // unmount/remount cycle. We intentionally do NOT destroy on unmount\n // because StrictMode re-mounts immediately and the transport must\n // stay alive. The instance is GC'd when the provider is truly removed.\n const ref = useRef<Synapse | null>(null);\n\n if (ref.current === null || ref.current.destroyed) {\n ref.current = createSynapse(options);\n }\n\n return (\n <SynapseContext.Provider value={ref.current}>\n <ThemeInjector synapse={ref.current} />\n {children}\n </SynapseContext.Provider>\n );\n}\n\n/** Injects theme CSS variables onto document.documentElement whenever the theme changes. */\nfunction ThemeInjector({ synapse }: { synapse: Synapse }) {\n const [theme, setTheme] = useState<SynapseTheme>(() => synapse.getTheme());\n\n useEffect(() => {\n setTheme(synapse.getTheme());\n return synapse.onThemeChanged(setTheme);\n }, [synapse]);\n\n useEffect(() => {\n // The host's values go inline; the neutral defaults for the mode go in a\n // cascade layer, where they back any var nobody declares and lose to every\n // var that is declared — the same single path the handshake uses in core.ts.\n applyTheme(theme.mode, theme.tokens, theme.fontFaces);\n }, [theme]);\n\n return null;\n}\n\nexport function useSynapseContext(): Synapse {\n const ctx = useContext(SynapseContext);\n if (!ctx) {\n throw new Error(\n \"useSynapse must be used within a <SynapseProvider>. \" +\n \"Wrap your app component tree with <SynapseProvider>.\",\n );\n }\n return ctx;\n}\n","import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport type { Task, TaskStatus } from \"@modelcontextprotocol/sdk/types.js\";\nimport { useCallback, useEffect, useRef, useState, useSyncExternalStore } from \"react\";\nimport type {\n ActionReducer,\n AgentAction,\n CallToolAsTaskOptions,\n DataChangedEvent,\n FileResult,\n RequestFileOptions,\n Store,\n StoreDispatch,\n Synapse,\n SynapseTheme,\n TaskHandle,\n ToolCallResult,\n} from \"../types.js\";\nimport { SynapseProvider, useSynapseContext } from \"./provider.js\";\n\n// Re-export provider components\nexport { SynapseProvider };\n\nexport function useSynapse(): Synapse {\n return useSynapseContext();\n}\n\nexport function useCallTool<TOutput = unknown>(\n toolName: string,\n): {\n call: (args?: Record<string, unknown>) => Promise<ToolCallResult<TOutput>>;\n isPending: boolean;\n error: Error | null;\n data: TOutput | null;\n} {\n const synapse = useSynapseContext();\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<TOutput | null>(null);\n const callIdRef = useRef(0);\n\n const call = useCallback(\n async (args?: Record<string, unknown>): Promise<ToolCallResult<TOutput>> => {\n const id = ++callIdRef.current;\n setIsPending(true);\n setError(null);\n\n try {\n const result = await synapse.callTool<Record<string, unknown>, TOutput>(toolName, args);\n // Stale guard: only update if this is still the latest call\n if (id === callIdRef.current) {\n setData(result.data);\n setIsPending(false);\n }\n return result;\n } catch (err) {\n if (id === callIdRef.current) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setIsPending(false);\n }\n throw err;\n }\n },\n [synapse, toolName],\n );\n\n return { call, isPending, error, data };\n}\n\nexport function useDataSync(callback: (event: DataChangedEvent) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onDataChanged((event) => callbackRef.current(event));\n }, [synapse]);\n}\n\n/**\n * Subscribe to agent actions — typed, declarative commands from the server.\n *\n * Actions are emitted by tools as deterministic side effects (e.g., \"navigate\n * to the board I just created\"). The UI decides how to handle each action type.\n *\n * @example\n * ```tsx\n * useAgentAction((action) => {\n * if (action.type === \"navigate\") {\n * const { entity, id } = action.payload as NavigatePayload;\n * if (entity === \"board\") setSelectedBoardId(id);\n * }\n * });\n * ```\n */\nexport function useAgentAction(callback: (action: AgentAction) => void): void {\n const synapse = useSynapseContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return synapse.onAction((action) => callbackRef.current(action));\n }, [synapse]);\n}\n\n/**\n * Subscribe to the full ext-apps host context.\n *\n * Returns the host context bag — spec-standardized fields (`theme`, `styles`,\n * `displayMode`, `toolInfo`) plus any host extensions. Re-renders on every\n * `ui/notifications/host-context-changed` notification.\n *\n * Prefer `useTheme()` when only theming matters (it filters no-op fires and\n * returns a typed `SynapseTheme`). Reach for `useHostContext()` for non-theme\n * fields like host-specific extensions, e.g. on NimbleBrain:\n *\n * ```tsx\n * const { workspace } = useHostContext<{ workspace?: { id: string } }>();\n * ```\n */\nexport function useHostContext<T extends McpUiHostContext = McpUiHostContext>(): T {\n const synapse = useSynapseContext();\n const [ctx, setCtx] = useState<T>(() => synapse.getHostContext() as T);\n\n useEffect(() => {\n // Sync in case context changed between render and effect\n setCtx(synapse.getHostContext() as T);\n return synapse.onHostContextChanged((c) => setCtx(c as T));\n }, [synapse]);\n\n return ctx;\n}\n\n/**\n * Re-renders only when the derived `SynapseTheme` actually changes (mode,\n * primaryColor, or any token value). Routes through `synapse.onThemeChanged`\n * — which is itself a `themesEqual`-filtered selector over the unified host\n * context — so a host-context update that doesn't move the theme (e.g. a\n * workspace switch) does NOT cause this hook's consumers to re-render.\n *\n * Building this on `useHostContext` instead would skip the filter: every\n * host-context-changed notification produces a new context reference, which\n * would propagate through `useState` and force a re-render even when the\n * derived theme is unchanged.\n */\nexport function useTheme(): SynapseTheme {\n const synapse = useSynapseContext();\n const [theme, setTheme] = useState<SynapseTheme>(() => synapse.getTheme());\n\n useEffect(() => {\n // Sync in case theme changed between render and effect\n setTheme(synapse.getTheme());\n return synapse.onThemeChanged(setTheme);\n }, [synapse]);\n\n return theme;\n}\n\nexport function useAction(): (action: string, params?: Record<string, unknown>) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (action: string, params?: Record<string, unknown>) => synapse.action(action, params),\n [synapse],\n );\n}\n\nexport function useChat(): (\n message: string,\n context?: { action?: string; entity?: string },\n) => void {\n const synapse = useSynapseContext();\n return useCallback(\n (message: string, context?: { action?: string; entity?: string }) =>\n synapse.chat(message, context),\n [synapse],\n );\n}\n\n/**\n * Push the app's visible state to the agent via ext-apps `ui/update-model-context`.\n *\n * **Imperative** (no args) — returns a push function you call manually:\n * ```tsx\n * const push = useVisibleState();\n * push({ board: selectedBoard }, \"Viewing board X\");\n * ```\n *\n * **Declarative** (factory + deps) — auto-pushes when deps change:\n * ```tsx\n * useVisibleState(() => ({\n * state: { board: selectedBoard },\n * summary: `Viewing \"${selectedBoard?.name}\"`,\n * }), [selectedBoard]);\n * ```\n */\nexport function useVisibleState(): (state: Record<string, unknown>, summary?: string) => void;\nexport function useVisibleState(\n factory: () => { state: Record<string, unknown>; summary?: string },\n deps: unknown[],\n): void;\nexport function useVisibleState(\n factory?: () => { state: Record<string, unknown>; summary?: string },\n deps?: unknown[],\n): ((state: Record<string, unknown>, summary?: string) => void) | undefined {\n const synapse = useSynapseContext();\n const push = useCallback(\n (state: Record<string, unknown>, summary?: string) => synapse.setVisibleState(state, summary),\n [synapse],\n );\n\n // Declarative mode: auto-push when deps change.\n // The deps array is caller-provided (mirrors useMemo/useEffect pattern).\n const factoryRef = useRef(factory);\n factoryRef.current = factory;\n useEffect(() => {\n if (!factoryRef.current) return;\n const { state, summary } = factoryRef.current();\n push(state, summary);\n }, [...(deps ?? []), push]);\n\n if (!factory) return push;\n}\n\nexport function useFileUpload(): {\n pickFile: (options?: RequestFileOptions) => Promise<FileResult | null>;\n pickFiles: (options?: RequestFileOptions) => Promise<FileResult[]>;\n isPending: boolean;\n} {\n const synapse = useSynapseContext();\n const [isPending, setIsPending] = useState(false);\n\n const pickFile = useCallback(\n async (options?: RequestFileOptions) => {\n setIsPending(true);\n try {\n return await synapse.pickFile(options);\n } finally {\n setIsPending(false);\n }\n },\n [synapse],\n );\n\n const pickFiles = useCallback(\n async (options?: RequestFileOptions) => {\n setIsPending(true);\n try {\n return await synapse.pickFiles(options);\n } finally {\n setIsPending(false);\n }\n },\n [synapse],\n );\n\n return { pickFile, pickFiles, isPending };\n}\n\nexport function useStore<TState, TActions extends Record<string, ActionReducer<TState, any>>>(\n store: Store<TState, TActions>,\n): {\n state: TState;\n dispatch: StoreDispatch<TActions>;\n} {\n const state = useSyncExternalStore(\n (onStoreChange) => store.subscribe(onStoreChange),\n () => store.getState(),\n () => store.getState(),\n );\n\n return { state, dispatch: store.dispatch };\n}\n\n// -----------------------------------------------------------------------------\n// useCallToolAsTask — lifecycle wrapper around `synapse.callToolAsTask`\n// -----------------------------------------------------------------------------\n\n/**\n * Spec terminal status values for the MCP 2025-11-25 tasks utility.\n *\n * Typed via `satisfies TaskStatus` so a rename of any member of the\n * spec enum (`completed`/`failed`/`cancelled`) trips `tsc` — we never\n * hand-type these as bare string literals in comparisons.\n */\nconst COMPLETED_STATUS = \"completed\" satisfies TaskStatus;\nconst FAILED_STATUS = \"failed\" satisfies TaskStatus;\nconst CANCELLED_STATUS = \"cancelled\" satisfies TaskStatus;\n\nconst TERMINAL_STATUSES: ReadonlySet<TaskStatus> = new Set<TaskStatus>([\n COMPLETED_STATUS,\n FAILED_STATUS,\n CANCELLED_STATUS,\n]);\n\n/**\n * Fallback poll cadence used when the receiver's `CreateTaskResult.task`\n * carries no `pollInterval`. Chosen to roughly match the 5s cadence\n * described in Task 005 (\"a sensible default like 5s if pollInterval is\n * absent\") — the effective fire delay is this value × 1.5 ≈ 7.5s, well\n * below default TTLs but long enough to avoid hammering hosts that do\n * emit `notifications/tasks/status`.\n */\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst POLL_FALLBACK_MULTIPLIER = 1.5;\n\n/**\n * Stop the poll fallback after this many consecutive `refresh()` failures.\n * Bridge teardown / TTL eviction / network outage all manifest as repeated\n * `tasks/get` rejections. Without a guard the timer re-arms forever; with\n * it we stop polling silently after `MAX_REFRESH_FAILURES` strikes — the\n * blocking `result()` path remains the authoritative source of truth, so\n * giving up on polling never loses the terminal value.\n */\nconst MAX_REFRESH_FAILURES = 5;\n\nexport interface UseCallToolAsTaskResult<TInput, TOutput> {\n /**\n * Start (or re-start) a task-augmented tool call. Returns the\n * resolved `TaskHandle` so callers can `await fire(...)` if they\n * want to know when the server has accepted the task, but reading\n * `task`/`result`/`error` from the hook is usually enough.\n *\n * Re-firing while a previous task is still in flight detaches this\n * hook from the prior handle (stops polling, unsubscribes) but does\n * NOT cancel the server-side task — the task keeps running and its\n * result may still be fetched elsewhere (e.g. on page revisit).\n */\n fire(args?: TInput, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>>;\n /** Latest `Task` state, or `null` before `fire()` has been called. */\n task: Task | null;\n /** Populated once `handle.result()` resolves non-error. */\n result: ToolCallResult<TOutput> | null;\n /** Populated on rejection or when `result.isError === true`. */\n error: Error | null;\n /** `true` while the task is non-terminal (`working` / `input_required`). */\n isWorking: boolean;\n /** `true` when `task.status ∈ {completed, failed, cancelled}`. */\n isTerminal: boolean;\n /**\n * Cancel the active task via `tasks/cancel`. No-op when no task is\n * active. Swallowed errors surface via `error`.\n */\n cancel(): Promise<void>;\n}\n\n/**\n * React hook wrapper around `synapse.callToolAsTask`.\n *\n * Handles the full MCP 2025-11-25 task lifecycle:\n *\n * 1. `fire(args, options?)` sends the task-augmented `tools/call` and\n * stores the returned `TaskHandle` in a ref.\n * 2. Subscribes to `handle.onStatus` — updates `task` whenever the\n * host emits `notifications/tasks/status` (OPTIONAL per spec).\n * 3. Starts a polling fallback: if no status notification arrives\n * within `pollInterval × 1.5` (defaulting to ~7.5s), calls\n * `handle.refresh()` for canonical state. Stops on terminal.\n * 4. Awaits `handle.result()` in the background — resolves to either\n * `result` (success / `isError: false`) or `error` (network reject\n * OR `result.isError === true`).\n *\n * Cleanup (unmount or re-fire) unsubscribes from status events and\n * clears the poll timer, but does NOT cancel the server-side task —\n * the caller may remount and recover state by firing again, and tasks\n * outlive iframe teardown until TTL elapses.\n */\nexport function useCallToolAsTask<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n): UseCallToolAsTaskResult<TInput, TOutput> {\n const synapse = useSynapseContext();\n\n const [task, setTask] = useState<Task | null>(null);\n const [result, setResult] = useState<ToolCallResult<TOutput> | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n // Per-fire generation counter. Every `fire()` increments; any\n // asynchronous callback (status listener, poll timer, `result()`\n // resolution) captures the gen at schedule time and bails if the\n // current gen has moved past it. This is the single source of truth\n // for \"is this work still relevant?\" — more robust than comparing\n // TaskHandle identity because handles can be detached by re-fire.\n const genRef = useRef(0);\n\n // Active handle + its teardown handles. We keep both in refs so the\n // hook's stable `fire`/`cancel` callbacks can reach the current\n // lifecycle state without re-binding on every render.\n const handleRef = useRef<TaskHandle<TOutput> | null>(null);\n const unsubscribeRef = useRef<(() => void) | null>(null);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // `pollInterval × 1.5`, captured per-fire. Falls back to the 5s\n // default when the host didn't provide a `pollInterval` in the\n // initial CreateTaskResult.task.\n const pollDelayRef = useRef<number>(DEFAULT_POLL_INTERVAL_MS * POLL_FALLBACK_MULTIPLIER);\n\n // Track the latest known status out-of-band so the poll callback\n // can decide whether to keep polling without depending on the\n // `task` React state (which lags a render behind setState).\n const terminalRef = useRef<boolean>(false);\n\n // Consecutive `refresh()` failure count for the active fire. Reset\n // on every successful refresh, status notification, or new fire.\n const refreshFailureCountRef = useRef<number>(0);\n\n const clearPollTimer = useCallback(() => {\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n }, []);\n\n const detachCurrent = useCallback(() => {\n clearPollTimer();\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n handleRef.current = null;\n }, [clearPollTimer]);\n\n const scheduleNextPoll = useCallback(\n (gen: number) => {\n clearPollTimer();\n if (terminalRef.current) return;\n pollTimerRef.current = setTimeout(() => {\n // Bail if this fire has been superseded or torn down.\n if (gen !== genRef.current) return;\n const h = handleRef.current;\n if (!h) return;\n if (terminalRef.current) return;\n // `refresh()` is the canonical source for `createdAt` /\n // `lastUpdatedAt` / `ttl` — notification-derived Tasks carry\n // placeholders per the router in `task-handle.ts`.\n h.refresh().then(\n (fresh) => {\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current = 0;\n setTask(fresh);\n const isTerminal = TERMINAL_STATUSES.has(fresh.status);\n terminalRef.current = isTerminal;\n if (!isTerminal) scheduleNextPoll(gen);\n },\n () => {\n // Swallow refresh errors — the blocking `tasks/result` is\n // the authoritative path; polling is best-effort. But guard\n // against runaway re-arming: if the bridge is gone or the\n // task TTL has elapsed, every refresh rejects. Stop after\n // MAX_REFRESH_FAILURES consecutive strikes; result() will\n // still surface a terminal value or error when it settles.\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current += 1;\n if (refreshFailureCountRef.current >= MAX_REFRESH_FAILURES) return;\n if (!terminalRef.current) scheduleNextPoll(gen);\n },\n );\n }, pollDelayRef.current);\n },\n [clearPollTimer],\n );\n\n const fire = useCallback(\n async (args?: TInput, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>> => {\n // Detach any in-flight prior task BEFORE incrementing the gen so\n // its callbacks see the new gen and bail. (Incrementing then\n // detaching would also work, but detach-first makes the order\n // obvious: stop listening, bump generation, start fresh.)\n detachCurrent();\n const gen = ++genRef.current;\n\n // Reset per-fire state. Don't wipe `task` yet — `callToolAsTask`\n // is async; showing the previous terminal state briefly is less\n // jarring than flicker to null → working. We clear on resolution.\n setResult(null);\n setError(null);\n terminalRef.current = false;\n refreshFailureCountRef.current = 0;\n\n let handle: TaskHandle<TOutput>;\n try {\n handle = await synapse.callToolAsTask<TInput, TOutput>(toolName, args, options);\n } catch (err) {\n if (gen !== genRef.current) throw err;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw err;\n }\n\n // Caller superseded the fire between request and response —\n // don't attach listeners, but still return the handle so the\n // awaiter can observe it.\n if (gen !== genRef.current) return handle;\n\n handleRef.current = handle;\n\n // Derive the fallback poll delay from the receiver's advertised\n // `pollInterval`. Spec allows it to be absent; we then use the\n // 5s default described in Task 005.\n const hintedInterval = handle.task.pollInterval;\n pollDelayRef.current =\n typeof hintedInterval === \"number\" && hintedInterval > 0\n ? hintedInterval * POLL_FALLBACK_MULTIPLIER\n : DEFAULT_POLL_INTERVAL_MS * POLL_FALLBACK_MULTIPLIER;\n\n setTask(handle.task);\n terminalRef.current = TERMINAL_STATUSES.has(handle.task.status);\n\n // Subscribe to `notifications/tasks/status`. Each notification\n // resets the poll countdown (that's the whole point of the\n // \"notification OR polling\" contract — if notifications flow,\n // we don't poll; if they don't, the timer fires).\n unsubscribeRef.current = handle.onStatus((updated) => {\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current = 0;\n setTask(updated);\n const isTerminal = TERMINAL_STATUSES.has(updated.status);\n terminalRef.current = isTerminal;\n if (isTerminal) {\n clearPollTimer();\n } else {\n scheduleNextPoll(gen);\n }\n });\n\n // Kick off the blocking result fetch — this is the authoritative\n // terminal value regardless of whether notifications or polls\n // landed in between. By spec, `tasks/result` blocks until the task\n // reaches a terminal status, so when this settles we KNOW the task\n // is terminal — stop polling and synthesize a terminal `task`\n // status so derived flags (`isTerminal`, `isWorking`) match the\n // populated `result` / `error` immediately.\n handle.result().then(\n (res) => {\n if (gen !== genRef.current) return;\n terminalRef.current = true;\n clearPollTimer();\n // Synthesize the terminal Task: failed if `isError`, otherwise\n // completed. The next status notification or refresh would\n // confirm this, but we want internal state consistent the\n // instant `result` is populated — a \"result populated while\n // isWorking=true\" render is incoherent for consumers.\n setTask((prev) => {\n const status: TaskStatus = res.isError ? \"failed\" : \"completed\";\n const now = new Date().toISOString();\n return prev\n ? { ...prev, status, lastUpdatedAt: now }\n : {\n taskId: handle.task.taskId,\n status,\n ttl: handle.task.ttl,\n createdAt: handle.task.createdAt,\n lastUpdatedAt: now,\n };\n });\n if (res.isError) {\n // Spec: `CallToolResult.isError === true` is a tool-level\n // error, not a protocol error. Surface via `error` for\n // consumers who treat it as a failure, but also populate\n // `result` so callers inspecting the raw content block\n // still have access.\n setResult(res);\n const msg =\n typeof res.data === \"string\" && res.data.length > 0\n ? res.data\n : `Tool \"${toolName}\" returned isError: true`;\n setError(new Error(msg));\n } else {\n setResult(res);\n }\n },\n (err) => {\n if (gen !== genRef.current) return;\n // result() rejection means the `tasks/result` RPC failed\n // (transport error, taskId not found, bridge teardown). We\n // can't know the server-side task's actual final state, but\n // we know polling won't recover here either — same transport.\n // Mark terminal and synthesize `failed` status for UX\n // coherence; the populated `error` tells the consumer what\n // specifically went wrong.\n terminalRef.current = true;\n clearPollTimer();\n setTask((prev) => {\n const now = new Date().toISOString();\n return prev\n ? { ...prev, status: \"failed\", lastUpdatedAt: now }\n : {\n taskId: handle.task.taskId,\n status: \"failed\",\n ttl: handle.task.ttl,\n createdAt: handle.task.createdAt,\n lastUpdatedAt: now,\n };\n });\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n },\n );\n\n // Start the poll fallback only if we aren't already terminal.\n if (!terminalRef.current) scheduleNextPoll(gen);\n\n return handle;\n },\n [synapse, toolName, detachCurrent, clearPollTimer, scheduleNextPoll],\n );\n\n const cancel = useCallback(async (): Promise<void> => {\n const h = handleRef.current;\n if (!h) return;\n const gen = genRef.current;\n try {\n const cancelled = await h.cancel();\n if (gen !== genRef.current) return;\n setTask(cancelled);\n terminalRef.current = TERMINAL_STATUSES.has(cancelled.status);\n clearPollTimer();\n } catch (err) {\n if (gen !== genRef.current) return;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n }\n }, [clearPollTimer]);\n\n // Cleanup on unmount: stop polling, drop the status subscription.\n // Deliberately do NOT call `handle.cancel()` — per Task 005, the\n // server-side task keeps running so a remount can recover state.\n useEffect(() => {\n return () => {\n genRef.current += 1;\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n handleRef.current = null;\n };\n }, []);\n\n const isTerminal = task !== null && TERMINAL_STATUSES.has(task.status);\n const isWorking = task !== null && !isTerminal;\n\n return { fire, task, result, error, isWorking, isTerminal, cancel };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/app-provider.tsx","../../src/react/hooks.ts"],"names":["createContext","useState","useRef","useEffect","connect","useContext","useCallback","action","pickFile","pickFiles","isTerminal","callToolAsTask"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from \"react\";\nimport { connect } from \"../connect.js\";\nimport type { App, ConnectOptions } from \"../types.js\";\n\nconst AppContext = createContext<App | null>(null);\n\nexport interface AppProviderProps extends ConnectOptions {\n children: ReactNode;\n}\n\n/**\n * Connect on mount and provide the {@link App} to everything below.\n *\n * Renders nothing until the handshake completes, so no hook below can observe\n * a half-connected app and no tool call can be fired into a host that has not\n * answered `ui/initialize` yet.\n */\nexport function AppProvider({ children, ...options }: AppProviderProps) {\n const [app, setApp] = useState<App | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const connectingRef = useRef(false);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: connect once on mount\n useEffect(() => {\n // A ref, not cleanup-on-unmount: StrictMode unmounts and immediately\n // remounts, and the transport must survive that. The app is GC'd when the\n // provider is truly gone.\n if (connectingRef.current) return;\n connectingRef.current = true;\n\n connect(options).then(setApp, (err: unknown) => {\n setError(err instanceof Error ? err : new Error(String(err)));\n });\n }, []);\n\n // A host that answers `ui/initialize` with an error is a failure the app\n // cannot proceed through, and rendering nothing forever is the worst way to\n // report it: a blank pane and an unhandled rejection in a console nobody has\n // open. Re-throwing during render makes it an ordinary React error, so it\n // reaches the nearest error boundary and is loud in development.\n if (error) throw error;\n\n // Still connecting. Nothing below can observe a half-connected app, and no\n // hook can fire a call into a host that has not answered yet.\n if (!app) return null;\n\n return <AppContext.Provider value={app}>{children}</AppContext.Provider>;\n}\n\nexport function useAppContext(): App {\n const ctx = useContext(AppContext);\n if (!ctx) {\n throw new Error(\n \"useApp must be used within an <AppProvider>. Wrap your component tree with <AppProvider>.\",\n );\n }\n return ctx;\n}\n","import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport type { Task, TaskStatus } from \"@modelcontextprotocol/sdk/types.js\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { pickFile, pickFiles, action as sendAction } from \"../extensions.js\";\nimport { callToolAsTask } from \"../task-handle.js\";\nimport type {\n App,\n CallToolAsTaskOptions,\n CallToolOptions,\n DataChangedEvent,\n FileResult,\n ModelContext,\n RequestFileOptions,\n TaskHandle,\n Theme,\n ToolCallResult,\n ToolResultData,\n} from \"../types.js\";\nimport { useAppContext } from \"./app-provider.js\";\n\n/** The connected app. Throws outside an `<AppProvider>`. */\nexport function useApp(): App {\n return useAppContext();\n}\n\n// -----------------------------------------------------------------------------\n// Host state\n// -----------------------------------------------------------------------------\n\n/**\n * The current theme, re-rendering only when it actually moves.\n *\n * `App` filters `host-context-changed` notifications through a theme equality\n * check, so a context update that leaves the derived theme untouched (a\n * workspace switch, say) does not re-render every themed component.\n */\nexport function useTheme(): Theme {\n const app = useAppContext();\n const [theme, setTheme] = useState<Theme>(() => app.theme);\n\n useEffect(() => {\n // Sync in case the theme changed between render and effect.\n setTheme(app.theme);\n return app.on(\"theme-changed\", setTheme);\n }, [app]);\n\n return theme;\n}\n\n/**\n * The full ext-apps host context — spec fields (`theme`, `styles`,\n * `displayMode`, `toolInfo`) plus whatever the host publishes alongside.\n * Re-renders on every `host-context-changed`.\n *\n * Prefer `useTheme()` when only theming matters: it filters no-op fires.\n * Reach for this one for host extensions, e.g. on NimbleBrain:\n *\n * ```tsx\n * const { workspace } = useHostContext<{ workspace?: { id: string } }>();\n * ```\n */\nexport function useHostContext<T extends McpUiHostContext = McpUiHostContext>(): T {\n const app = useAppContext();\n const [ctx, setCtx] = useState<T>(() => app.hostContext as T);\n\n useEffect(() => {\n setCtx(app.hostContext as T);\n return app.on(\"host-context-changed\", (c) => setCtx(c as T));\n }, [app]);\n\n return ctx;\n}\n\n/** The latest tool result pushed by the host, or `null` before one arrives. */\nexport function useToolResult(): ToolResultData | null {\n const app = useAppContext();\n const [data, setData] = useState<ToolResultData | null>(null);\n\n useEffect(() => {\n return app.on(\"tool-result\", setData);\n }, [app]);\n\n return data;\n}\n\n/** The arguments the host is calling the bound tool with, as they arrive. */\nexport function useToolInput(): Record<string, unknown> | null {\n const app = useAppContext();\n const [input, setInput] = useState<Record<string, unknown> | null>(null);\n\n useEffect(() => {\n return app.on(\"tool-input\", setInput);\n }, [app]);\n\n return input;\n}\n\n/** Ask the host to resize this app's frame. */\nexport function useResize(): (width?: number, height?: number) => void {\n const app = useAppContext();\n return useCallback((width?: number, height?: number) => app.resize(width, height), [app]);\n}\n\n// -----------------------------------------------------------------------------\n// Tools\n// -----------------------------------------------------------------------------\n\nexport interface UseCallToolResult<TOutput> {\n call: (\n args?: Record<string, unknown>,\n options?: CallToolOptions,\n ) => Promise<ToolCallResult<TOutput>>;\n isPending: boolean;\n error: Error | null;\n data: TOutput | null;\n}\n\n/** Call one tool, with pending/error/data state for the latest call. */\nexport function useCallTool<TOutput = unknown>(toolName: string): UseCallToolResult<TOutput> {\n const app = useAppContext();\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [data, setData] = useState<TOutput | null>(null);\n const callIdRef = useRef(0);\n\n const call = useCallback(\n async (\n args?: Record<string, unknown>,\n options?: CallToolOptions,\n ): Promise<ToolCallResult<TOutput>> => {\n const id = ++callIdRef.current;\n setIsPending(true);\n setError(null);\n\n try {\n const result = await app.callTool<TOutput>(toolName, args, options);\n // Stale guard: only update if this is still the latest call.\n if (id === callIdRef.current) {\n setData(result.data);\n setIsPending(false);\n }\n return result;\n } catch (err) {\n if (id === callIdRef.current) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n setIsPending(false);\n }\n throw err;\n }\n },\n [app, toolName],\n );\n\n return { call, isPending, error, data };\n}\n\n/** Run `callback` whenever the agent changes data this app displays. */\nexport function useDataSync(callback: (event: DataChangedEvent) => void): void {\n const app = useAppContext();\n const callbackRef = useRef(callback);\n callbackRef.current = callback;\n\n useEffect(() => {\n return app.on(\"data-changed\", (event) => callbackRef.current(event));\n }, [app]);\n}\n\n// -----------------------------------------------------------------------------\n// Agent-facing state\n// -----------------------------------------------------------------------------\n\n/** Debounce window for `useModelContext`, in milliseconds. */\nconst MODEL_CONTEXT_DEBOUNCE_MS = 250;\n\n/**\n * Push what the user is looking at to the agent (ext-apps\n * `ui/update-model-context`), debounced so a selection the user drags through\n * costs one frame rather than thirty.\n *\n * **Declarative** — pushes when `deps` change:\n * ```tsx\n * useModelContext(() => ({\n * state: { board: selectedBoard },\n * summary: `Viewing \"${selectedBoard?.name}\"`,\n * }), [selectedBoard]);\n * ```\n *\n * **Imperative** — returns a push function:\n * ```tsx\n * const push = useModelContext();\n * push({ board: selectedBoard }, \"Viewing board X\");\n * ```\n *\n * The debounce lives here rather than on `app.updateModelContext`, which\n * sends immediately: the rapid-change problem is a React one, and the plain\n * method should do what it says.\n */\nexport function useModelContext(): (state: Record<string, unknown>, summary?: string) => void;\nexport function useModelContext(factory: () => ModelContext, deps: unknown[]): void;\nexport function useModelContext(\n factory?: () => ModelContext,\n deps?: unknown[],\n): ((state: Record<string, unknown>, summary?: string) => void) | undefined {\n const app = useAppContext();\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const push = useCallback(\n (state: Record<string, unknown>, summary?: string) => {\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n app.updateModelContext(state, summary);\n }, MODEL_CONTEXT_DEBOUNCE_MS);\n },\n [app],\n );\n\n useEffect(() => {\n return () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n };\n }, []);\n\n // Declarative mode: push when deps change. The deps array is\n // caller-provided (mirrors useMemo/useEffect).\n const factoryRef = useRef(factory);\n factoryRef.current = factory;\n useEffect(() => {\n if (!factoryRef.current) return;\n const { state, summary } = factoryRef.current();\n push(state, summary);\n }, [...(deps ?? []), push]);\n\n if (!factory) return push;\n}\n\n/** Send a user message into the agent conversation. */\nexport function useSendMessage(): (\n text: string,\n context?: { action?: string; entity?: string },\n) => void {\n const app = useAppContext();\n return useCallback(\n (text: string, context?: { action?: string; entity?: string }) =>\n app.sendMessage(text, context),\n [app],\n );\n}\n\n// -----------------------------------------------------------------------------\n// NimbleBrain host extensions\n// -----------------------------------------------------------------------------\n\n/** Trigger a host-side action. No-op off a NimbleBrain host. */\nexport function useAction(): (name: string, params?: Record<string, unknown>) => void {\n const app = useAppContext();\n return useCallback(\n (name: string, params?: Record<string, unknown>) => sendAction(app, name, params),\n [app],\n );\n}\n\nexport interface UseFileUploadResult {\n pickFile: (options?: RequestFileOptions) => Promise<FileResult | null>;\n pickFiles: (options?: RequestFileOptions) => Promise<FileResult[]>;\n isPending: boolean;\n}\n\n/** The host's native file picker, with a pending flag. NimbleBrain only. */\nexport function useFileUpload(): UseFileUploadResult {\n const app = useAppContext();\n const [isPending, setIsPending] = useState(false);\n\n const one = useCallback(\n async (options?: RequestFileOptions) => {\n setIsPending(true);\n try {\n return await pickFile(app, options);\n } finally {\n setIsPending(false);\n }\n },\n [app],\n );\n\n const many = useCallback(\n async (options?: RequestFileOptions) => {\n setIsPending(true);\n try {\n return await pickFiles(app, options);\n } finally {\n setIsPending(false);\n }\n },\n [app],\n );\n\n return { pickFile: one, pickFiles: many, isPending };\n}\n\n// -----------------------------------------------------------------------------\n// useCallToolAsTask — lifecycle wrapper around `callToolAsTask(app, …)`\n// -----------------------------------------------------------------------------\n\n/**\n * Spec terminal status values for the MCP 2025-11-25 tasks utility.\n *\n * Typed via `satisfies TaskStatus` so a rename of any member of the\n * spec enum (`completed`/`failed`/`cancelled`) trips `tsc` — we never\n * hand-type these as bare string literals in comparisons.\n */\nconst COMPLETED_STATUS = \"completed\" satisfies TaskStatus;\nconst FAILED_STATUS = \"failed\" satisfies TaskStatus;\nconst CANCELLED_STATUS = \"cancelled\" satisfies TaskStatus;\n\nconst TERMINAL_STATUSES: ReadonlySet<TaskStatus> = new Set<TaskStatus>([\n COMPLETED_STATUS,\n FAILED_STATUS,\n CANCELLED_STATUS,\n]);\n\n/**\n * Fallback poll cadence used when the receiver's `CreateTaskResult.task`\n * carries no `pollInterval`. The effective fire delay is this value × 1.5\n * ≈ 7.5s, well below default TTLs but long enough to avoid hammering hosts\n * that do emit `notifications/tasks/status`.\n */\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst POLL_FALLBACK_MULTIPLIER = 1.5;\n\n/**\n * Stop the poll fallback after this many consecutive `refresh()` failures.\n * Bridge teardown / TTL eviction / network outage all manifest as repeated\n * `tasks/get` rejections. Without a guard the timer re-arms forever; with\n * it we stop polling silently after `MAX_REFRESH_FAILURES` strikes — the\n * blocking `result()` path remains the authoritative source of truth, so\n * giving up on polling never loses the terminal value.\n */\nconst MAX_REFRESH_FAILURES = 5;\n\nexport interface UseCallToolAsTaskResult<TInput, TOutput> {\n /**\n * Start (or re-start) a task-augmented tool call. Returns the\n * resolved `TaskHandle` so callers can `await fire(...)` if they\n * want to know when the server has accepted the task, but reading\n * `task`/`result`/`error` from the hook is usually enough.\n *\n * Re-firing while a previous task is still in flight detaches this\n * hook from the prior handle (stops polling, unsubscribes) but does\n * NOT cancel the server-side task — the task keeps running and its\n * result may still be fetched elsewhere (e.g. on page revisit).\n */\n fire(args?: TInput, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>>;\n /** Latest `Task` state, or `null` before `fire()` has been called. */\n task: Task | null;\n /** Populated once `handle.result()` resolves non-error. */\n result: ToolCallResult<TOutput> | null;\n /** Populated on rejection or when `result.isError === true`. */\n error: Error | null;\n /** `true` while the task is non-terminal (`working` / `input_required`). */\n isWorking: boolean;\n /** `true` when `task.status ∈ {completed, failed, cancelled}`. */\n isTerminal: boolean;\n /**\n * Cancel the active task via `tasks/cancel`. No-op when no task is\n * active. Swallowed errors surface via `error`.\n */\n cancel(): Promise<void>;\n}\n\n/**\n * React wrapper around `callToolAsTask(app, …)`.\n *\n * Handles the full MCP 2025-11-25 task lifecycle:\n *\n * 1. `fire(args, options?)` sends the task-augmented `tools/call` and\n * stores the returned `TaskHandle` in a ref.\n * 2. Subscribes to `handle.onStatus` — updates `task` whenever the\n * host emits `notifications/tasks/status` (OPTIONAL per spec).\n * 3. Starts a polling fallback: if no status notification arrives\n * within `pollInterval × 1.5` (defaulting to ~7.5s), calls\n * `handle.refresh()` for canonical state. Stops on terminal.\n * 4. Awaits `handle.result()` in the background — resolves to either\n * `result` (success / `isError: false`) or `error` (network reject\n * OR `result.isError === true`).\n *\n * Cleanup (unmount or re-fire) unsubscribes from status events and\n * clears the poll timer, but does NOT cancel the server-side task —\n * the caller may remount and recover state by firing again, and tasks\n * outlive iframe teardown until TTL elapses.\n */\nexport function useCallToolAsTask<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n): UseCallToolAsTaskResult<TInput, TOutput> {\n const app = useAppContext();\n\n const [task, setTask] = useState<Task | null>(null);\n const [result, setResult] = useState<ToolCallResult<TOutput> | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n // Per-fire generation counter. Every `fire()` increments; any\n // asynchronous callback (status listener, poll timer, `result()`\n // resolution) captures the gen at schedule time and bails if the\n // current gen has moved past it. This is the single source of truth\n // for \"is this work still relevant?\" — more robust than comparing\n // TaskHandle identity because handles can be detached by re-fire.\n const genRef = useRef(0);\n\n // Active handle + its teardown handles. We keep both in refs so the\n // hook's stable `fire`/`cancel` callbacks can reach the current\n // lifecycle state without re-binding on every render.\n const handleRef = useRef<TaskHandle<TOutput> | null>(null);\n const unsubscribeRef = useRef<(() => void) | null>(null);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // `pollInterval × 1.5`, captured per-fire. Falls back to the 5s\n // default when the host didn't provide a `pollInterval` in the\n // initial CreateTaskResult.task.\n const pollDelayRef = useRef<number>(DEFAULT_POLL_INTERVAL_MS * POLL_FALLBACK_MULTIPLIER);\n\n // Track the latest known status out-of-band so the poll callback\n // can decide whether to keep polling without depending on the\n // `task` React state (which lags a render behind setState).\n const terminalRef = useRef<boolean>(false);\n\n // Consecutive `refresh()` failure count for the active fire. Reset\n // on every successful refresh, status notification, or new fire.\n const refreshFailureCountRef = useRef<number>(0);\n\n const clearPollTimer = useCallback(() => {\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n }, []);\n\n const detachCurrent = useCallback(() => {\n clearPollTimer();\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n handleRef.current = null;\n }, [clearPollTimer]);\n\n const scheduleNextPoll = useCallback(\n (gen: number) => {\n clearPollTimer();\n if (terminalRef.current) return;\n pollTimerRef.current = setTimeout(() => {\n // Bail if this fire has been superseded or torn down.\n if (gen !== genRef.current) return;\n const h = handleRef.current;\n if (!h) return;\n if (terminalRef.current) return;\n // `refresh()` is the canonical source for `createdAt` /\n // `lastUpdatedAt` / `ttl` — notification-derived Tasks carry\n // placeholders per the router in `task-handle.ts`.\n h.refresh().then(\n (fresh) => {\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current = 0;\n setTask(fresh);\n const isTerminal = TERMINAL_STATUSES.has(fresh.status);\n terminalRef.current = isTerminal;\n if (!isTerminal) scheduleNextPoll(gen);\n },\n () => {\n // Swallow refresh errors — the blocking `tasks/result` is\n // the authoritative path; polling is best-effort. But guard\n // against runaway re-arming: if the bridge is gone or the\n // task TTL has elapsed, every refresh rejects. Stop after\n // MAX_REFRESH_FAILURES consecutive strikes; result() will\n // still surface a terminal value or error when it settles.\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current += 1;\n if (refreshFailureCountRef.current >= MAX_REFRESH_FAILURES) return;\n if (!terminalRef.current) scheduleNextPoll(gen);\n },\n );\n }, pollDelayRef.current);\n },\n [clearPollTimer],\n );\n\n const fire = useCallback(\n async (args?: TInput, options?: CallToolAsTaskOptions): Promise<TaskHandle<TOutput>> => {\n // Detach any in-flight prior task BEFORE incrementing the gen so\n // its callbacks see the new gen and bail. (Incrementing then\n // detaching would also work, but detach-first makes the order\n // obvious: stop listening, bump generation, start fresh.)\n detachCurrent();\n const gen = ++genRef.current;\n\n // Reset per-fire state. Don't wipe `task` yet — `callToolAsTask`\n // is async; showing the previous terminal state briefly is less\n // jarring than flicker to null → working. We clear on resolution.\n setResult(null);\n setError(null);\n terminalRef.current = false;\n refreshFailureCountRef.current = 0;\n\n let handle: TaskHandle<TOutput>;\n try {\n handle = await callToolAsTask<TOutput>(app, toolName, args, options);\n } catch (err) {\n if (gen !== genRef.current) throw err;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n throw err;\n }\n\n // Caller superseded the fire between request and response —\n // don't attach listeners, but still return the handle so the\n // awaiter can observe it.\n if (gen !== genRef.current) return handle;\n\n handleRef.current = handle;\n\n // Derive the fallback poll delay from the receiver's advertised\n // `pollInterval`. Spec allows it to be absent; we then use the\n // 5s default.\n const hintedInterval = handle.task.pollInterval;\n pollDelayRef.current =\n typeof hintedInterval === \"number\" && hintedInterval > 0\n ? hintedInterval * POLL_FALLBACK_MULTIPLIER\n : DEFAULT_POLL_INTERVAL_MS * POLL_FALLBACK_MULTIPLIER;\n\n setTask(handle.task);\n terminalRef.current = TERMINAL_STATUSES.has(handle.task.status);\n\n // Subscribe to `notifications/tasks/status`. Each notification\n // resets the poll countdown (that's the whole point of the\n // \"notification OR polling\" contract — if notifications flow,\n // we don't poll; if they don't, the timer fires).\n unsubscribeRef.current = handle.onStatus((updated) => {\n if (gen !== genRef.current) return;\n refreshFailureCountRef.current = 0;\n setTask(updated);\n const isTerminal = TERMINAL_STATUSES.has(updated.status);\n terminalRef.current = isTerminal;\n if (isTerminal) {\n clearPollTimer();\n } else {\n scheduleNextPoll(gen);\n }\n });\n\n // Kick off the blocking result fetch — this is the authoritative\n // terminal value regardless of whether notifications or polls\n // landed in between. By spec, `tasks/result` blocks until the task\n // reaches a terminal status, so when this settles we KNOW the task\n // is terminal — stop polling and synthesize a terminal `task`\n // status so derived flags (`isTerminal`, `isWorking`) match the\n // populated `result` / `error` immediately.\n handle.result().then(\n (res) => {\n if (gen !== genRef.current) return;\n terminalRef.current = true;\n clearPollTimer();\n // Synthesize the terminal Task: failed if `isError`, otherwise\n // completed. The next status notification or refresh would\n // confirm this, but we want internal state consistent the\n // instant `result` is populated — a \"result populated while\n // isWorking=true\" render is incoherent for consumers.\n setTask((prev) => {\n const status: TaskStatus = res.isError ? FAILED_STATUS : COMPLETED_STATUS;\n const now = new Date().toISOString();\n return prev\n ? { ...prev, status, lastUpdatedAt: now }\n : {\n taskId: handle.task.taskId,\n status,\n ttl: handle.task.ttl,\n createdAt: handle.task.createdAt,\n lastUpdatedAt: now,\n };\n });\n if (res.isError) {\n // Spec: `CallToolResult.isError === true` is a tool-level\n // error, not a protocol error. Surface via `error` for\n // consumers who treat it as a failure, but also populate\n // `result` so callers inspecting the raw content block\n // still have access.\n setResult(res);\n const msg =\n typeof res.data === \"string\" && res.data.length > 0\n ? res.data\n : `Tool \"${toolName}\" returned isError: true`;\n setError(new Error(msg));\n } else {\n setResult(res);\n }\n },\n (err) => {\n if (gen !== genRef.current) return;\n // result() rejection means the `tasks/result` RPC failed\n // (transport error, taskId not found, bridge teardown). We\n // can't know the server-side task's actual final state, but\n // we know polling won't recover here either — same transport.\n // Mark terminal and synthesize `failed` status for UX\n // coherence; the populated `error` tells the consumer what\n // specifically went wrong.\n terminalRef.current = true;\n clearPollTimer();\n setTask((prev) => {\n const now = new Date().toISOString();\n return prev\n ? { ...prev, status: FAILED_STATUS, lastUpdatedAt: now }\n : {\n taskId: handle.task.taskId,\n status: FAILED_STATUS,\n ttl: handle.task.ttl,\n createdAt: handle.task.createdAt,\n lastUpdatedAt: now,\n };\n });\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n },\n );\n\n // Start the poll fallback only if we aren't already terminal.\n if (!terminalRef.current) scheduleNextPoll(gen);\n\n return handle;\n },\n [app, toolName, detachCurrent, clearPollTimer, scheduleNextPoll],\n );\n\n const cancel = useCallback(async (): Promise<void> => {\n const h = handleRef.current;\n if (!h) return;\n const gen = genRef.current;\n try {\n const cancelled = await h.cancel();\n if (gen !== genRef.current) return;\n setTask(cancelled);\n terminalRef.current = TERMINAL_STATUSES.has(cancelled.status);\n clearPollTimer();\n } catch (err) {\n if (gen !== genRef.current) return;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n }\n }, [clearPollTimer]);\n\n // Cleanup on unmount: stop polling, drop the status subscription.\n // Deliberately do NOT call `handle.cancel()` — the server-side task\n // keeps running so a remount can recover state.\n useEffect(() => {\n return () => {\n genRef.current += 1;\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n if (unsubscribeRef.current) {\n unsubscribeRef.current();\n unsubscribeRef.current = null;\n }\n handleRef.current = null;\n };\n }, []);\n\n const isTerminal = task !== null && TERMINAL_STATUSES.has(task.status);\n const isWorking = task !== null && !isTerminal;\n\n return { fire, task, result, error, isWorking, isTerminal, cancel };\n}\n"]}
|