@noodleseed/assistant 1.23.0 → 1.25.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 (50) hide show
  1. package/README.md +4 -9
  2. package/dist/{app-view-element-7ZQZLYbL.d.ts → app-view-element-9ojWWYov.d.cts} +3 -2
  3. package/dist/{app-view-element-1i_w6FDf.d.cts → app-view-element-BM4lral-.d.ts} +3 -2
  4. package/dist/app-view.cjs +213 -41
  5. package/dist/app-view.cjs.map +1 -1
  6. package/dist/app-view.d.cts +3 -3
  7. package/dist/app-view.d.ts +3 -3
  8. package/dist/app-view.js +2 -2
  9. package/dist/{appearance-Cc-eqSbp.d.cts → appearance-B6vSg8Ub.d.cts} +5 -0
  10. package/dist/{appearance-Cc-eqSbp.d.ts → appearance-B6vSg8Ub.d.ts} +5 -0
  11. package/dist/{chunk-IBDP6PXQ.js → chunk-7BQHW37I.js} +71 -3
  12. package/dist/chunk-7BQHW37I.js.map +1 -0
  13. package/dist/{chunk-5NBHHNI3.js → chunk-LNBUYHL7.js} +2 -2
  14. package/dist/chunk-LNBUYHL7.js.map +1 -0
  15. package/dist/{chunk-XKTUXFGX.js → chunk-P32MAR4K.js} +214 -42
  16. package/dist/chunk-P32MAR4K.js.map +1 -0
  17. package/dist/{chunk-X7UQDIGS.js → chunk-SGMECNNU.js} +94 -43
  18. package/dist/chunk-SGMECNNU.js.map +1 -0
  19. package/dist/{client-Bp_VKpMg.d.ts → client-BkogON2A.d.ts} +11 -1
  20. package/dist/{client-uo57Swlm.d.cts → client-C07ppTU_.d.cts} +11 -1
  21. package/dist/client.cjs +70 -2
  22. package/dist/client.cjs.map +1 -1
  23. package/dist/client.d.cts +2 -2
  24. package/dist/client.d.ts +2 -2
  25. package/dist/client.js +1 -1
  26. package/dist/embed.global.js +149 -114
  27. package/dist/index.cjs +396 -105
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +3 -3
  30. package/dist/index.d.ts +3 -3
  31. package/dist/index.js +3 -3
  32. package/dist/react/client.cjs +70 -2
  33. package/dist/react/client.cjs.map +1 -1
  34. package/dist/react/client.d.cts +2 -2
  35. package/dist/react/client.d.ts +2 -2
  36. package/dist/react/client.js +1 -1
  37. package/dist/react.cjs +396 -105
  38. package/dist/react.cjs.map +1 -1
  39. package/dist/react.d.cts +3 -3
  40. package/dist/react.d.ts +3 -3
  41. package/dist/react.js +4 -4
  42. package/dist/server.cjs.map +1 -1
  43. package/dist/server.d.cts +3 -1
  44. package/dist/server.d.ts +3 -1
  45. package/dist/server.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/chunk-5NBHHNI3.js.map +0 -1
  48. package/dist/chunk-IBDP6PXQ.js.map +0 -1
  49. package/dist/chunk-X7UQDIGS.js.map +0 -1
  50. package/dist/chunk-XKTUXFGX.js.map +0 -1
@@ -3,7 +3,7 @@ import {
3
3
  mountAssistantApp,
4
4
  unmountAssistantApp,
5
5
  updateAssistantAppTheme
6
- } from "./chunk-XKTUXFGX.js";
6
+ } from "./chunk-P32MAR4K.js";
7
7
 
8
8
  // src/app-view-element.ts
9
9
  var APP_VIEW_TAG_NAME = "noodle-app-view";
@@ -177,4 +177,4 @@ export {
177
177
  NoodleAppViewElement,
178
178
  registerNoodleAppView
179
179
  };
