sunpeak 0.18.6 → 0.18.7

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 (49) hide show
  1. package/bin/commands/dev.mjs +6 -2
  2. package/bin/commands/inspect.mjs +13 -1
  3. package/bin/commands/new.mjs +5 -0
  4. package/bin/lib/dev-overlay.mjs +50 -0
  5. package/bin/lib/live/live-config.d.mts +3 -0
  6. package/bin/lib/live/live-config.mjs +3 -1
  7. package/dist/chatgpt/index.cjs +2 -2
  8. package/dist/chatgpt/index.js +2 -2
  9. package/dist/claude/index.cjs +1 -1
  10. package/dist/claude/index.js +1 -1
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/inspector/index.cjs +2 -2
  14. package/dist/inspector/index.js +2 -2
  15. package/dist/inspector/inspector-url.d.ts +13 -0
  16. package/dist/inspector/use-inspector-state.d.ts +2 -0
  17. package/dist/{inspector-DRD_Q66E.cjs → inspector-CKc58UuI.cjs} +60 -19
  18. package/dist/inspector-CKc58UuI.cjs.map +1 -0
  19. package/dist/{inspector-CjSoXm6N.js → inspector-DZrN0kej.js} +60 -19
  20. package/dist/inspector-DZrN0kej.js.map +1 -0
  21. package/dist/{inspector-url-7qhtJwY6.cjs → inspector-url-C3LTKgXt.cjs} +3 -1
  22. package/dist/inspector-url-C3LTKgXt.cjs.map +1 -0
  23. package/dist/{inspector-url-DuEFmxLP.js → inspector-url-CyQcuBI9.js} +3 -1
  24. package/dist/inspector-url-CyQcuBI9.js.map +1 -0
  25. package/dist/mcp/index.cjs +81 -7
  26. package/dist/mcp/index.cjs.map +1 -1
  27. package/dist/mcp/index.js +81 -7
  28. package/dist/mcp/index.js.map +1 -1
  29. package/dist/style.css +4 -0
  30. package/package.json +1 -1
  31. package/template/dist/albums/albums.html +1 -1
  32. package/template/dist/albums/albums.json +1 -1
  33. package/template/dist/carousel/carousel.html +1 -1
  34. package/template/dist/carousel/carousel.json +1 -1
  35. package/template/dist/map/map.html +1 -1
  36. package/template/dist/map/map.json +1 -1
  37. package/template/dist/review/review.html +1 -1
  38. package/template/dist/review/review.json +1 -1
  39. package/template/tests/e2e/albums.spec.ts +1 -1
  40. package/template/tests/e2e/carousel.spec.ts +1 -1
  41. package/template/tests/e2e/dev-overlay.spec.ts +118 -0
  42. package/template/tests/e2e/helpers.ts +13 -0
  43. package/template/tests/e2e/map.spec.ts +1 -1
  44. package/template/tests/e2e/review.spec.ts +1 -1
  45. package/template/tests/live/playwright.config.ts +1 -1
  46. package/dist/inspector-CjSoXm6N.js.map +0 -1
  47. package/dist/inspector-DRD_Q66E.cjs.map +0 -1
  48. package/dist/inspector-url-7qhtJwY6.cjs.map +0 -1
  49. package/dist/inspector-url-DuEFmxLP.js.map +0 -1
