@noodleseed/assistant 1.8.0 → 1.9.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/README.md +33 -7
- package/dist/{appearance-DowzFJjh.d.cts → appearance-Cc-eqSbp.d.cts} +1 -1
- package/dist/{appearance-DowzFJjh.d.ts → appearance-Cc-eqSbp.d.ts} +1 -1
- package/dist/{chunk-JMBUJSFP.js → chunk-4B3XXGRP.js} +74 -13
- package/dist/chunk-4B3XXGRP.js.map +1 -0
- package/dist/{client-ERQCAH_0.d.ts → client-B803xZVn.d.ts} +2 -2
- package/dist/{client-BxoNZkWp.d.cts → client-CsmtpD1z.d.cts} +2 -2
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/index.cjs +68 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/react/client.d.cts +2 -2
- package/dist/react/client.d.ts +2 -2
- package/dist/react.cjs +124 -19
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +22 -4
- package/dist/react.d.ts +22 -4
- package/dist/react.js +58 -9
- package/dist/react.js.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-JMBUJSFP.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Customer-branded embedded assistant surfaces for Noodle Seed deployments.
|
|
4
4
|
|
|
5
|
-
The package exports the canonical `<noodle-assistant>` Web Component, a managed React wrapper
|
|
6
|
-
`@noodleseed/assistant/react`, a renderer-free React hook from
|
|
5
|
+
The package exports the canonical `<noodle-assistant>` Web Component, a managed React wrapper and secure
|
|
6
|
+
`NoodleAppView` MCP App renderer from `@noodleseed/assistant/react`, a renderer-free React hook from
|
|
7
7
|
`@noodleseed/assistant/react/client`, a DOM-free client from `@noodleseed/assistant/client`, and the
|
|
8
8
|
backend-only `createAssistantSession` helper from `@noodleseed/assistant/server`. Light, dark, and automatic
|
|
9
9
|
themes work without configuration; the component inherits the deployed MCP server's brand kit while slots,
|
|
@@ -247,6 +247,30 @@ interaction streams a continuation. `principalKey` is browser-local and never se
|
|
|
247
247
|
whenever the authenticated user or tenant changes so the hook aborts and clears the previous session and
|
|
248
248
|
transcript.
|
|
249
249
|
|
|
250
|
+
When the customer-owned transcript should render the linked MCP App itself, pass the typed `data-view`
|
|
251
|
+
payload and the same client to the supported host component:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { NoodleAppView } from "@noodleseed/assistant/react";
|
|
255
|
+
|
|
256
|
+
if (part.type === "data-view") {
|
|
257
|
+
return (
|
|
258
|
+
<NoodleAppView
|
|
259
|
+
key={`${part.data.id}:${part.data.resourceUri}`}
|
|
260
|
+
client={client}
|
|
261
|
+
view={part.data}
|
|
262
|
+
theme="dark"
|
|
263
|
+
onError={(failure) => reportAssistantError(failure)}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`NoodleAppView` owns the double iframe and AppBridge. Its lifecycle identity is the supplied client plus
|
|
270
|
+
`view.id` plus `view.resourceUri`: ordinary parent rerenders and fresh view/callback objects retain the
|
|
271
|
+
iframe, while a semantic view replacement or unmount requests standard App teardown and closes the bridge.
|
|
272
|
+
Do not also key an ancestor by the whole view object or a callback.
|
|
273
|
+
|
|
250
274
|
Outside React, subscribe to the DOM-free AI SDK `UIMessage` state without registering a custom element or
|
|
251
275
|
touching browser storage:
|
|
252
276
|
|
|
@@ -331,11 +355,13 @@ leaves the same input interaction pending so the renderer can submit a corrected
|
|
|
331
355
|
flow collects all elicited input before its first connector operation.
|
|
332
356
|
|
|
333
357
|
A completed widget-linked tool emits typed `view_available` data with its call/interaction id, tool,
|
|
334
|
-
`ui://` resource identity, optional title,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
358
|
+
`ui://` resource identity, optional title, bounded/redacted public result, and—on current services—the
|
|
359
|
+
self-contained App document. This is an availability signal, not proof of rendering. A customer-owned React
|
|
360
|
+
renderer either mounts the actual App with `NoodleAppView` or deliberately substitutes a component already
|
|
361
|
+
trusted by the application and selected by `resourceUri`/tool. The JSON `result` is data for a native
|
|
362
|
+
component; serializing it is not a rendering of the linked App. Never fetch the `ui://` URI, inject
|
|
363
|
+
`part.data.html`, or assign it to `srcdoc` yourself. The standard element also forwards the same detail as a
|
|
364
|
+
DOM event:
|
|
339
365
|
|
|
340
366
|
```ts
|
|
341
367
|
element.addEventListener("assistant-view-available", (event) => {
|
|
@@ -3072,6 +3072,8 @@ function createAssistantAppHostContext(theme) {
|
|
|
3072
3072
|
};
|
|
3073
3073
|
}
|
|
3074
3074
|
var APP_RENDER_TIMEOUT_MS = 1e4;
|
|
3075
|
+
var APP_TEARDOWN_TIMEOUT_MS = 1e3;
|
|
3076
|
+
var mountedApps = /* @__PURE__ */ new WeakMap();
|
|
3075
3077
|
function resolveSandboxUrl(value) {
|
|
3076
3078
|
if (!value) return void 0;
|
|
3077
3079
|
try {
|
|
@@ -3100,7 +3102,11 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3100
3102
|
if (sandboxUrl) frame.src = sandboxUrl;
|
|
3101
3103
|
else frame.srcdoc = PROXY_DOCUMENT;
|
|
3102
3104
|
card.append(frame);
|
|
3105
|
+
let mountedBridge;
|
|
3106
|
+
let mountedTransport;
|
|
3107
|
+
let destroyed = false;
|
|
3103
3108
|
let initialized = false;
|
|
3109
|
+
let destroyPromise;
|
|
3104
3110
|
const renderTimer = setTimeout(() => {
|
|
3105
3111
|
if (initialized || !frame.isConnected) return;
|
|
3106
3112
|
const note = document.createElement("div");
|
|
@@ -3112,6 +3118,7 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3112
3118
|
frame.addEventListener(
|
|
3113
3119
|
"load",
|
|
3114
3120
|
() => {
|
|
3121
|
+
if (destroyed) return;
|
|
3115
3122
|
const target = frame.contentWindow;
|
|
3116
3123
|
if (!target) return;
|
|
3117
3124
|
const bridge = new DX(
|
|
@@ -3128,19 +3135,24 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3128
3135
|
hostContext: createAssistantAppHostContext(actions.theme)
|
|
3129
3136
|
}
|
|
3130
3137
|
);
|
|
3138
|
+
mountedBridge = bridge;
|
|
3131
3139
|
bridge.onsandboxready = () => {
|
|
3140
|
+
if (destroyed) return;
|
|
3141
|
+
const current = options.getDetail?.() ?? detail;
|
|
3132
3142
|
void bridge.sendSandboxResourceReady({
|
|
3133
|
-
html:
|
|
3143
|
+
html: current.html,
|
|
3134
3144
|
sandbox: "allow-scripts allow-forms",
|
|
3135
|
-
...
|
|
3145
|
+
...current.resourceMeta ?? {}
|
|
3136
3146
|
});
|
|
3137
3147
|
};
|
|
3138
3148
|
bridge.onsizechange = ({ height }) => {
|
|
3149
|
+
if (destroyed) return;
|
|
3139
3150
|
if (typeof height === "number" && Number.isFinite(height)) {
|
|
3140
3151
|
frame.style.height = `${Math.min(1200, Math.max(120, Math.ceil(height)))}px`;
|
|
3141
3152
|
}
|
|
3142
3153
|
};
|
|
3143
3154
|
bridge.onmessage = async ({ content }) => {
|
|
3155
|
+
if (destroyed) return {};
|
|
3144
3156
|
const text2 = content.flatMap(
|
|
3145
3157
|
(part) => part.type === "text" && typeof part.text === "string" ? [part.text] : []
|
|
3146
3158
|
).join("\n").trim();
|
|
@@ -3148,13 +3160,16 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3148
3160
|
return {};
|
|
3149
3161
|
};
|
|
3150
3162
|
bridge.onupdatemodelcontext = async (update) => {
|
|
3163
|
+
if (destroyed) return {};
|
|
3151
3164
|
actions.updateModelContext(update);
|
|
3152
3165
|
return {};
|
|
3153
3166
|
};
|
|
3154
3167
|
bridge.onopenlink = async ({ url }) => {
|
|
3168
|
+
if (destroyed) return { isError: true };
|
|
3169
|
+
const current = options.getDetail?.() ?? detail;
|
|
3155
3170
|
const allowed = isAllowedAppLink(
|
|
3156
3171
|
url,
|
|
3157
|
-
|
|
3172
|
+
current.allowedOpenDomains ?? [],
|
|
3158
3173
|
globalThis.location?.href ?? "https://invalid.example"
|
|
3159
3174
|
);
|
|
3160
3175
|
if (!allowed) return { isError: true };
|
|
@@ -3163,30 +3178,74 @@ function mountAssistantApp(detail, actions, options = {}) {
|
|
|
3163
3178
|
};
|
|
3164
3179
|
bridge.onrequestdisplaymode = async ({ mode }) => {
|
|
3165
3180
|
const accepted = mode === "fullscreen" ? "fullscreen" : "inline";
|
|
3181
|
+
if (destroyed) return { mode: accepted };
|
|
3166
3182
|
card.toggleAttribute("data-fullscreen", accepted === "fullscreen");
|
|
3167
3183
|
return { mode: accepted };
|
|
3168
3184
|
};
|
|
3169
|
-
bridge.oncalltool = async (params) =>
|
|
3170
|
-
|
|
3171
|
-
|
|
3185
|
+
bridge.oncalltool = async (params) => {
|
|
3186
|
+
if (destroyed) {
|
|
3187
|
+
return {
|
|
3188
|
+
content: [{ type: "text", text: "App view is closing." }],
|
|
3189
|
+
isError: true
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
return await actions.client.requestApp("tools/call", params);
|
|
3193
|
+
};
|
|
3194
|
+
bridge.onlistresources = async (params) => {
|
|
3195
|
+
if (destroyed) {
|
|
3196
|
+
return { resources: [] };
|
|
3197
|
+
}
|
|
3198
|
+
return await actions.client.requestApp("resources/list", params ?? {});
|
|
3199
|
+
};
|
|
3200
|
+
bridge.onreadresource = async (params) => {
|
|
3201
|
+
if (destroyed) {
|
|
3202
|
+
return { contents: [] };
|
|
3203
|
+
}
|
|
3204
|
+
return await actions.client.requestApp("resources/read", params);
|
|
3205
|
+
};
|
|
3172
3206
|
bridge.oninitialized = () => {
|
|
3207
|
+
if (destroyed) return;
|
|
3173
3208
|
initialized = true;
|
|
3174
3209
|
clearTimeout(renderTimer);
|
|
3175
|
-
|
|
3210
|
+
const current = options.getDetail?.() ?? detail;
|
|
3211
|
+
void bridge.sendToolInput({ arguments: current.arguments ?? {} });
|
|
3176
3212
|
void bridge.sendToolResult({
|
|
3177
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
3178
|
-
...typeof
|
|
3213
|
+
content: [{ type: "text", text: JSON.stringify(current.result) }],
|
|
3214
|
+
...typeof current.result === "object" && current.result !== null && !Array.isArray(current.result) ? { structuredContent: current.result } : {}
|
|
3179
3215
|
});
|
|
3180
3216
|
};
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3217
|
+
const transport = new E(target, target);
|
|
3218
|
+
mountedTransport = transport;
|
|
3219
|
+
void bridge.connect(transport).catch(() => {
|
|
3220
|
+
void destroy();
|
|
3184
3221
|
});
|
|
3185
3222
|
},
|
|
3186
3223
|
{ once: true }
|
|
3187
3224
|
);
|
|
3225
|
+
const destroy = () => {
|
|
3226
|
+
if (destroyPromise) return destroyPromise;
|
|
3227
|
+
destroyed = true;
|
|
3228
|
+
clearTimeout(renderTimer);
|
|
3229
|
+
destroyPromise = (async () => {
|
|
3230
|
+
try {
|
|
3231
|
+
if (initialized) {
|
|
3232
|
+
await mountedBridge?.teardownResource({}, { timeout: APP_TEARDOWN_TIMEOUT_MS });
|
|
3233
|
+
}
|
|
3234
|
+
} catch {
|
|
3235
|
+
}
|
|
3236
|
+
await mountedTransport?.close().catch(() => {
|
|
3237
|
+
});
|
|
3238
|
+
card.remove();
|
|
3239
|
+
mountedApps.delete(card);
|
|
3240
|
+
})();
|
|
3241
|
+
return destroyPromise;
|
|
3242
|
+
};
|
|
3243
|
+
mountedApps.set(card, { destroy });
|
|
3188
3244
|
return card;
|
|
3189
3245
|
}
|
|
3246
|
+
function unmountAssistantApp(card) {
|
|
3247
|
+
return mountedApps.get(card)?.destroy() ?? Promise.resolve();
|
|
3248
|
+
}
|
|
3190
3249
|
|
|
3191
3250
|
// src/appearance.ts
|
|
3192
3251
|
var common = {
|
|
@@ -8136,6 +8195,8 @@ function registerNoodleAssistant() {
|
|
|
8136
8195
|
}
|
|
8137
8196
|
|
|
8138
8197
|
export {
|
|
8198
|
+
mountAssistantApp,
|
|
8199
|
+
unmountAssistantApp,
|
|
8139
8200
|
ASSISTANT_TAG_NAME,
|
|
8140
8201
|
NoodleAssistantElement,
|
|
8141
8202
|
registerNoodleAssistant
|
|
@@ -8145,4 +8206,4 @@ export {
|
|
|
8145
8206
|
dompurify/dist/purify.es.mjs:
|
|
8146
8207
|
(*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE *)
|
|
8147
8208
|
*/
|
|
8148
|
-
//# sourceMappingURL=chunk-
|
|
8209
|
+
//# sourceMappingURL=chunk-4B3XXGRP.js.map
|