180
- //# sourceMappingURL=chunk-5NBHHNI3.js.map
180
+ //# sourceMappingURL=chunk-LNBUYHL7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app-view-element.ts","../src/app-view.ts"],"sourcesContent":["import { mountAssistantApp, unmountAssistantApp, updateAssistantAppTheme } from './app-host.js';\nimport { ASSISTANT_APP_VIEW_STYLES } from './app-view-styles.js';\nimport type { AssistantClient, AssistantViewAvailableDetail } from './client.js';\nimport type { AssistantErrorDetail } from './transport.js';\n\nexport const APP_VIEW_TAG_NAME = 'noodle-app-view';\n\nexport interface AssistantAppViewErrorDetail {\n readonly code: 'view_render_timeout' | 'view_bridge_error';\n readonly retryable: false;\n readonly stage: 'sandbox_load' | 'sandbox_proxy' | 'app_initialize';\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'noodle-app-view': NoodleAppViewElement;\n }\n\n interface HTMLElementEventMap {\n 'assistant-error': CustomEvent<AssistantErrorDetail>;\n }\n}\n\nconst HTMLElementBase: typeof HTMLElement =\n globalThis.HTMLElement ?? (class {} as unknown as typeof HTMLElement);\n\nfunction viewIdentity(view: AssistantViewAvailableDetail): string {\n return `${view.id}\\u0000${view.resourceUri}`;\n}\n\n/**\n * Framework-neutral host for one service-resolved MCP App view.\n *\n * `client` and `view` are object properties, not serialized attributes. Vue, Angular, and plain DOM\n * renderers can therefore pass the same `data-view` payload without taking a React dependency.\n */\nexport class NoodleAppViewElement extends HTMLElementBase {\n static readonly observedAttributes = ['allow-fullscreen', 'theme'];\n\n #allowFullscreen = false;\n #client: AssistantClient | undefined;\n #view: AssistantViewAvailableDetail | undefined;\n #theme: 'light' | 'dark' = 'light';\n #container: HTMLElement | undefined;\n #mounted: HTMLElement | undefined;\n #mountedClient: AssistantClient | undefined;\n #mountedAllowFullscreen = false;\n #mountedIdentity: string | undefined;\n #requestedTeardown: { readonly client: AssistantClient; readonly identity: string } | undefined;\n\n get client(): AssistantClient | undefined {\n return this.#client;\n }\n\n /** Allow the hosted App to request fullscreen. Disabled by default. */\n get allowFullscreen(): boolean {\n return this.#allowFullscreen;\n }\n\n set allowFullscreen(value: boolean) {\n const next = value === true;\n if (next === this.#allowFullscreen) return;\n this.#allowFullscreen = next;\n this.toggleAttribute('allow-fullscreen', next);\n this.#reconcile();\n }\n\n set client(value: AssistantClient | undefined) {\n if (value === this.#client) return;\n this.#client = value;\n this.#reconcile();\n }\n\n get view(): AssistantViewAvailableDetail | undefined {\n return this.#view;\n }\n\n set view(value: AssistantViewAvailableDetail | undefined) {\n this.#view = value;\n this.#reconcile();\n }\n\n get theme(): 'light' | 'dark' {\n return this.#theme;\n }\n\n set theme(value: 'light' | 'dark') {\n const next = value === 'dark' ? 'dark' : 'light';\n const changed = next !== this.#theme;\n this.#theme = next;\n if (this.getAttribute('data-theme') !== next) this.setAttribute('data-theme', next);\n if (changed && this.#mounted) updateAssistantAppTheme(this.#mounted, next);\n }\n\n connectedCallback(): void {\n this.#ensureContainer();\n this.#upgradeProperty('allowFullscreen');\n this.#upgradeProperty('client');\n this.#upgradeProperty('theme');\n this.#upgradeProperty('view');\n this.#reconcile();\n }\n\n disconnectedCallback(): void {\n this.#requestedTeardown = undefined;\n this.#releaseMount();\n }\n\n attributeChangedCallback(name: string, _previous: string | null, value: string | null): void {\n if (name === 'allow-fullscreen') this.allowFullscreen = value !== null;\n if (name === 'theme') this.theme = value === 'dark' ? 'dark' : 'light';\n }\n\n #ensureContainer(): HTMLElement {\n if (this.#container) return this.#container;\n const root = this.shadowRoot ?? this.attachShadow({ mode: 'open' });\n root.innerHTML = ASSISTANT_APP_VIEW_STYLES;\n const container = document.createElement('div');\n container.className = 'noodle-app-mount';\n container.setAttribute('part', 'mount');\n root.append(container);\n this.#container = container;\n return container;\n }\n\n #upgradeProperty(name: 'allowFullscreen' | 'client' | 'theme' | 'view'): void {\n const property = Object.getOwnPropertyDescriptor(this, name);\n if (!property) return;\n Reflect.deleteProperty(this, name);\n Reflect.set(this, name, property.value);\n }\n\n #releaseMount(): void {\n const mounted = this.#mounted;\n this.#mounted = undefined;\n this.#mountedClient = undefined;\n this.#mountedAllowFullscreen = false;\n this.#mountedIdentity = undefined;\n this.#container?.replaceChildren();\n if (mounted) void unmountAssistantApp(mounted);\n }\n\n #reconcile(): void {\n if (!this.isConnected) return;\n const client = this.#client;\n const detail = this.#view;\n const identity = detail ? viewIdentity(detail) : undefined;\n\n if (this.#mounted) {\n if (\n client === this.#mountedClient &&\n identity === this.#mountedIdentity &&\n this.#allowFullscreen === this.#mountedAllowFullscreen\n ) {\n return;\n }\n this.#releaseMount();\n }\n\n if (\n this.#requestedTeardown &&\n (client !== this.#requestedTeardown.client || identity !== this.#requestedTeardown.identity)\n ) {\n this.#requestedTeardown = undefined;\n }\n if (!client || !detail?.html || !identity || this.#requestedTeardown) return;\n\n const initialHtml = detail.html;\n const sandboxUrl = client.appSandboxUrl?.();\n let card: HTMLElement | undefined;\n card = mountAssistantApp(\n detail,\n {\n client,\n sendMessage: (message) => client.sendMessage(message),\n updateModelContext: (update) => client.updateModelContext(update),\n theme: this.#theme,\n },\n {\n allowFullscreen: this.#allowFullscreen,\n ...(sandboxUrl ? { sandboxUrl } : {}),\n getDetail: () => {\n const latest = this.#view;\n if (this.#client !== client || !latest || viewIdentity(latest) !== identity)\n return detail;\n return latest.html ? latest : { ...latest, html: initialHtml };\n },\n onRenderFailure: (failure) => {\n this.dispatchEvent(\n new CustomEvent<AssistantAppViewErrorDetail>('assistant-error', {\n detail: failure,\n bubbles: true,\n composed: true,\n }),\n );\n },\n onTeardownRequest: () => {\n if (this.#mounted !== card) return;\n this.#mounted = undefined;\n this.#mountedClient = undefined;\n this.#mountedIdentity = undefined;\n this.#requestedTeardown = { client, identity };\n },\n },\n );\n if (!card) return;\n this.#mounted = card;\n this.#mountedClient = client;\n this.#mountedAllowFullscreen = this.#allowFullscreen;\n this.#mountedIdentity = identity;\n this.#ensureContainer().replaceChildren(card);\n }\n}\n","import { APP_VIEW_TAG_NAME, NoodleAppViewElement } from './app-view-element.js';\n\nexport {\n APP_VIEW_TAG_NAME,\n type AssistantAppViewErrorDetail,\n NoodleAppViewElement,\n} from './app-view-element.js';\nexport type { AssistantClient, AssistantViewAvailableDetail } from './client.js';\n\n/** Register the framework-neutral MCP App host. Safe to call more than once or during SSR. */\nexport function registerNoodleAppView(): void {\n if (!globalThis.customElements?.get(APP_VIEW_TAG_NAME)) {\n globalThis.customElements?.define(APP_VIEW_TAG_NAME, NoodleAppViewElement);\n }\n}\n\nregisterNoodleAppView();\n"],"mappings":"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","names":[]}
@@ -24,7 +24,7 @@ import {
24
24
  __require
25
25
  } from "./chunk-5FUTL2UF.js";