@@ -412,6 +412,7 @@ if (!Component) {
412
412
  if (import.meta.hot) {
413
413
  import.meta.hot.accept();
414
414
  }
415
+
415
416
  `;
416
417
  }
417
418
  },
@@ -547,11 +548,14 @@ if (import.meta.hot) {
547
548
  if (typeof mod.default !== 'function') {
548
549
  throw new Error(`Tool "${name}" has no default export handler`);
549
550
  }
551
+ const startTime = performance.now();
550
552
  const result = await mod.default(args, {});
553
+ const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
551
554
  if (typeof result === 'string') {
552
- return { content: [{ type: 'text', text: result }] };
555
+ return { content: [{ type: 'text', text: result }], _meta: { _sunpeak: { requestTimeMs: durationMs } } };
553
556
  }
554
- return result;
557
+ const typed = result ?? {};
558
+ return { ...typed, _meta: { ...typed._meta, _sunpeak: { requestTimeMs: durationMs } } };
555
559
  }
556
560
  throw new Error(`Tool "${name}" not found`);
557
561
  },
@@ -20,6 +20,7 @@ const { join, resolve, dirname } = path;
20
20
  import { fileURLToPath, pathToFileURL } from 'url';
21
21
  import { getPort } from '../lib/get-port.mjs';
22
22
  import { startSandboxServer } from '../lib/sandbox-server.mjs';
23
+ import { getDevOverlayScript } from '../lib/dev-overlay.mjs';
23
24
 
24
25
  const __dirname = dirname(fileURLToPath(import.meta.url));
25
26
  const SUNPEAK_PKG_DIR = resolve(__dirname, '..', '..');
@@ -850,7 +851,18 @@ function sunpeakInspectEndpointsPlugin(getClient, setClient, pluginOpts = {}) {
850
851
  'X-Content-Type-Options': 'nosniff',
851
852
  });
852
853
  if (typeof content.text === 'string') {
853
- res.end(content.text);
854
+ const stripOverlay = url.searchParams.get('devOverlay') === 'false';
855
+ let text = content.text;
856
+ if (stripOverlay) {
857
+ // Strip dev overlay (e.g., for e2e tests)
858
+ text = text.replace(/<script>(?:(?!<\/script>)[\s\S])*?__sunpeak-dev-timing(?:(?!<\/script>)[\s\S])*?<\/script>/g, '');
859
+ } else if (process.env.SUNPEAK_DEV_OVERLAY !== 'false' && !text.includes('__sunpeak-dev-timing') && text.includes('</body>')) {
860
+ // Inject dev overlay into resources from non-sunpeak servers.
861
+ // The overlay shows resource served timestamp and tool timing (from
862
+ // _meta._sunpeak.requestTimeMs on the PostMessage tool-result notification).
863
+ text = text.replace('</body>', `${getDevOverlayScript(Date.now(), null)}\n</body>`);
864
+ }
865
+ res.end(text);
854
866
  } else if (content.blob) {
855
867
  res.end(Buffer.from(content.blob, 'base64'));
856
868
  } else {
@@ -175,6 +175,11 @@ export async function init(projectName, resourcesArg, deps = defaultDeps) {
175
175
  return false;
176
176
  }
177
177
 
178
+ // Skip framework-internal test files (dev overlay tests are for sunpeak development, not user projects)
179
+ if ((src.includes('/tests/e2e/') || src.includes('/tests/live/')) && name.startsWith('dev-')) {
180
+ return false;
181
+ }
182
+
178
183
  // Skip deps.json files (build-time metadata, not needed in scaffolded projects)
179
184
  if (name === 'deps.json' && src.includes('/resources/')) {
180
185
  return false;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Generate an inline script that shows a dev overlay with resource served timestamp
3
+ * and tool call request timing.
4
+ *
5
+ * The resource timestamp is baked into the HTML at readResource time. Tool timing
6
+ * arrives two ways:
7
+ * 1. Baked-in `toolMs` from readResource time (works when the tool call precedes the
8
+ * resource read, which is the case for Claude and the inspector's initial render).
9
+ * 2. `_meta._sunpeak.requestTimeMs` on the tool result PostMessage (handles inspector
10
+ * Re-run and hosts that pass `_meta` through to the resource iframe).
11
+ *
12
+ * @param {number} servedAt - Unix timestamp (ms) when the resource HTML was generated/served.
13
+ * @param {number | null} toolMs - Most recent tool call duration (ms), or null if no call yet.
14
+ * @returns {string} HTML script tag with the dev overlay.
15
+ */
16
+ export function getDevOverlayScript(servedAt, toolMs) {
17
+ return `<script>
18
+ (function(){
19
+ var servedAt=${servedAt};
20
+ var el=null,hidden=false,lastMs=${toolMs ?? 'null'};
21
+ function fmt(ts){var d=new Date(ts);var h=d.getHours(),m=d.getMinutes(),s=d.getSeconds();return (h<10?'0':'')+h+':'+(m<10?'0':'')+m+':'+(s<10?'0':'')+s}
22
+ function make(){
23
+ var existing=document.getElementById('__sunpeak-dev-timing');
24
+ if(existing)return existing;
25
+ var b=document.createElement('button');b.id='__sunpeak-dev-timing';
26
+ b.style.cssText='position:fixed;bottom:8px;right:8px;z-index:2147483647;display:grid;grid-template-columns:auto auto;gap:0 6px;align-items:baseline;padding:5px 8px;border-radius:6px;border:1px solid rgba(128,128,128,0.25);background:rgba(0,0,0,0.75);backdrop-filter:blur(8px);color:#e5e5e5;font-size:11px;font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;line-height:1.4;cursor:pointer;user-select:none;opacity:0.85;transition:opacity 150ms;';
27
+ b.onmouseenter=function(){b.style.opacity='1'};
28
+ b.onmouseleave=function(){b.style.opacity='0.85'};
29
+ b.onclick=function(){hidden=!hidden;upd()};
30
+ document.body.appendChild(b);return b;
31
+ }
32
+ function upd(){
33
+ if(!el)el=make();
34
+ if(hidden){el.title='Show dev info';el.innerHTML='<span style="grid-column:1/-1;font-size:9px;text-align:center">DEV</span>';return}
35
+ var h='';
36
+ h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Resource:</span><span style="white-space:nowrap">'+fmt(servedAt)+'</span>';
37
+ if(lastMs!=null)h+='<span style="text-align:right;color:rgba(255,255,255,0.5);white-space:nowrap">Tool:</span><span style="white-space:nowrap">'+(lastMs%1===0?lastMs:lastMs.toFixed(1))+'ms</span>';
38
+ el.title='Hide dev info';el.innerHTML=h;
39
+ }
40
+ upd();
41
+ window.addEventListener('message',function(e){
42
+ var d=e.data;if(!d||typeof d!=='object')return;
43
+ if(d.method!=='ui/notifications/tool-result')return;
44
+ var p=d.params;if(!p)return;
45
+ var ms=p._meta&&p._meta._sunpeak&&p._meta._sunpeak.requestTimeMs;
46
+ if(typeof ms==='number'){lastMs=ms;upd()}
47
+ });
48
+ })();
49
+ </script>`;
50
+ }
@@ -23,6 +23,9 @@ export interface LiveConfigOptions {
23
23
  /** Browser permissions to grant (e.g., ['geolocation']). */
24
24
  permissions?: string[];
25
25
 
26
+ /** Show the dev overlay (resource timestamp + tool timing) in resources. Default: true */
27
+ devOverlay?: boolean;
28
+
26
29
  /** Additional Playwright `use` options, merged with defaults. */
27
30
  use?: Record<string, unknown>;
28
31
  }
@@ -32,6 +32,7 @@ const GLOBAL_SETUP_PATH = join(__dirname, 'global-setup.mjs');
32
32
  * @param {string} [options.timezoneId] - Timezone (e.g., 'America/New_York')
33
33
  * @param {{ latitude: number, longitude: number }} [options.geolocation] - Geolocation coordinates
34
34
  * @param {string[]} [options.permissions] - Browser permissions to grant (e.g., ['geolocation'])
35
+ * @param {boolean} [options.devOverlay=true] - Show the dev overlay (resource timestamp + tool timing) in resources
35
36
  * @param {Object} [options.use] - Additional Playwright `use` options (merged with defaults)
36
37
  */
37
38
  export function createLiveConfig(hostOptions, options = {}) {
@@ -40,6 +41,7 @@ export function createLiveConfig(hostOptions, options = {}) {
40
41
  testDir = '.',
41
42
  authDir,
42
43
  vitePort = getPortSync(3456),
44
+ devOverlay = true,
43
45
  colorScheme,
44
46
  viewport,
45
47
  locale,
@@ -89,7 +91,7 @@ export function createLiveConfig(hostOptions, options = {}) {
89
91
  },
90
92
  ],
91
93
  webServer: {
92
- command: `SUNPEAK_LIVE_TEST=1 SUNPEAK_SANDBOX_PORT=${getPortSync(24680)} pnpm dev -- --prod-resources --port ${vitePort}`,
94
+ command: `SUNPEAK_LIVE_TEST=1 SUNPEAK_SANDBOX_PORT=${getPortSync(24680)}${devOverlay ? '' : ' SUNPEAK_DEV_OVERLAY=false'} pnpm dev -- --prod-resources --port ${vitePort}`,
93
95
  url: `http://localhost:${vitePort}/health`,
94
96
  reuseExistingServer: !process.env.CI,
95
97
  timeout: 60_000,
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../chunk-9hOWP6kD.cjs");
3
3
  require("../protocol-jbxhzcnS.cjs");
4
- const require_inspector = require("../inspector-DRD_Q66E.cjs");
5
- const require_inspector_url = require("../inspector-url-7qhtJwY6.cjs");
4
+ const require_inspector = require("../inspector-CKc58UuI.cjs");
5
+ const require_inspector_url = require("../inspector-url-C3LTKgXt.cjs");
6
6
  const require_discovery = require("../discovery-Clu4uHp1.cjs");
7
7
  //#region src/chatgpt/index.ts
8
8
  var chatgpt_exports = /* @__PURE__ */ require_chunk.__exportAll({
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
2
2
  import "../protocol-DJmRaBzO.js";
3
- import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Inspector, v as SCREEN_WIDTHS } from "../inspector-CjSoXm6N.js";
4
- import { t as createInspectorUrl } from "../inspector-url-DuEFmxLP.js";
3
+ import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Inspector, v as SCREEN_WIDTHS } from "../inspector-DZrN0kej.js";
4
+ import { t as createInspectorUrl } from "../inspector-url-CyQcuBI9.js";
5
5
  import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
6
6
  //#region src/chatgpt/index.ts
7
7
  var chatgpt_exports = /* @__PURE__ */ __exportAll({
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../chunk-9hOWP6kD.cjs");
3
3
  require("../protocol-jbxhzcnS.cjs");
4
- const require_inspector = require("../inspector-DRD_Q66E.cjs");
4
+ const require_inspector = require("../inspector-CKc58UuI.cjs");
5
5
  exports.Inspector = require_inspector.Inspector;
@@ -1,3 +1,3 @@
1
1
  import "../protocol-DJmRaBzO.js";
2
- import { t as Inspector } from "../inspector-CjSoXm6N.js";
2
+ import { t as Inspector } from "../inspector-DZrN0kej.js";
3
3
  export { Inspector };
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-9hOWP6kD.cjs");
3
3
  const require_protocol = require("./protocol-jbxhzcnS.cjs");
4
4
  const require_use_app = require("./use-app-Dqh20JPP.cjs");
5
- const require_inspector = require("./inspector-DRD_Q66E.cjs");
5
+ const require_inspector = require("./inspector-CKc58UuI.cjs");
6
6
  const require_host_index = require("./host/index.cjs");
7
7
  const require_inspector_index = require("./inspector/index.cjs");
8
8
  const require_chatgpt_index = require("./chatgpt/index.cjs");
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { $ as literal, L as RequestIdSchema, Q as boolean, R as ResourceLinkSchema, U as ToolSchema, X as _undefined, Z as array, _ as ImplementationSchema, a as CallToolResultSchema, at as unknown, et as number, it as union, nt as record, p as EmbeddedResourceSchema, rt as string, s as ContentBlockSchema, tt as object } from "./protocol-DJmRaBzO.js";
2
2
  import { $ as s, A as V, B as e, C as QQ, D as T, E as S, F as _Q, G as k, H as gQ, I as a, J as o, K as m, L as b, M as W, N as WQ, O as TQ, P as _, Q as r, R as c, S as PQ, T as RQ, U as h, V as g, W as jQ, X as q, Y as p, Z as qQ, _ as LQ, a as B, at as zQ, b as OQ, c as EQ, d as G, et as t, f as I, g as L, h as K, i as AQ, it as z, j as VQ, k as U, l as F, m as J, n as AppProvider, nt as w, o as BQ, p as IQ, q as n, r as A, rt as x, s as C, t as useApp, tt as vX, u as FQ, v as MX, w as R, x as P, y as O, z as d } from "./use-app-BNbz1uzj.js";
3
- import { S as DEFAULT_STYLE_VARIABLES } from "./inspector-CjSoXm6N.js";
3
+ import { S as DEFAULT_STYLE_VARIABLES } from "./inspector-DZrN0kej.js";
4
4
  import { detectHost, isChatGPT, isClaude } from "./host/index.js";
5
5
  import { t as inspector_exports } from "./inspector/index.js";
6
6
  import { t as chatgpt_exports } from "./chatgpt/index.js";
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../chunk-9hOWP6kD.cjs");
3
3
  require("../protocol-jbxhzcnS.cjs");
4
- const require_inspector = require("../inspector-DRD_Q66E.cjs");
5
- const require_inspector_url = require("../inspector-url-7qhtJwY6.cjs");
4
+ const require_inspector = require("../inspector-CKc58UuI.cjs");
5
+ const require_inspector_url = require("../inspector-url-C3LTKgXt.cjs");
6
6
  const require_discovery = require("../discovery-Clu4uHp1.cjs");
7
7
  //#region src/inspector/index.ts
8
8
  var inspector_exports = /* @__PURE__ */ require_chunk.__exportAll({
@@ -1,7 +1,7 @@
1
1
  import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
2
2
  import "../protocol-DJmRaBzO.js";
3
- import { _ as McpAppHost, a as SidebarControl, b as getRegisteredHosts, c as SidebarTextarea, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, i as SidebarCollapsibleControl, l as SidebarToggle, m as useInspectorState, n as resolveServerToolResult, o as SidebarInput, p as useMcpConnection, r as SidebarCheckbox, s as SidebarSelect, t as Inspector, u as SimpleSidebar, v as SCREEN_WIDTHS, x as registerHostShell, y as getHostShell } from "../inspector-CjSoXm6N.js";
4
- import { t as createInspectorUrl } from "../inspector-url-DuEFmxLP.js";
3
+ import { _ as McpAppHost, a as SidebarControl, b as getRegisteredHosts, c as SidebarTextarea, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, i as SidebarCollapsibleControl, l as SidebarToggle, m as useInspectorState, n as resolveServerToolResult, o as SidebarInput, p as useMcpConnection, r as SidebarCheckbox, s as SidebarSelect, t as Inspector, u as SimpleSidebar, v as SCREEN_WIDTHS, x as registerHostShell, y as getHostShell } from "../inspector-DZrN0kej.js";
4
+ import { t as createInspectorUrl } from "../inspector-url-CyQcuBI9.js";
5
5
  import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
6
6
  //#region src/inspector/index.ts
7
7
  var inspector_exports = /* @__PURE__ */ __exportAll({
@@ -109,6 +109,19 @@ export interface InspectorUrlParams {
109
109
  * Enable Prod Resources mode (production dist/ bundles instead of HMR).
110
110
  */
111
111
  prodResources?: boolean;
112
+ /**
113
+ * Show the inspector sidebar. Useful for headless testing or embedding
114
+ * the inspector as a pure resource viewer.
115
+ * @default true
116
+ */
117
+ sidebar?: boolean;
118
+ /**
119
+ * Show the dev overlay (resource timestamp + tool timing) inside resources.
120
+ * Set to false to hide it during e2e tests so it doesn't interfere with
121
+ * element assertions.
122
+ * @default true
123
+ */
124
+ devOverlay?: boolean;
112
125
  }
113
126
  /**
114
127
  * Creates a URL path with query parameters for the Inspector.
@@ -88,6 +88,8 @@ export interface InspectorState {
88
88
  prefersBorder: boolean;
89
89
  urlTool: string | undefined;
90
90
  urlProdResources: boolean | undefined;
91
+ urlSidebar: boolean | undefined;
92
+ urlDevOverlay: boolean | undefined;
91
93
  }
92
94
  export declare function useInspectorState({ simulations, defaultHost, }: UseInspectorStateOptions): InspectorState;
93
95
  export {};
@@ -2338,6 +2338,10 @@ function parseUrlParams() {
2338
2338
  const host = params.get("host") ?? void 0;
2339
2339
  const prodResourcesParam = params.get("prodResources");
2340
2340
  const prodResources = prodResourcesParam === "true" ? true : prodResourcesParam === "false" ? false : void 0;
2341
+ const sidebarParam = params.get("sidebar");
2342
+ const sidebar = sidebarParam === "false" ? false : sidebarParam === "true" ? true : void 0;
2343
+ const devOverlayParam = params.get("devOverlay");
2344
+ const devOverlay = devOverlayParam === "false" ? false : devOverlayParam === "true" ? true : void 0;
2341
2345
  const deviceType = params.get("deviceType");
2342
2346
  let platform;
2343
2347
  if (deviceType === "mobile" || deviceType === "tablet") platform = "mobile";
@@ -2370,7 +2374,9 @@ function parseUrlParams() {
2370
2374
  deviceCapabilities,
2371
2375
  safeAreaInsets,
2372
2376
  host: host ?? void 0,
2373
- prodResources
2377
+ prodResources,
2378
+ sidebar,
2379
+ devOverlay
2374
2380
  };
2375
2381
  }
2376
2382
  function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
@@ -2621,7 +2627,9 @@ function useInspectorState({ simulations, defaultHost = "chatgpt" }) {
2621
2627
  permissions: resourceMeta?.permissions,
2622
2628
  prefersBorder: resourceMeta?.prefersBorder ?? false,
2623
2629
  urlTool: urlParams.tool,
2624
- urlProdResources: urlParams.prodResources
2630
+ urlProdResources: urlParams.prodResources,
2631
+ urlSidebar: urlParams.sidebar,
2632
+ urlDevOverlay: urlParams.devOverlay
2625
2633
  };
2626
2634
  }
2627
2635
  //#endregion
@@ -3196,6 +3204,8 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3196
3204
  const [oauthError, setOauthError] = react.useState();
3197
3205
  const connection = useMcpConnection(mcpServerUrl || void 0);
3198
3206
  const [prodResources, setProdResources] = react.useState(state.urlProdResources ?? defaultProdResources);
3207
+ const showSidebar = state.urlSidebar !== false;
3208
+ const showDevOverlay = state.urlDevOverlay !== false;
3199
3209
  const [isRunning, setIsRunning] = react.useState(false);
3200
3210
  const [hasRun, setHasRun] = react.useState(false);
3201
3211
  const [showCheck, setShowCheck] = react.useState(false);
@@ -3366,26 +3376,47 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3366
3376
  if (!caller || !sim) return;
3367
3377
  const toolName = sim.tool.name;
3368
3378
  setIsRunning(true);
3379
+ const startTime = performance.now();
3369
3380
  try {
3370
3381
  const result = await caller({
3371
3382
  name: toolName,
3372
3383
  arguments: state.toolInput
3373
3384
  });
3374
- state.setToolResult(result);
3375
- state.setToolResultJson(JSON.stringify(result, null, 2));
3385
+ const clientMs = Math.round((performance.now() - startTime) * 10) / 10;
3386
+ const resultMeta = result?._meta;
3387
+ const serverMs = (resultMeta?._sunpeak)?.requestTimeMs;
3388
+ const durationMs = typeof serverMs === "number" ? serverMs : clientMs;
3389
+ const resultWithTiming = {
3390
+ ...result,
3391
+ _meta: {
3392
+ ...resultMeta,
3393
+ _sunpeak: { requestTimeMs: durationMs }
3394
+ }
3395
+ };
3396
+ state.setToolResult(resultWithTiming);
3397
+ const displayResult = resultMeta?._sunpeak ? (() => {
3398
+ const { _sunpeak: _, ...cleanMeta } = resultMeta;
3399
+ const clean = { ...result };
3400
+ clean._meta = Object.keys(cleanMeta).length > 0 ? cleanMeta : void 0;
3401
+ if (clean._meta === void 0) delete clean._meta;
3402
+ return clean;
3403
+ })() : result;
3404
+ state.setToolResultJson(JSON.stringify(displayResult, null, 2));
3376
3405
  state.setToolResultError("");
3377
3406
  setHasRun(true);
3378
3407
  setShowCheck(true);
3379
3408
  clearTimeout(checkTimerRef.current);
3380
3409
  checkTimerRef.current = setTimeout(() => setShowCheck(false), 2e3);
3381
3410
  } catch (err) {
3411
+ const durationMs = Math.round((performance.now() - startTime) * 10) / 10;
3382
3412
  const message = err instanceof Error ? err.message : String(err);
3383
3413
  state.setToolResult({
3384
3414
  content: [{
3385
3415
  type: "text",
3386
3416
  text: `Error: ${message}`
3387
3417
  }],
3388
- isError: true
3418
+ isError: true,
3419
+ _meta: { _sunpeak: { requestTimeMs: durationMs } }
3389
3420
  });
3390
3421
  state.setToolResultJson(JSON.stringify({
3391
3422
  content: [{
@@ -3520,7 +3551,8 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3520
3551
  clearTimeout(timer);
3521
3552
  };
3522
3553
  }, [prodResourcesPath]);
3523
- const effectiveResourceUrl = (prodResourcesPath && prodResourcesReady ? prodResourcesPath : void 0) ?? state.resourceUrl;
3554
+ const baseResourceUrl = (prodResourcesPath && prodResourcesReady ? prodResourcesPath : void 0) ?? state.resourceUrl;
3555
+ const effectiveResourceUrl = baseResourceUrl && !showDevOverlay ? `${baseResourceUrl}${baseResourceUrl.includes("?") ? "&" : "?"}devOverlay=false` : baseResourceUrl;
3524
3556
  const prodResourcesLoading = !!prodResourcesPath && !prodResourcesReady;
3525
3557
  const hasTools = toolNames.length > 0;
3526
3558
  const showEmptyState = !(activeSimulationName !== null && currentSim?.toolResult != null) && !hasRun;
@@ -3638,6 +3670,26 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
3638
3670
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M0 0L10 6L0 12V0Z" })
3639
3671
  }), "Run"]
3640
3672
  }) : void 0;
3673
+ const conversationContent = ShellConversation ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellConversation, {
3674
+ screenWidth: state.screenWidth,
3675
+ displayMode: state.displayMode,
3676
+ platform: state.platform,
3677
+ onRequestDisplayMode: state.handleDisplayModeChange,
3678
+ appName,
3679
+ appIcon,
3680
+ userMessage,
3681
+ onContentWidthChange: state.handleContentWidthChange,
3682
+ headerAction: runButton,
3683
+ children: content
3684
+ }) : content;
3685
+ if (!showSidebar) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeProvider, {
3686
+ theme: state.theme,
3687
+ applyTheme,
3688
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3689
+ className: "flex h-screen w-screen",
3690
+ children: conversationContent
3691
+ })
3692
+ });
3641
3693
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeProvider, {
3642
3694
  theme: state.theme,
3643
3695
  applyTheme,
@@ -4170,18 +4222,7 @@ function Inspector({ children, simulations: initialSimulations = {}, appName = "
4170
4222
  })
4171
4223
  ]
4172
4224
  }),
4173
- children: ShellConversation ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellConversation, {
4174
- screenWidth: state.screenWidth,
4175
- displayMode: state.displayMode,
4176
- platform: state.platform,
4177
- onRequestDisplayMode: state.handleDisplayModeChange,
4178
- appName,
4179
- appIcon,
4180
- userMessage,
4181
- onContentWidthChange: state.handleContentWidthChange,
4182
- headerAction: runButton,
4183
- children: content
4184
- }) : content
4225
+ children: conversationContent
4185
4226
  })
4186
4227
  });
4187
4228
  }
@@ -4319,4 +4360,4 @@ Object.defineProperty(exports, "useThemeContext", {
4319
4360
  }
4320
4361
  });
4321
4362
 
4322
- //# sourceMappingURL=inspector-DRD_Q66E.cjs.map
4363
+ //# sourceMappingURL=inspector-CKc58UuI.cjs.map