@nimblebrain/synapse 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +61 -111
  2. package/dist/{chunk-SN7YXLAV.cjs → chunk-EHLQWCYV.cjs} +6 -6
  3. package/dist/{chunk-SN7YXLAV.cjs.map → chunk-EHLQWCYV.cjs.map} +1 -1
  4. package/dist/{chunk-ZMNYUGSF.cjs → chunk-FDMRJZKT.cjs} +576 -662
  5. package/dist/chunk-FDMRJZKT.cjs.map +1 -0
  6. package/dist/{chunk-E7OOOYVR.js → chunk-SRQNQ26I.js} +3 -3
  7. package/dist/{chunk-E7OOOYVR.js.map → chunk-SRQNQ26I.js.map} +1 -1
  8. package/dist/{chunk-RYWW6EHB.cjs → chunk-TXJMGQYF.cjs} +3 -5
  9. package/dist/chunk-TXJMGQYF.cjs.map +1 -0
  10. package/dist/{chunk-J5ICAHK2.js → chunk-UPDBDXQB.js} +4 -5
  11. package/dist/chunk-UPDBDXQB.js.map +1 -0
  12. package/dist/{chunk-GNTQJSQX.js → chunk-VYR6V47N.js} +573 -663
  13. package/dist/chunk-VYR6V47N.js.map +1 -0
  14. package/dist/codegen/cli.cjs +1 -1
  15. package/dist/codegen/cli.js +1 -1
  16. package/dist/codegen/index.d.cts +1 -1
  17. package/dist/codegen/index.d.ts +1 -1
  18. package/dist/connect.iife.global.js +28 -28
  19. package/dist/{detect-C_qmYsh9.d.cts → detect-BHYg26_d.d.cts} +1 -1
  20. package/dist/{detect-Dfd0upoz.d.ts → detect-Bf8Q_0dK.d.ts} +1 -1
  21. package/dist/host/index.cjs +14 -14
  22. package/dist/host/index.d.cts +3 -3
  23. package/dist/host/index.d.ts +3 -3
  24. package/dist/host/index.js +2 -2
  25. package/dist/index.cjs +28 -87
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +58 -26
  28. package/dist/index.d.ts +58 -26
  29. package/dist/index.js +3 -81
  30. package/dist/index.js.map +1 -1
  31. package/dist/react/index.cjs +81 -140
  32. package/dist/react/index.cjs.map +1 -1
  33. package/dist/react/index.d.cts +66 -82
  34. package/dist/react/index.d.ts +66 -82
  35. package/dist/react/index.js +82 -136
  36. package/dist/react/index.js.map +1 -1
  37. package/dist/{server-ERRDGEZ7.js → server-5N76YCWC.js} +3 -3
  38. package/dist/{server-ERRDGEZ7.js.map → server-5N76YCWC.js.map} +1 -1
  39. package/dist/{server-P54IYX2G.cjs → server-QIYIJHD5.cjs} +3 -3
  40. package/dist/{server-P54IYX2G.cjs.map → server-QIYIJHD5.cjs.map} +1 -1
  41. package/dist/synapse-runtime.iife.global.js +28 -28
  42. package/dist/synapse-ui.iife.global.js +2 -2
  43. package/dist/{types-BSZasM8q.d.cts → types-uEO4VFJ2.d.cts} +87 -166
  44. package/dist/{types-BSZasM8q.d.ts → types-uEO4VFJ2.d.ts} +87 -166
  45. package/dist/vite/index.cjs +1 -1
  46. package/dist/vite/index.cjs.map +1 -1
  47. package/dist/vite/index.js +1 -1
  48. package/dist/vite/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-GNTQJSQX.js.map +0 -1
  51. package/dist/chunk-J5ICAHK2.js.map +0 -1
  52. package/dist/chunk-RYWW6EHB.cjs.map +0 -1
  53. package/dist/chunk-ZMNYUGSF.cjs.map +0 -1