26
26
 
27
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
27
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
28
28
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
29
29
  var JSONRPC_VERSION = "2.0";
30
30
  var AssertObjectSchema = custom((v2) => v2 !== null && (typeof v2 === "object" || typeof v2 === "function"));
@@ -1541,7 +1541,7 @@ var UrlElicitationRequiredError = class extends McpError {
1541
1541
  }
1542
1542
  };
1543
1543
 
1544
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
1544
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
1545
1545
  function isZ4Schema(s2) {
1546
1546
  const schema = s2;
1547
1547
  return !!schema._zod;
@@ -1604,7 +1604,7 @@ function getLiteralValue(schema) {
1604
1604
  return void 0;
1605
1605
  }
1606
1606
 
1607
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
1607
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
1608
1608
  function isTerminal(status) {
1609
1609
  return status === "completed" || status === "failed" || status === "cancelled";
1610
1610
  }
@@ -1612,7 +1612,7 @@ function isTerminal(status) {
1612
1612
  // ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
1613
1613
  var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
1614
1614
 
1615
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
1615
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
1616
1616
  function getMethodLiteral(schema) {
1617
1617
  const shape = getObjectShape(schema);
1618
1618
  const methodSchema = shape?.method;
@@ -1633,7 +1633,7 @@ function parseWithCompat(schema, data) {
1633
1633
  return result.data;
1634
1634
  }
1635
1635
 
1636
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
1636
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
1637
1637
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
1638
1638
  var Protocol = class {
1639
1639
  constructor(_options) {
@@ -2744,6 +2744,15 @@ var E = class {
2744
2744
  sessionId;
2745
2745
  setProtocolVersion;
2746
2746
  };
2747
+ function AZ(X) {
2748
+ if (!X) return "";
2749
+ let Y = [];
2750
+ if (X.camera) Y.push("camera");
2751
+ if (X.microphone) Y.push("microphone");
2752
+ if (X.geolocation) Y.push("geolocation");
2753
+ if (X.clipboardWrite) Y.push("clipboard-write");
2754
+ return Y.join("; ");
2755
+ }
2747
2756
  var KX = [j];
2748
2757
  var DX = class extends _ {
2749
2758
  _client;
@@ -3014,34 +3023,131 @@ function GX(X, Y) {
3014
3023
  return JSON.stringify(X) === JSON.stringify(Y);
3015
3024
  }
3016
3025
 
3026
+ // src/app-sandbox-policy.ts
3027
+ var MAX_DOMAINS_PER_DIRECTIVE = 32;
3028
+ function isRecord(value) {
3029
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3030
+ }
3031
+ function isLoopback(hostname) {
3032
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
3033
+ }
3034
+ function normalizeDomain(value, connect) {
3035
+ if (value.length > 512 || /[;\r\n'"\s]/u.test(value)) return void 0;
3036
+ try {
3037
+ const parsed = new URL(value);
3038
+ const secure = parsed.protocol === "https:" || connect && parsed.protocol === "wss:";
3039
+ const local = isLoopback(parsed.hostname) && (parsed.protocol === "http:" || connect && parsed.protocol === "ws:");
3040
+ if (!secure && !local) return void 0;
3041
+ if (parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
3042
+ return void 0;
3043
+ }
3044
+ return parsed.origin;
3045
+ } catch {
3046
+ return void 0;
3047
+ }
3048
+ }
3049
+ function normalizeDomains(values, connect) {
3050
+ const normalized = [
3051
+ ...new Set(
3052
+ values.slice(0, MAX_DOMAINS_PER_DIRECTIVE).map((value) => typeof value === "string" ? normalizeDomain(value, connect) : void 0).filter((value) => value !== void 0)
3053
+ )
3054
+ ];
3055
+ return normalized;
3056
+ }
3057
+ function resolveAssistantAppSandboxPolicy(resourceMeta) {
3058
+ if (!isRecord(resourceMeta)) return {};
3059
+ const rawCsp = resourceMeta.csp;
3060
+ const csp = isRecord(rawCsp) ? {
3061
+ ...Array.isArray(rawCsp.connectDomains) ? { connectDomains: normalizeDomains(rawCsp.connectDomains, true) } : {},
3062
+ ...Array.isArray(rawCsp.resourceDomains) ? { resourceDomains: normalizeDomains(rawCsp.resourceDomains, false) } : {},
3063
+ ...Array.isArray(rawCsp.frameDomains) ? { frameDomains: normalizeDomains(rawCsp.frameDomains, false) } : {},
3064
+ ...Array.isArray(rawCsp.baseUriDomains) ? { baseUriDomains: normalizeDomains(rawCsp.baseUriDomains, false) } : {}
3065
+ } : void 0;
3066
+ const rawPermissions = resourceMeta.permissions;
3067
+ const permissions = isRecord(rawPermissions) ? {
3068
+ ...isRecord(rawPermissions.camera) ? { camera: {} } : {},
3069
+ ...isRecord(rawPermissions.microphone) ? { microphone: {} } : {},
3070
+ ...isRecord(rawPermissions.geolocation) ? { geolocation: {} } : {},
3071
+ ...isRecord(rawPermissions.clipboardWrite) ? { clipboardWrite: {} } : {}
3072
+ } : void 0;
3073
+ const allowAttribute = AZ(permissions);
3074
+ return {
3075
+ ...csp === void 0 ? {} : { csp },
3076
+ ...permissions === void 0 ? {} : { permissions },
3077
+ ...allowAttribute ? { allowAttribute } : {}
3078
+ };
3079
+ }
3080
+ function assistantAppSandboxUrl(sandboxUrl, policy) {
3081
+ const parsed = new URL(sandboxUrl);
3082
+ if (policy.csp) parsed.searchParams.set("csp", JSON.stringify(policy.csp));
3083
+ const parentOrigin = globalThis.location?.origin;
3084
+ if (parentOrigin && parentOrigin !== "null") {
3085
+ parsed.hash = new URLSearchParams({ parentOrigin }).toString();
3086
+ }
3087
+ return parsed.href;
3088
+ }
3089
+
3017
3090
  // src/app-host.ts
3018
- var PROXY_DOCUMENT = `<!doctype html><meta charset="utf-8"><style>html,body,iframe{border:0;margin:0;width:100%;height:100%;overflow:hidden}body{background:transparent}</style><script>
3019
- let inner;
3091
+ var PROXY_DOCUMENT = `<!doctype html><meta charset="utf-8"><style>html,body,iframe{border:0;margin:0;width:100%;height:100%;overflow:hidden}body{background:transparent}</style><body><script>
3092
+ let inner,loaded=false;
3093
+ if(self===top)throw new Error('The assistant sandbox relay must be embedded');
3094
+ const standardSandbox='allow-scripts allow-same-origin allow-forms';
3095
+ const rawParentOrigin=new URLSearchParams(location.hash.slice(1)).get('parentOrigin');
3096
+ let expectedParentOrigin;
3097
+ try{const parsed=new URL(rawParentOrigin||'');if(parsed.origin===rawParentOrigin&&/^https?:$/.test(parsed.protocol))expectedParentOrigin=parsed.origin}catch{}
3098
+ const parentTarget=expectedParentOrigin||'*';
3099
+ const ownOrigin=self.origin;
3020
3100
  const ready={jsonrpc:'2.0',method:'ui/notifications/sandbox-proxy-ready',params:{}};
3021
3101
  // Re-announce until the host acknowledges with the resource: when this document is hosted
3022
3102
  // cross-origin, the first announcement can arrive before the host's load handler is listening.
3023
- const announce=setInterval(()=>parent.postMessage(ready,'*'),120);
3103
+ const announce=setInterval(()=>parent.postMessage(ready,parentTarget),120);
3024
3104
  setTimeout(()=>clearInterval(announce),15000);
3025
3105
  addEventListener('message',(event)=>{
3026
3106
  if(event.source===parent){
3107
+ if(expectedParentOrigin&&event.origin!==expectedParentOrigin)return;
3027
3108
  const message=event.data;
3028
3109
  if(message?.method==='ui/notifications/sandbox-resource-ready'){
3029
3110
  clearInterval(announce);
3030
- if(inner)return;
3111
+ if(loaded)return;
3112
+ loaded=true;
3113
+ const requestedSandbox=message.params?.sandbox;
3031
3114
  inner=document.createElement('iframe');
3032
- inner.setAttribute('sandbox',message.params?.sandbox||'allow-scripts');
3115
+ inner.setAttribute('sandbox',requestedSandbox===standardSandbox?standardSandbox:'allow-scripts');
3033
3116
  inner.setAttribute('referrerpolicy','no-referrer');
3034
- inner.srcdoc=String(message.params?.html||'');
3035
- document.body.replaceChildren(inner);
3117
+ const permissions=message.params?.permissions;
3118
+ const allow=[];
3119
+ if(permissions?.camera)allow.push('camera');
3120
+ if(permissions?.microphone)allow.push('microphone');
3121
+ if(permissions?.geolocation)allow.push('geolocation');
3122
+ if(permissions?.clipboardWrite)allow.push('clipboard-write');
3123
+ if(allow.length)inner.setAttribute('allow',allow.join('; '));
3124
+ document.body.append(inner);
3125
+ let html=String(message.params?.html||'');
3126
+ const csp=message.params?.csp;
3127
+ if(csp&&typeof csp==='object'){
3128
+ const list=(value,fallback)=>Array.isArray(value)&&value.length?value.join(' '):fallback;
3129
+ const resources=Array.isArray(csp.resourceDomains)?csp.resourceDomains:[];
3130
+ const suffix=resources.length?' '+resources.join(' '):'';
3131
+ const policy=["default-src 'none'","script-src 'unsafe-inline'"+suffix,"style-src 'unsafe-inline'"+suffix,'img-src data: blob:'+suffix,'font-src data: blob:'+suffix,'media-src data: blob:'+suffix,'worker-src blob:'+suffix,'connect-src '+list(csp.connectDomains,"'none'"),'frame-src '+list(csp.frameDomains,"'none'"),"object-src 'none'","form-action 'none'",'base-uri '+list(csp.baseUriDomains,"'none'")].join('; ');
3132
+ const meta='<meta http-equiv="Content-Security-Policy" content="'+policy.replace(/&/g,'&amp;').replace(/"/g,'&quot;')+'">';
3133
+ const doctype=html.match(/^\\s*<!doctype[^>]*>/i);
3134
+ html=doctype?html.slice(0,doctype[0].length)+meta+html.slice(doctype[0].length):meta+html;
3135
+ }
3136
+ try{
3137
+ const doc=inner.contentDocument||inner.contentWindow?.document;
3138
+ if(!doc)throw new Error('document unavailable');
3139
+ doc.open();doc.write(html);doc.close();
3140
+ }catch{inner.srcdoc=html}
3036
3141
  return;
3037
3142
  }
3038
3143
  inner?.contentWindow?.postMessage(message,'*');
3039
3144
  } else if(inner && event.source===inner.contentWindow) {
3040
- parent.postMessage(event.data,'*');
3145
+ if(event.origin!==ownOrigin)return;
3146
+ parent.postMessage(event.data,parentTarget);
3041
3147
  }
3042
3148
  });
3043
- parent.postMessage(ready,'*');
3044
- </script>`;
3149
+ parent.postMessage(ready,parentTarget);
3150
+ </script></body>`;
3045
3151
  function isAllowedAppLink(value, allowedDomains, baseUrl) {
3046
3152
  try {
3047
3153
  const parsed = new URL(value, baseUrl);
@@ -3068,16 +3174,42 @@ function createAssistantAppHostContext(theme, allowFullscreen = false, displayMo
3068
3174
  }
3069
3175
  var APP_RENDER_TIMEOUT_MS = 1e4;
3070
3176
  var APP_TEARDOWN_TIMEOUT_MS = 1e3;
3177
+ var STANDARD_SANDBOX = "allow-scripts allow-same-origin allow-forms";
3071
3178
  var mountedApps = /* @__PURE__ */ new WeakMap();
3072
3179
  function resolveSandboxUrl(value) {
3073
3180
  if (!value) return void 0;
3074
3181
  try {
3075
3182
  const parsed = new URL(value);
3076
- return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.href : void 0;
3183
+ const loopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]";
3184
+ const permittedProtocol = parsed.protocol === "https:" || parsed.protocol === "http:" && loopback;
3185
+ const embedderOrigin = globalThis.location?.origin;
3186
+ if (!permittedProtocol || embedderOrigin && embedderOrigin !== "null" && parsed.origin === embedderOrigin) {
3187
+ return void 0;
3188
+ }
3189
+ return parsed.href;
3077
3190
  } catch {
3078
3191
  return void 0;
3079
3192
  }
3080
3193
  }
3194
+ function copyAppJson(value, depth = 0) {
3195
+ if (depth > 64) throw new Error("App payload nesting exceeds the host limit");
3196
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
3197
+ if (typeof value === "number") {
3198
+ if (!Number.isFinite(value)) throw new Error("App payload numbers must be finite");
3199
+ return value;
3200
+ }
3201
+ if (Array.isArray(value)) return value.map((entry) => copyAppJson(entry, depth + 1));
3202
+ const copied = {};
3203
+ for (const [key, entry] of Object.entries(value)) {
3204
+ Object.defineProperty(copied, key, {
3205
+ value: copyAppJson(entry, depth + 1),
3206
+ enumerable: true,
3207
+ configurable: true,
3208
+ writable: true
3209
+ });
3210
+ }
3211
+ return copied;
3212
+ }
3081
3213
  function mountAssistantApp(detail, actions, options = {}) {
3082
3214
  if (!detail.html) return void 0;
3083
3215
  const card = document.createElement("section");
@@ -3098,16 +3230,25 @@ function mountAssistantApp(detail, actions, options = {}) {
3098
3230
  const frame = document.createElement("iframe");
3099
3231
  frame.className = "noodle-app-frame";
3100
3232
  frame.title = detail.title ?? detail.tool;
3101
- frame.setAttribute("sandbox", "allow-scripts");
3233
+ const policy = resolveAssistantAppSandboxPolicy(detail.resourceMeta);
3102
3234
  frame.setAttribute("referrerpolicy", "no-referrer");
3103
3235
  const sandboxUrl = resolveSandboxUrl(options.sandboxUrl);
3104
- if (sandboxUrl) frame.src = sandboxUrl;
3105
- else frame.srcdoc = PROXY_DOCUMENT;
3236
+ const appliedPolicy = sandboxUrl ? policy : { csp: policy.csp ?? {} };
3237
+ if (sandboxUrl) {
3238
+ frame.setAttribute("sandbox", STANDARD_SANDBOX);
3239
+ if (policy.allowAttribute) frame.setAttribute("allow", policy.allowAttribute);
3240
+ frame.src = assistantAppSandboxUrl(sandboxUrl, policy);
3241
+ } else {
3242
+ frame.setAttribute("sandbox", "allow-scripts");
3243
+ frame.srcdoc = PROXY_DOCUMENT;
3244
+ }
3106
3245
  card.append(frame);
3107
3246
  let mountedBridge;
3108
3247
  let mountedTransport;
3109
3248
  let destroyed = false;
3110
3249
  let initialized = false;
3250
+ let renderFailed = false;
3251
+ let renderStage = "sandbox_load";
3111
3252
  let currentTheme = actions.theme;
3112
3253
  let displayMode = "inline";
3113
3254
  let hostContext = createAssistantAppHostContext(
@@ -3116,6 +3257,15 @@ function mountAssistantApp(detail, actions, options = {}) {
3116
3257
  displayMode
3117
3258
  );
3118
3259
  let destroyPromise;
3260
+ let transportClosePromise;
3261
+ let renderTimer;
3262
+ const closeTransport = () => {
3263
+ if (transportClosePromise) return transportClosePromise;
3264
+ const closing = mountedTransport?.close().catch(() => {
3265
+ }) ?? Promise.resolve();
3266
+ transportClosePromise = closing;
3267
+ return closing;
3268
+ };
3119
3269
  const applyDisplayMode = (requestedMode) => {
3120
3270
  displayMode = requestedMode === "fullscreen" && options.allowFullscreen === true ? "fullscreen" : "inline";
3121
3271
  card.toggleAttribute("data-fullscreen", displayMode === "fullscreen");
@@ -3133,18 +3283,26 @@ function mountAssistantApp(detail, actions, options = {}) {
3133
3283
  applyDisplayMode("inline");
3134
3284
  frame.focus();
3135
3285
  });
3136
- const renderTimer = setTimeout(() => {
3137
- if (initialized || !frame.isConnected) return;
3286
+ const showRenderFailure = (code) => {
3287
+ if (code === "view_render_timeout" && initialized || destroyed || renderFailed || !frame.isConnected)
3288
+ return;
3289
+ renderFailed = true;
3290
+ clearTimeout(renderTimer);
3138
3291
  const note = document.createElement("div");
3139
3292
  note.className = "noodle-app-fallback";
3140
3293
  note.textContent = "This app view could not be displayed.";
3141
3294
  frame.replaceWith(note);
3142
- options.onRenderFailure?.({ code: "view_render_timeout", retryable: false });
3295
+ options.onRenderFailure?.({ code, retryable: false, stage: renderStage });
3296
+ void closeTransport();
3297
+ };
3298
+ renderTimer = setTimeout(() => {
3299
+ showRenderFailure("view_render_timeout");
3143
3300
  }, APP_RENDER_TIMEOUT_MS);
3144
3301
  frame.addEventListener(
3145
3302
  "load",
3146
3303
  () => {
3147
- if (destroyed) return;
3304
+ if (destroyed || renderFailed) return;
3305
+ renderStage = "sandbox_proxy";
3148
3306
  const target = frame.contentWindow;
3149
3307
  if (!target) return;
3150
3308
  const bridge = new DX(
@@ -3155,7 +3313,11 @@ function mountAssistantApp(detail, actions, options = {}) {
3155
3313
  serverTools: {},
3156
3314
  serverResources: {},
3157
3315
  updateModelContext: { text: {}, structuredContent: {} },
3158
- message: { text: {} }
3316
+ message: { text: {} },
3317
+ sandbox: {
3318
+ ...appliedPolicy.csp === void 0 ? {} : { csp: appliedPolicy.csp },
3319
+ ...appliedPolicy.permissions === void 0 ? {} : { permissions: appliedPolicy.permissions }
3320
+ }
3159
3321
  },
3160
3322
  {
3161
3323
  hostContext
@@ -3163,13 +3325,15 @@ function mountAssistantApp(detail, actions, options = {}) {
3163
3325
  );
3164
3326
  mountedBridge = bridge;
3165
3327
  bridge.onsandboxready = () => {
3166
- if (destroyed) return;
3328
+ if (destroyed || renderFailed) return;
3329
+ renderStage = "app_initialize";
3167
3330
  const current = options.getDetail?.() ?? detail;
3168
3331
  void bridge.sendSandboxResourceReady({
3169
3332
  html: current.html,
3170
- sandbox: "allow-scripts",
3171
- ...current.resourceMeta ?? {}
3172
- });
3333
+ sandbox: sandboxUrl ? STANDARD_SANDBOX : "allow-scripts",
3334
+ ...appliedPolicy.csp === void 0 ? {} : { csp: appliedPolicy.csp },
3335
+ ...appliedPolicy.permissions === void 0 ? {} : { permissions: appliedPolicy.permissions }
3336
+ }).catch(() => showRenderFailure("view_bridge_error"));
3173
3337
  };
3174
3338
  bridge.onsizechange = ({ height }) => {
3175
3339
  if (destroyed) return;
@@ -3233,21 +3397,30 @@ function mountAssistantApp(detail, actions, options = {}) {
3233
3397
  return await actions.client.requestApp("resources/read", params);
3234
3398
  };
3235
3399
  bridge.oninitialized = () => {
3236
- if (destroyed) return;
3237
- initialized = true;
3238
- clearTimeout(renderTimer);
3239
- bridge.setHostContext(hostContext);
3240
- const current = options.getDetail?.() ?? detail;
3241
- void bridge.sendToolInput({ arguments: current.arguments ?? {} });
3242
- void bridge.sendToolResult({
3243
- content: [{ type: "text", text: JSON.stringify(current.result) }],
3244
- ...typeof current.result === "object" && current.result !== null && !Array.isArray(current.result) ? { structuredContent: current.result } : {}
3245
- });
3400
+ if (destroyed || renderFailed) return;
3401
+ try {
3402
+ const current = options.getDetail?.() ?? detail;
3403
+ const copiedArguments = copyAppJson(current.arguments ?? {});
3404
+ const copiedResult = copyAppJson(current.result);
3405
+ const argumentsRecord = typeof copiedArguments === "object" && copiedArguments !== null && !Array.isArray(copiedArguments) ? copiedArguments : {};
3406
+ initialized = true;
3407
+ clearTimeout(renderTimer);
3408
+ bridge.setHostContext(hostContext);
3409
+ void Promise.all([
3410
+ bridge.sendToolInput({ arguments: argumentsRecord }),
3411
+ bridge.sendToolResult({
3412
+ content: [{ type: "text", text: JSON.stringify(copiedResult) }],
3413
+ ...typeof copiedResult === "object" && copiedResult !== null && !Array.isArray(copiedResult) ? { structuredContent: copiedResult } : {}
3414
+ })
3415
+ ]).catch(() => showRenderFailure("view_bridge_error"));
3416
+ } catch {
3417
+ showRenderFailure("view_bridge_error");
3418
+ }
3246
3419
  };
3247
3420
  const transport = new E(target, target);
3248
3421
  mountedTransport = transport;
3249
3422
  void bridge.connect(transport).catch(() => {
3250
- void destroy();
3423
+ showRenderFailure("view_bridge_error");
3251
3424
  });
3252
3425
  },
3253
3426
  { once: true }
@@ -3273,8 +3446,7 @@ function mountAssistantApp(detail, actions, options = {}) {
3273
3446
  }
3274
3447
  } catch {
3275
3448
  }
3276
- await mountedTransport?.close().catch(() => {
3277
- });
3449
+ await closeTransport();
3278
3450
  card.remove();
3279
3451
  mountedApps.delete(card);
3280
3452
  })();
@@ -3320,4 +3492,4 @@ export {
3320
3492
  ASSISTANT_APP_VIEW_CSS,
3321
3493
  ASSISTANT_APP_VIEW_STYLES
3322
3494
  };
3323
- //# sourceMappingURL=chunk-XKTUXFGX.js.map
3495
+ //# sourceMappingURL=chunk-P32MAR4K.js.map