@iloveagents/foundry-web-shell 0.11.0 → 0.12.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.
@@ -8,11 +8,5 @@ interface ShellAppProps {
8
8
  /** Test seam — overrides defaultAgentFetch. */
9
9
  agentFetch?: typeof fetch;
10
10
  }
11
- /**
12
- * Inner shell component — pure React (no `createRoot`). Imported by
13
- * `bootstrap-shell.tsx` for production and by `__tests__/` for assertions.
14
- *
15
- * Wrapper ordering is contractual; see `wrapper-order.test.tsx`.
16
- */
17
11
  export declare function ShellApp({ modules, pages, baseThemeLayers, authProvider, agentFetch, }: ShellAppProps): import("react/jsx-runtime").JSX.Element;
18
12
  export {};
package/dist/shell-app.js CHANGED
@@ -12,17 +12,29 @@ import { _setChatConfigFetch, loadChatConfig } from "./load-chat-config.js";
12
12
  *
13
13
  * Wrapper ordering is contractual; see `wrapper-order.test.tsx`.
14
14
  */
15
+ /**
16
+ * Loads the chat config once, from INSIDE the authenticated subtree.
17
+ *
18
+ * This must not run in `ShellApp`'s own body: `ShellAuth` is its CHILD, so
19
+ * an effect there fires before sign-in completes. The agent fetch would
20
+ * then 401, exhaust its refresh retry, and call
21
+ * `recoverFromHardAuthFailure` -> `loginRedirect` -> remount -> repeat: an
22
+ * infinite login loop on any session that isn't already cached. Rendering
23
+ * the load here means there is a token by the time it runs.
24
+ */
25
+ function ChatConfigBoot({ fetchImpl }) {
26
+ useEffect(() => {
27
+ void loadChatConfig(fetchImpl);
28
+ }, [fetchImpl]);
29
+ return null;
30
+ }
15
31
  export function ShellApp({ modules, pages, baseThemeLayers, authProvider, agentFetch, }) {
16
- // Ask the backend what `Auto` currently resolves to, so the composer's
17
- // effort picker can name the level instead of leaving the user guessing.
18
- // Uses the same agentFetch seam as everything else (auth, base URL, test
19
- // override); failure resets the level to unknown and the picker says so.
20
32
  const resolvedAgentFetch = agentFetch ?? defaultAgentFetch;
33
+ // Bind the refresh seam here (cheap, no I/O) so a host-app context
34
+ // refresh that races boot uses the same fetch client. The actual load
35
+ // happens in <ChatConfigBoot> INSIDE ShellAuth — see the note there.
21
36
  useEffect(() => {
22
- // Bind the refresh seam first so a host-app context refresh that races
23
- // shell boot still uses the same fetch client.
24
37
  _setChatConfigFetch(resolvedAgentFetch);
25
- void loadChatConfig(resolvedAgentFetch);
26
38
  }, [resolvedAgentFetch]);
27
39
  // Merge module pages with customer pages — customer wins on path collision
28
40
  // (last-wins). Stable order: customer pages first (they appear in Routes
@@ -60,7 +72,7 @@ export function ShellApp({ modules, pages, baseThemeLayers, authProvider, agentF
60
72
  // Aggregate chatConversation configs from all modules. First-match
61
73
  // wins at URL-match time (rare to have more than one anyway).
62
74
  const chatConversationConfigs = useMemo(() => modules.flatMap((m) => (m.chatConversation ? [m.chatConversation] : [])), [modules]);
63
- return (_jsx(BrowserRouter, { children: _jsx(ShellAuth, { adapter: authProvider, children: _jsx(ComposedWrappers, { wrappers: wrappers, children: _jsx(ChatConversationAwareRuntime, { fetchFn: fetchFn, configs: chatConversationConfigs, toolUIs: toolUIs, children: _jsx(Suspense, { fallback: null, children: _jsx(Routes, { children: _jsx(Route, { element: _jsx(ShellLayout, { modules: modules, baseThemeLayers: baseThemeLayers }), children: mergedPages.map((p) => (_jsx(Route, { path: p.path === "/" ? undefined : p.path, index: p.path === "/", element: p.element }, p.path))) }) }) }) }) }) }) }));
75
+ return (_jsx(BrowserRouter, { children: _jsxs(ShellAuth, { adapter: authProvider, children: [_jsx(ChatConfigBoot, { fetchImpl: resolvedAgentFetch }), _jsx(ComposedWrappers, { wrappers: wrappers, children: _jsx(ChatConversationAwareRuntime, { fetchFn: fetchFn, configs: chatConversationConfigs, toolUIs: toolUIs, children: _jsx(Suspense, { fallback: null, children: _jsx(Routes, { children: _jsx(Route, { element: _jsx(ShellLayout, { modules: modules, baseThemeLayers: baseThemeLayers }), children: mergedPages.map((p) => (_jsx(Route, { path: p.path === "/" ? undefined : p.path, index: p.path === "/", element: p.element }, p.path))) }) }) }) }) })] }) }));
64
76
  }
65
77
  /**
66
78
  * Reads the current pathname (only meaningful inside ``<BrowserRouter>``),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloveagents/foundry-web-shell",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "description": "Browser bootstrap and layout shell for Foundry UI — bootstrapShell({ modules, pages, theme, authProvider }) mounts the SPA around foundry-web-ui. Module-agnostic.",
6
6
  "keywords": [
@@ -45,9 +45,9 @@
45
45
  "zustand": "^5.0.0"
46
46
  },
47
47
  "dependencies": {
48
- "@iloveagents/foundry-agent": "^0.11.0",
49
- "@iloveagents/foundry-web-primitives": "^0.11.0",
50
- "@iloveagents/foundry-web-ui": "^0.11.0"
48
+ "@iloveagents/foundry-agent": "^0.12.0",
49
+ "@iloveagents/foundry-web-primitives": "^0.12.0",
50
+ "@iloveagents/foundry-web-ui": "^0.12.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@assistant-ui/react": "^0.15.1",