@@ -1,19 +1,21 @@
1
- import { connect, createSynapse } from '../chunk-GNTQJSQX.js';
2
- import { applyTheme } from '../chunk-J5ICAHK2.js';
3
- import { createContext, useState, useRef, useEffect, useCallback, useSyncExternalStore, useContext } from 'react';
4
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { connect, action, pickFile, pickFiles, callToolAsTask } from '../chunk-VYR6V47N.js';
2
+ import '../chunk-UPDBDXQB.js';
3
+ import { createContext, useState, useRef, useEffect, useCallback, useContext } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
5
 
6
6
  var AppContext = createContext(null);
7
- function AppProvider({ children, name, version, autoResize }) {
7
+ function AppProvider({ children, ...options }) {
8
8
  const [app, setApp] = useState(null);
9
+ const [error, setError] = useState(null);
9
10
  const connectingRef = useRef(false);
10
11
  useEffect(() => {
11
12
  if (connectingRef.current) return;
12
13
  connectingRef.current = true;
13
- connect({ name, version, autoResize }).then((a) => {
14
- setApp(a);
14
+ connect(options).then(setApp, (err) => {
15
+ setError(err instanceof Error ? err : new Error(String(err)));
15
16
  });
16
17
  }, []);
18
+ if (error) throw error;
17
19
  if (!app) return null;
18
20
  return /* @__PURE__ */ jsx(AppContext.Provider, { value: app, children });
19
21
  }
@@ -26,18 +28,32 @@ function useAppContext() {
26
28
  }
27
29
  return ctx;
28
30
  }
29
-
30
- // src/react/connect-hooks.ts
31
31
  function useApp() {
32
32
  return useAppContext();
33
33
  }
34
+ function useTheme() {
35
+ const app = useAppContext();
36
+ const [theme, setTheme] = useState(() => app.theme);
37
+ useEffect(() => {
38
+ setTheme(app.theme);
39
+ return app.on("theme-changed", setTheme);
40
+ }, [app]);
41
+ return theme;
42
+ }
43
+ function useHostContext() {
44
+ const app = useAppContext();
45
+ const [ctx, setCtx] = useState(() => app.hostContext);
46
+ useEffect(() => {
47
+ setCtx(app.hostContext);
48
+ return app.on("host-context-changed", (c) => setCtx(c));
49
+ }, [app]);
50
+ return ctx;
51
+ }
34
52
  function useToolResult() {
35
53
  const app = useAppContext();
36
54
  const [data, setData] = useState(null);
37
55
  useEffect(() => {
38
- return app.on("tool-result", (result) => {
39
- setData(result);
40
- });
56
+ return app.on("tool-result", setData);
41
57
  }, [app]);
42
58
  return data;
43
59
  }
@@ -45,76 +61,27 @@ function useToolInput() {
45
61
  const app = useAppContext();
46
62
  const [input, setInput] = useState(null);
47
63
  useEffect(() => {
48
- return app.on("tool-input", (args) => {
49
- setInput(args);
50
- });
64
+ return app.on("tool-input", setInput);
51
65
  }, [app]);
52
66
  return input;
53
67
  }
54
- function useConnectTheme() {
55
- const app = useAppContext();
56
- const [theme, setTheme] = useState(() => app.theme);
57
- useEffect(() => {
58
- setTheme(app.theme);
59
- return app.on("theme-changed", (t) => {
60
- setTheme(t);
61
- });
62
- }, [app]);
63
- return theme;
64
- }
65
68
  function useResize() {
66
69
  const app = useAppContext();
67
70
  return useCallback((width, height) => app.resize(width, height), [app]);
68
71
  }
69
- var SynapseContext = createContext(null);
70
- function SynapseProvider({ children, ...options }) {
71
- const ref = useRef(null);
72
- if (ref.current === null || ref.current.destroyed) {
73
- ref.current = createSynapse(options);
74
- }
75
- return /* @__PURE__ */ jsxs(SynapseContext.Provider, { value: ref.current, children: [
76
- /* @__PURE__ */ jsx(ThemeInjector, { synapse: ref.current }),
77
- children
78
- ] });
79
- }
80
- function ThemeInjector({ synapse }) {
81
- const [theme, setTheme] = useState(() => synapse.getTheme());
82
- useEffect(() => {
83
- setTheme(synapse.getTheme());
84
- return synapse.onThemeChanged(setTheme);
85
- }, [synapse]);
86
- useEffect(() => {
87
- applyTheme(theme.mode, theme.tokens, theme.fontFaces);
88
- }, [theme]);
89
- return null;
90
- }
91
- function useSynapseContext() {
92
- const ctx = useContext(SynapseContext);
93
- if (!ctx) {
94
- throw new Error(
95
- "useSynapse must be used within a <SynapseProvider>. Wrap your app component tree with <SynapseProvider>."
96
- );
97
- }
98
- return ctx;
99
- }
100
-
101
- // src/react/hooks.ts
102
- function useSynapse() {
103
- return useSynapseContext();
104
- }
105
72
  function useCallTool(toolName) {
106
- const synapse = useSynapseContext();
73
+ const app = useAppContext();
107
74
  const [isPending, setIsPending] = useState(false);
108
75
  const [error, setError] = useState(null);
109
76
  const [data, setData] = useState(null);
110
77
  const callIdRef = useRef(0);
111
78
  const call = useCallback(
112
- async (args) => {
79
+ async (args, options) => {
113
80
  const id = ++callIdRef.current;
114
81
  setIsPending(true);
115
82
  setError(null);
116
83
  try {
117
- const result = await synapse.callTool(toolName, args);
84
+ const result = await app.callTool(toolName, args, options);
118
85
  if (id === callIdRef.current) {
119
86
  setData(result.data);
120
87
  setIsPending(false);
@@ -129,64 +96,37 @@ function useCallTool(toolName) {
129
96
  throw err;
130
97
  }
131
98
  },
132
- [synapse, toolName]
99
+ [app, toolName]
133
100
  );
134
101
  return { call, isPending, error, data };
135
102
  }
136
103
  function useDataSync(callback) {
137
- const synapse = useSynapseContext();
138
- const callbackRef = useRef(callback);
139
- callbackRef.current = callback;
140
- useEffect(() => {
141
- return synapse.onDataChanged((event) => callbackRef.current(event));
142
- }, [synapse]);
143
- }
144
- function useAgentAction(callback) {
145
- const synapse = useSynapseContext();
104
+ const app = useAppContext();
146
105
  const callbackRef = useRef(callback);
147
106
  callbackRef.current = callback;
148
107
  useEffect(() => {
149
- return synapse.onAction((action) => callbackRef.current(action));
150
- }, [synapse]);
151
- }
152
- function useHostContext() {
153
- const synapse = useSynapseContext();
154
- const [ctx, setCtx] = useState(() => synapse.getHostContext());
155
- useEffect(() => {
156
- setCtx(synapse.getHostContext());
157
- return synapse.onHostContextChanged((c) => setCtx(c));
158
- }, [synapse]);
159
- return ctx;
160
- }
161
- function useTheme() {
162
- const synapse = useSynapseContext();
163
- const [theme, setTheme] = useState(() => synapse.getTheme());
164
- useEffect(() => {
165
- setTheme(synapse.getTheme());
166
- return synapse.onThemeChanged(setTheme);
167
- }, [synapse]);
168
- return theme;
169
- }
170
- function useAction() {
171
- const synapse = useSynapseContext();
172
- return useCallback(
173
- (action, params) => synapse.action(action, params),
174
- [synapse]
175
- );
176
- }
177
- function useChat() {
178
- const synapse = useSynapseContext();
179
- return useCallback(
180
- (message, context) => synapse.chat(message, context),
181
- [synapse]
182
- );
108
+ return app.on("data-changed", (event) => callbackRef.current(event));
109
+ }, [app]);
183
110
  }
184
- function useVisibleState(factory, deps) {
185
- const synapse = useSynapseContext();
111
+ var MODEL_CONTEXT_DEBOUNCE_MS = 250;
112
+ function useModelContext(factory, deps) {
113
+ const app = useAppContext();
114
+ const timerRef = useRef(null);
186
115
  const push = useCallback(
187
- (state, summary) => synapse.setVisibleState(state, summary),
188
- [synapse]
116
+ (state, summary) => {
117
+ if (timerRef.current) clearTimeout(timerRef.current);
118
+ timerRef.current = setTimeout(() => {
119
+ timerRef.current = null;
120
+ app.updateModelContext(state, summary);
121
+ }, MODEL_CONTEXT_DEBOUNCE_MS);
122
+ },
123
+ [app]
189
124
  );
125
+ useEffect(() => {
126
+ return () => {
127
+ if (timerRef.current) clearTimeout(timerRef.current);
128
+ };
129
+ }, []);
190
130
  const factoryRef = useRef(factory);
191
131
  factoryRef.current = factory;
192
132
  useEffect(() => {
@@ -196,40 +136,46 @@ function useVisibleState(factory, deps) {
196
136
  }, [...deps ?? [], push]);
197
137
  if (!factory) return push;
198
138
  }
139
+ function useSendMessage() {
140
+ const app = useAppContext();
141
+ return useCallback(
142
+ (text, context) => app.sendMessage(text, context),
143
+ [app]
144
+ );
145
+ }
146
+ function useAction() {
147
+ const app = useAppContext();
148
+ return useCallback(
149
+ (name, params) => action(app, name, params),
150
+ [app]
151
+ );
152
+ }
199
153
  function useFileUpload() {
200
- const synapse = useSynapseContext();
154
+ const app = useAppContext();
201
155
  const [isPending, setIsPending] = useState(false);
202
- const pickFile = useCallback(
156
+ const one = useCallback(
203
157
  async (options) => {
204
158
  setIsPending(true);
205
159
  try {
206
- return await synapse.pickFile(options);
160
+ return await pickFile(app, options);
207
161
  } finally {
208
162
  setIsPending(false);
209
163
  }
210
164
  },
211
- [synapse]
165
+ [app]
212
166
  );
213
- const pickFiles = useCallback(
167
+ const many = useCallback(
214
168
  async (options) => {
215
169
  setIsPending(true);
216
170
  try {
217
- return await synapse.pickFiles(options);
171
+ return await pickFiles(app, options);
218
172
  } finally {
219
173
  setIsPending(false);
220
174
  }
221
175
  },
222
- [synapse]
176
+ [app]
223
177
  );
224
- return { pickFile, pickFiles, isPending };
225
- }
226
- function useStore(store) {
227
- const state = useSyncExternalStore(
228
- (onStoreChange) => store.subscribe(onStoreChange),
229
- () => store.getState(),
230
- () => store.getState()
231
- );
232
- return { state, dispatch: store.dispatch };
178
+ return { pickFile: one, pickFiles: many, isPending };
233
179
  }
234
180
  var COMPLETED_STATUS = "completed";
235
181
  var FAILED_STATUS = "failed";
@@ -243,7 +189,7 @@ var DEFAULT_POLL_INTERVAL_MS = 5e3;
243
189
  var POLL_FALLBACK_MULTIPLIER = 1.5;
244
190
  var MAX_REFRESH_FAILURES = 5;
245
191
  function useCallToolAsTask(toolName) {
246
- const synapse = useSynapseContext();
192
+ const app = useAppContext();
247
193
  const [task, setTask] = useState(null);
248
194
  const [result, setResult] = useState(null);
249
195
  const [error, setError] = useState(null);
@@ -307,7 +253,7 @@ function useCallToolAsTask(toolName) {
307
253
  refreshFailureCountRef.current = 0;
308
254
  let handle;
309
255
  try {
310
- handle = await synapse.callToolAsTask(toolName, args, options);
256
+ handle = await callToolAsTask(app, toolName, args, options);
311
257
  } catch (err) {
312
258
  if (gen !== genRef.current) throw err;
313
259
  const e = err instanceof Error ? err : new Error(String(err));
@@ -338,7 +284,7 @@ function useCallToolAsTask(toolName) {
338
284
  terminalRef.current = true;
339
285
  clearPollTimer();
340
286
  setTask((prev) => {
341
- const status = res.isError ? "failed" : "completed";
287
+ const status = res.isError ? FAILED_STATUS : COMPLETED_STATUS;
342
288
  const now = (/* @__PURE__ */ new Date()).toISOString();
343
289
  return prev ? { ...prev, status, lastUpdatedAt: now } : {
344
290
  taskId: handle.task.taskId,
@@ -362,9 +308,9 @@ function useCallToolAsTask(toolName) {
362
308
  clearPollTimer();
363
309
  setTask((prev) => {
364
310
  const now = (/* @__PURE__ */ new Date()).toISOString();
365
- return prev ? { ...prev, status: "failed", lastUpdatedAt: now } : {
311
+ return prev ? { ...prev, status: FAILED_STATUS, lastUpdatedAt: now } : {
366
312
  taskId: handle.task.taskId,
367
- status: "failed",
313
+ status: FAILED_STATUS,
368
314
  ttl: handle.task.ttl,
369
315
  createdAt: handle.task.createdAt,
370
316
  lastUpdatedAt: now
@@ -377,7 +323,7 @@ function useCallToolAsTask(toolName) {
377
323
  if (!terminalRef.current) scheduleNextPoll(gen);
378
324
  return handle;
379
325
  },
380
- [synapse, toolName, detachCurrent, clearPollTimer, scheduleNextPoll]
326
+ [app, toolName, detachCurrent, clearPollTimer, scheduleNextPoll]
381
327
  );
382
328
  const cancel = useCallback(async () => {
383
329
  const h = handleRef.current;
@@ -414,6 +360,6 @@ function useCallToolAsTask(toolName) {
414
360
  return { fire, task, result, error, isWorking, isTerminal, cancel };
415
361
  }
416
362
 
417
- export { AppProvider, SynapseProvider, useAction, useAgentAction, useApp, useCallTool, useCallToolAsTask, useChat, useConnectTheme, useDataSync, useFileUpload, useHostContext, useResize, useStore, useSynapse, useTheme, useToolInput, useToolResult, useVisibleState };
363
+ export { AppProvider, useAction, useApp, useCallTool, useCallToolAsTask, useDataSync, useFileUpload, useHostContext, useModelContext, useResize, useSendMessage, useTheme, useToolInput, useToolResult };
418
364
  //# sourceMappingURL=index.js.map
419
365
  //# sourceMappingURL=index.js.map
@@ -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":["useState","useEffect","createContext","useRef","jsx","useContext","useCallback","isTerminal"],"mappings":"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","file":"index.js","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":["useState","useEffect","useRef","isTerminal"],"mappings":"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","file":"index.js","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"]}
@@ -67,7 +67,7 @@ var HOST_HTML = (uiPort, serverPort) => `<!DOCTYPE html>
67
67
  protocolVersion: "2026-01-26",
68
68
  serverInfo: { name: "nimblebrain", version: "preview" },
69
69
  capabilities: { openLinks: {}, serverTools: {} },
70
- hostContext: { theme: darkMode ? "dark" : "light", primaryColor: "#6366f1", styles: { variables: tokens } }
70
+ hostContext: { theme: darkMode ? "dark" : "light", styles: { variables: tokens } }
71
71
  }
72
72
  });
73
73
  return;
@@ -204,5 +204,5 @@ async function startPreview(options) {
204
204
  }
205
205
 
206
206
  export { startPreview };
207
- //# sourceMappingURL=server-ERRDGEZ7.js.map
208
- //# sourceMappingURL=server-ERRDGEZ7.js.map
207
+ //# sourceMappingURL=server-5N76YCWC.js.map
208
+ //# sourceMappingURL=server-5N76YCWC.js.map