@noodleseed/assistant 1.5.0 → 1.7.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.
package/dist/index.cjs CHANGED
@@ -18195,10 +18195,17 @@ function GX(X2, Y) {
18195
18195
  // src/app-host.ts
18196
18196
  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>
18197
18197
  let inner;
18198
+ const ready={jsonrpc:'2.0',method:'ui/notifications/sandbox-proxy-ready',params:{}};
18199
+ // Re-announce until the host acknowledges with the resource: when this document is hosted
18200
+ // cross-origin, the first announcement can arrive before the host's load handler is listening.
18201
+ const announce=setInterval(()=>parent.postMessage(ready,'*'),120);
18202
+ setTimeout(()=>clearInterval(announce),15000);
18198
18203
  addEventListener('message',(event)=>{
18199
18204
  if(event.source===parent){
18200
18205
  const message=event.data;
18201
18206
  if(message?.method==='ui/notifications/sandbox-resource-ready'){
18207
+ clearInterval(announce);
18208
+ if(inner)return;
18202
18209
  inner=document.createElement('iframe');
18203
18210
  inner.setAttribute('sandbox',message.params?.sandbox||'allow-scripts');
18204
18211
  inner.setAttribute('referrerpolicy','no-referrer');
@@ -18211,7 +18218,7 @@ addEventListener('message',(event)=>{
18211
18218
  parent.postMessage(event.data,'*');
18212
18219
  }
18213
18220
  });
18214
- parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/sandbox-proxy-ready',params:{}},'*');
18221
+ parent.postMessage(ready,'*');
18215
18222
  </script>`;
18216
18223
  function isAllowedAppLink(value, allowedDomains, baseUrl) {
18217
18224
  try {
@@ -18229,7 +18236,17 @@ function isAllowedAppLink(value, allowedDomains, baseUrl) {
18229
18236
  return void 0;
18230
18237
  }
18231
18238
  }
18232
- function mountAssistantApp(detail, actions) {
18239
+ var APP_RENDER_TIMEOUT_MS = 1e4;
18240
+ function resolveSandboxUrl(value) {
18241
+ if (!value) return void 0;
18242
+ try {
18243
+ const parsed = new URL(value);
18244
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.href : void 0;
18245
+ } catch {
18246
+ return void 0;
18247
+ }
18248
+ }
18249
+ function mountAssistantApp(detail, actions, options = {}) {
18233
18250
  if (!detail.html) return void 0;
18234
18251
  const card = document.createElement("section");
18235
18252
  card.className = "noodle-app-card";
@@ -18244,8 +18261,19 @@ function mountAssistantApp(detail, actions) {
18244
18261
  frame.title = detail.title ?? detail.tool;
18245
18262
  frame.setAttribute("sandbox", "allow-scripts");
18246
18263
  frame.setAttribute("referrerpolicy", "no-referrer");
18247
- frame.srcdoc = PROXY_DOCUMENT;
18264
+ const sandboxUrl = resolveSandboxUrl(options.sandboxUrl);
18265
+ if (sandboxUrl) frame.src = sandboxUrl;
18266
+ else frame.srcdoc = PROXY_DOCUMENT;
18248
18267
  card.append(frame);
18268
+ let initialized = false;
18269
+ const renderTimer = setTimeout(() => {
18270
+ if (initialized || !frame.isConnected) return;
18271
+ const note = document.createElement("div");
18272
+ note.className = "noodle-app-fallback";
18273
+ note.textContent = "This app view could not be displayed.";
18274
+ frame.replaceWith(note);
18275
+ options.onRenderFailure?.({ code: "view_render_timeout", retryable: false });
18276
+ }, APP_RENDER_TIMEOUT_MS);
18249
18277
  frame.addEventListener(
18250
18278
  "load",
18251
18279
  () => {
@@ -18311,13 +18339,18 @@ function mountAssistantApp(detail, actions) {
18311
18339
  bridge.onlistresources = async (params) => await actions.client.requestApp("resources/list", params ?? {});
18312
18340
  bridge.onreadresource = async (params) => await actions.client.requestApp("resources/read", params);
18313
18341
  bridge.oninitialized = () => {
18342
+ initialized = true;
18343
+ clearTimeout(renderTimer);
18314
18344
  void bridge.sendToolInput({ arguments: detail.arguments ?? {} });
18315
18345
  void bridge.sendToolResult({
18316
18346
  content: [{ type: "text", text: JSON.stringify(detail.result) }],
18317
18347
  ...typeof detail.result === "object" && detail.result !== null && !Array.isArray(detail.result) ? { structuredContent: detail.result } : {}
18318
18348
  });
18319
18349
  };
18320
- void bridge.connect(new E(target, target)).catch(() => card.remove());
18350
+ void bridge.connect(new E(target, target)).catch(() => {
18351
+ clearTimeout(renderTimer);
18352
+ card.remove();
18353
+ });
18321
18354
  },
18322
18355
  { once: true }
18323
18356
  );
@@ -19067,6 +19100,9 @@ var DefaultAssistantClient = class {
19067
19100
  }
19068
19101
  return response.json();
19069
19102
  }
19103
+ appSandboxUrl() {
19104
+ return this.#session?.endpoints.sandbox;
19105
+ }
19070
19106
  resetSession() {
19071
19107
  this.#session = void 0;
19072
19108
  this.#emit({ event: "session_reset", data: {} });
@@ -19268,7 +19304,8 @@ function parseSession(value) {
19268
19304
  }
19269
19305
  const interactions = value.endpoints.interactions;
19270
19306
  const apps = value.endpoints.apps;
19271
- if (typeof value.token !== "string" || typeof value.expiresAt !== "string" || typeof value.endpoints.turns !== "string" || typeof value.endpoints.toolConfirmations !== "string" || interactions !== void 0 && typeof interactions !== "string" || apps !== void 0 && typeof apps !== "string") {
19307
+ const sandbox = value.endpoints.sandbox;
19308
+ if (typeof value.token !== "string" || typeof value.expiresAt !== "string" || typeof value.endpoints.turns !== "string" || typeof value.endpoints.toolConfirmations !== "string" || interactions !== void 0 && typeof interactions !== "string" || apps !== void 0 && typeof apps !== "string" || sandbox !== void 0 && typeof sandbox !== "string") {
19272
19309
  throw clientError("session_failed", "Assistant session response is invalid", true);
19273
19310
  }
19274
19311
  return {
@@ -19278,7 +19315,8 @@ function parseSession(value) {
19278
19315
  turns: value.endpoints.turns,
19279
19316
  toolConfirmations: value.endpoints.toolConfirmations,
19280
19317
  ...typeof interactions === "string" ? { interactions } : {},
19281
- ...typeof apps === "string" ? { apps } : {}
19318
+ ...typeof apps === "string" ? { apps } : {},
19319
+ ...typeof sandbox === "string" ? { sandbox } : {}
19282
19320
  },
19283
19321
  ...isRecord3(value.configuration) ? { configuration: value.configuration } : {}
19284
19322
  };
@@ -22746,6 +22784,7 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
22746
22784
  .noodle-app-card { position: relative; display: grid; gap: 8px; width: 100%; margin: 4px 0 16px; overflow: hidden; color: var(--ns-assistant-app-text, inherit); border: 1px solid var(--ns-assistant-app-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-card-radius, var(--ns-assistant-default-card-radius)); background: var(--ns-assistant-app, var(--ns-assistant-elevated, var(--ns-assistant-default-elevated))); }
22747
22785
  .noodle-app-title { padding: 10px 12px 0; font-size: .86em; font-weight: 650; }
22748
22786
  .noodle-app-frame { display: block; width: 100%; min-height: 120px; border: 0; background: transparent; }
22787
+ .noodle-app-fallback { padding: 14px 16px; font-size: 13px; color: var(--ns-assistant-muted, var(--ns-assistant-default-muted, #6b7280)); }
22749
22788
  .noodle-app-card[data-fullscreen] { position: fixed; inset: 0; z-index: 2; margin: 0; border-radius: 0; background: var(--ns-assistant-panel, var(--ns-assistant-default-panel)); }
22750
22789
  .noodle-app-card[data-fullscreen] .noodle-app-frame { height: 100% !important; }
22751
22790
  form { display: flex; flex: 0 0 auto; gap: 8px; align-items: center; min-height: 56px; margin: 8px 16px 16px; padding: 6px 6px 6px 16px; color: var(--ns-assistant-composer-text, inherit); border: 1px solid var(--ns-assistant-composer-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-input-radius, 14px); background: var(--ns-assistant-composer, var(--ns-assistant-input, var(--ns-assistant-default-input))); box-shadow: 0 1px 2px rgba(0,0,0,.04); transition: border-color .15s ease, box-shadow .15s ease; }
@@ -23311,11 +23350,19 @@ var NoodleAssistantElement = class extends HTMLElementBase {
23311
23350
  const messages = this.#messages;
23312
23351
  const client = this.#client;
23313
23352
  if (!messages || !client) return;
23314
- const view = mountAssistantApp(detail, {
23315
- client,
23316
- sendMessage: (message) => this.sendMessage(message),
23317
- updateModelContext: (update) => this.updateModelContext(update)
23318
- });
23353
+ const sandboxUrl = client.appSandboxUrl?.();
23354
+ const view = mountAssistantApp(
23355
+ detail,
23356
+ {
23357
+ client,
23358
+ sendMessage: (message) => this.sendMessage(message),
23359
+ updateModelContext: (update) => this.updateModelContext(update)
23360
+ },
23361
+ {
23362
+ ...sandboxUrl === void 0 ? {} : { sandboxUrl },
23363
+ onRenderFailure: (failure) => this.#dispatchError(failure)
23364
+ }
23365
+ );
23319
23366
  if (!view) return;
23320
23367
  messages.append(view);
23321
23368
  this.setAttribute("has-messages", "");