@noodleseed/assistant 1.28.0 → 1.29.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/{app-view-element-mXHvcAXc.d.ts → app-view-element-kR8eqhyD.d.cts} +1 -1
- package/dist/{app-view-element-BjRkwNN1.d.cts → app-view-element-nY1svwHT.d.ts} +1 -1
- package/dist/app-view.d.cts +3 -3
- package/dist/app-view.d.ts +3 -3
- package/dist/{chunk-WJV4MJUR.js → chunk-3VZ3M5BE.js} +92 -9
- package/dist/{chunk-WJV4MJUR.js.map → chunk-3VZ3M5BE.js.map} +1 -1
- package/dist/{chunk-QH7PL3GU.js → chunk-DOWFW3KA.js} +259 -54
- package/dist/chunk-DOWFW3KA.js.map +1 -0
- package/dist/{client-CPFw8tfo.d.ts → client-contract-Bw9dtrhw.d.cts} +26 -35
- package/dist/{client-_xivfs4q.d.cts → client-contract-DjU9qsoP.d.ts} +26 -35
- package/dist/client.cjs +91 -8
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +12 -2
- package/dist/client.d.ts +12 -2
- package/dist/client.js +1 -1
- package/dist/embed.global.js +46 -46
- package/dist/index.cjs +348 -60
- 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 +2 -2
- package/dist/react/client.cjs +91 -8
- package/dist/react/client.cjs.map +1 -1
- package/dist/react/client.d.cts +2 -2
- package/dist/react/client.d.ts +2 -2
- package/dist/react/client.js +1 -1
- package/dist/react.cjs +348 -60
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +3 -3
- package/dist/react.d.ts +3 -3
- package/dist/react.js +2 -2
- package/dist/server-CMqlAnxs.d.cts +270 -0
- package/dist/server-CMqlAnxs.d.ts +270 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -121
- package/dist/server.d.ts +1 -121
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/appearance-BpVu3lpk.d.cts +0 -146
- package/dist/appearance-BpVu3lpk.d.ts +0 -146
- package/dist/chunk-QH7PL3GU.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -22485,11 +22485,8 @@ var DEFAULT_APPEARANCE = {
|
|
|
22485
22485
|
composer: { leadingIcon: "none", sendIcon: "arrow-up", shape: "pill" },
|
|
22486
22486
|
messages: { userStyle: "bubble", assistantStyle: "plain" }
|
|
22487
22487
|
},
|
|
22488
|
-
suggestedPrompts: [
|
|
22489
|
-
|
|
22490
|
-
"Tell me about your business",
|
|
22491
|
-
"How can I get in touch?"
|
|
22492
|
-
],
|
|
22488
|
+
suggestedPrompts: [],
|
|
22489
|
+
suggestedPromptsSource: "model",
|
|
22493
22490
|
locale: "en",
|
|
22494
22491
|
direction: "auto"
|
|
22495
22492
|
};
|
|
@@ -22522,11 +22519,8 @@ function resolveAppearance(input = {}) {
|
|
|
22522
22519
|
...assistant?.labels
|
|
22523
22520
|
},
|
|
22524
22521
|
presentation: resolvePresentation(assistant?.presentation),
|
|
22525
|
-
suggestedPrompts: assistant?.suggestedPrompts ??
|
|
22526
|
-
|
|
22527
|
-
`Tell me about ${branding.name}`,
|
|
22528
|
-
"How can I get in touch?"
|
|
22529
|
-
] : DEFAULT_APPEARANCE.suggestedPrompts),
|
|
22522
|
+
suggestedPrompts: assistant?.suggestedPrompts ?? [],
|
|
22523
|
+
suggestedPromptsSource: assistant?.suggestedPrompts === void 0 ? "model" : "configured",
|
|
22530
22524
|
...assistant?.privacyUrl ? { privacyUrl: assistant.privacyUrl } : {},
|
|
22531
22525
|
...assistant?.termsUrl ? { termsUrl: assistant.termsUrl } : {},
|
|
22532
22526
|
locale: assistant?.locale ?? DEFAULT_APPEARANCE.locale,
|
|
@@ -27075,6 +27069,7 @@ var AssistantChatStateStore = class {
|
|
|
27075
27069
|
#messages = [];
|
|
27076
27070
|
#status = "ready";
|
|
27077
27071
|
#error;
|
|
27072
|
+
#suggestions;
|
|
27078
27073
|
#operation;
|
|
27079
27074
|
#nextId = 0;
|
|
27080
27075
|
subscribe(listener) {
|
|
@@ -27086,20 +27081,24 @@ var AssistantChatStateStore = class {
|
|
|
27086
27081
|
return structuredClone({
|
|
27087
27082
|
status: this.#status,
|
|
27088
27083
|
messages: this.#messages,
|
|
27084
|
+
...this.#suggestions ? { suggestions: this.#suggestions } : {},
|
|
27089
27085
|
...this.#error ? { error: this.#error } : {}
|
|
27090
27086
|
});
|
|
27091
27087
|
}
|
|
27092
27088
|
handle(event) {
|
|
27093
27089
|
switch (event.event) {
|
|
27094
27090
|
case "message_started":
|
|
27091
|
+
this.#suggestions = void 0;
|
|
27095
27092
|
this.#beginMessage(event.data.message);
|
|
27096
27093
|
return;
|
|
27097
27094
|
case "interaction_started":
|
|
27095
|
+
this.#suggestions = void 0;
|
|
27098
27096
|
this.#beginInteraction(event.data.id);
|
|
27099
27097
|
return;
|
|
27100
27098
|
case "resume_started":
|
|
27101
27099
|
this.#status = "submitted";
|
|
27102
27100
|
this.#error = void 0;
|
|
27101
|
+
this.#suggestions = void 0;
|
|
27103
27102
|
this.#startOperation();
|
|
27104
27103
|
this.#notify();
|
|
27105
27104
|
return;
|
|
@@ -27132,6 +27131,13 @@ var AssistantChatStateStore = class {
|
|
|
27132
27131
|
case "view_available":
|
|
27133
27132
|
this.#writeData("view", event.data.id, event.data);
|
|
27134
27133
|
return;
|
|
27134
|
+
case "suggested_prompts":
|
|
27135
|
+
this.#suggestions = {
|
|
27136
|
+
phase: event.data.phase,
|
|
27137
|
+
prompts: [...event.data.prompts]
|
|
27138
|
+
};
|
|
27139
|
+
this.#notify();
|
|
27140
|
+
return;
|
|
27135
27141
|
case "message_completed":
|
|
27136
27142
|
this.#closeOperation("ready");
|
|
27137
27143
|
return;
|
|
@@ -27172,6 +27178,7 @@ var AssistantChatStateStore = class {
|
|
|
27172
27178
|
this.#messages = [...this.#messages, userMessage];
|
|
27173
27179
|
this.#status = "submitted";
|
|
27174
27180
|
this.#error = void 0;
|
|
27181
|
+
this.#suggestions = void 0;
|
|
27175
27182
|
this.#startOperation();
|
|
27176
27183
|
this.#notify();
|
|
27177
27184
|
}
|
|
@@ -27501,7 +27508,8 @@ var assistantUiSchema = external_exports.object({
|
|
|
27501
27508
|
privacyUrl: httpsUrlSchema.optional(),
|
|
27502
27509
|
termsUrl: httpsUrlSchema.optional(),
|
|
27503
27510
|
locale: external_exports.string().trim().min(2).max(35).optional(),
|
|
27504
|
-
direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
|
|
27511
|
+
direction: external_exports.enum(["ltr", "rtl", "auto"]).optional(),
|
|
27512
|
+
webmcp: external_exports.object({ enabled: external_exports.boolean().optional() }).strict().optional()
|
|
27505
27513
|
}).strict();
|
|
27506
27514
|
var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
|
|
27507
27515
|
function parseAssistantConfiguration(value) {
|
|
@@ -27563,7 +27571,8 @@ function parseSession(value) {
|
|
|
27563
27571
|
const apps = value.endpoints.apps;
|
|
27564
27572
|
const sandbox = value.endpoints.sandbox;
|
|
27565
27573
|
const transcript = value.endpoints.transcript;
|
|
27566
|
-
|
|
27574
|
+
const suggestions = value.endpoints.suggestions;
|
|
27575
|
+
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" || transcript !== void 0 && typeof transcript !== "string" || suggestions !== void 0 && typeof suggestions !== "string") {
|
|
27567
27576
|
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
27568
27577
|
}
|
|
27569
27578
|
const configuration = parseAssistantConfiguration(value.configuration);
|
|
@@ -27576,7 +27585,8 @@ function parseSession(value) {
|
|
|
27576
27585
|
...typeof interactions === "string" ? { interactions } : {},
|
|
27577
27586
|
...typeof apps === "string" ? { apps } : {},
|
|
27578
27587
|
...typeof sandbox === "string" ? { sandbox } : {},
|
|
27579
|
-
...typeof transcript === "string" ? { transcript } : {}
|
|
27588
|
+
...typeof transcript === "string" ? { transcript } : {},
|
|
27589
|
+
...typeof suggestions === "string" ? { suggestions } : {}
|
|
27580
27590
|
},
|
|
27581
27591
|
...configuration === void 0 ? {} : { configuration },
|
|
27582
27592
|
...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
|
|
@@ -27660,6 +27670,10 @@ function toAssistantClientEvent(event) {
|
|
|
27660
27670
|
return event;
|
|
27661
27671
|
}
|
|
27662
27672
|
break;
|
|
27673
|
+
case "suggested_prompts":
|
|
27674
|
+
if (isSuggestedPromptsDetail(value))
|
|
27675
|
+
return event;
|
|
27676
|
+
break;
|
|
27663
27677
|
case "done":
|
|
27664
27678
|
if (hasOptionalTurnId(value)) return event;
|
|
27665
27679
|
break;
|
|
@@ -27671,6 +27685,12 @@ function toAssistantClientEvent(event) {
|
|
|
27671
27685
|
}
|
|
27672
27686
|
return { event: "unrecognized", data: { name: event.event, payload: value } };
|
|
27673
27687
|
}
|
|
27688
|
+
function isSuggestedPromptsDetail(value) {
|
|
27689
|
+
if (!isRecord3(value)) return false;
|
|
27690
|
+
return (value.phase === "initial" || value.phase === "follow_up") && Array.isArray(value.prompts) && value.prompts.length <= 3 && value.prompts.every(
|
|
27691
|
+
(prompt) => typeof prompt === "string" && prompt.trim().length > 0 && prompt.length <= 240
|
|
27692
|
+
) && hasOptionalTurnId(value);
|
|
27693
|
+
}
|
|
27674
27694
|
function isViewAvailableDetail(value) {
|
|
27675
27695
|
return hasString(value, "id") && hasString(value, "tool") && typeof value.resourceUri === "string" && value.resourceUri.startsWith("ui://") && hasOptionalString(value.title) && (value.replayed === void 0 || value.replayed === true) && isJsonValue(value.result, 0) && hasOptionalJson(value.arguments) && hasOptionalString(value.html) && (value.resourceMeta === void 0 || isRecord3(value.resourceMeta)) && (value.allowedOpenDomains === void 0 || Array.isArray(value.allowedOpenDomains) && value.allowedOpenDomains.every(isHttpsUrl2)) && hasOptionalTurnId(value);
|
|
27676
27696
|
}
|
|
@@ -27749,6 +27769,11 @@ function parseTranscriptEntries(value) {
|
|
|
27749
27769
|
}
|
|
27750
27770
|
return parsed;
|
|
27751
27771
|
}
|
|
27772
|
+
function parseTranscriptSuggestions(value) {
|
|
27773
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
27774
|
+
const suggestions = value.suggestions;
|
|
27775
|
+
return isSuggestedPromptsDetail(suggestions) && suggestions.phase === "follow_up" ? { phase: suggestions.phase, prompts: suggestions.prompts } : void 0;
|
|
27776
|
+
}
|
|
27752
27777
|
function transcriptReplayEvents(entries2) {
|
|
27753
27778
|
const events = [];
|
|
27754
27779
|
let open = false;
|
|
@@ -27911,6 +27936,7 @@ var DefaultAssistantClient = class {
|
|
|
27911
27936
|
#context;
|
|
27912
27937
|
#modelContext;
|
|
27913
27938
|
#active;
|
|
27939
|
+
#initialSuggestions;
|
|
27914
27940
|
#pendingAppInteractions = /* @__PURE__ */ new Map();
|
|
27915
27941
|
constructor(options) {
|
|
27916
27942
|
this.#source = resolveSessionSource(options);
|
|
@@ -27972,10 +27998,13 @@ var DefaultAssistantClient = class {
|
|
|
27972
27998
|
"turn_failed"
|
|
27973
27999
|
);
|
|
27974
28000
|
if (!response.ok) return;
|
|
27975
|
-
|
|
28001
|
+
const value = await response.json();
|
|
28002
|
+
for (const event of transcriptReplayEvents(parseTranscriptEntries(value))) {
|
|
27976
28003
|
this.#emit(event);
|
|
27977
28004
|
if (event.event === "message_completed") await this.#chat.flush();
|
|
27978
28005
|
}
|
|
28006
|
+
const suggestions = parseTranscriptSuggestions(value);
|
|
28007
|
+
if (suggestions) this.#emit({ event: "suggested_prompts", data: suggestions });
|
|
27979
28008
|
}
|
|
27980
28009
|
/** POST the one-shot resume trigger; a 409 means nothing was pending, which renders as silence. */
|
|
27981
28010
|
async #resumeTurn(session, signal) {
|
|
@@ -27984,7 +28013,10 @@ var DefaultAssistantClient = class {
|
|
|
27984
28013
|
{
|
|
27985
28014
|
method: "POST",
|
|
27986
28015
|
headers: authorizedHeaders(session.token, "text/event-stream"),
|
|
27987
|
-
body: JSON.stringify({
|
|
28016
|
+
body: JSON.stringify({
|
|
28017
|
+
resume: true,
|
|
28018
|
+
...session.endpoints.suggestions ? { suggestions: true } : {}
|
|
28019
|
+
}),
|
|
27988
28020
|
signal
|
|
27989
28021
|
},
|
|
27990
28022
|
"turn_failed"
|
|
@@ -28007,6 +28039,7 @@ var DefaultAssistantClient = class {
|
|
|
28007
28039
|
async sendMessage(text3) {
|
|
28008
28040
|
const message = text3.trim();
|
|
28009
28041
|
if (!message) return;
|
|
28042
|
+
this.#cancelInitialSuggestions();
|
|
28010
28043
|
await this.#singleFlight(async (signal) => {
|
|
28011
28044
|
await this.#runChatOperation(
|
|
28012
28045
|
signal,
|
|
@@ -28014,10 +28047,42 @@ var DefaultAssistantClient = class {
|
|
|
28014
28047
|
);
|
|
28015
28048
|
});
|
|
28016
28049
|
}
|
|
28050
|
+
async loadInitialSuggestions() {
|
|
28051
|
+
if (!this.#session) await this.connect();
|
|
28052
|
+
const session = this.#session;
|
|
28053
|
+
if (!session?.endpoints.suggestions) return;
|
|
28054
|
+
if (this.#initialSuggestions) return this.#initialSuggestions.promise;
|
|
28055
|
+
const controller = new AbortController();
|
|
28056
|
+
const clientContext = this.#resolveClientContext();
|
|
28057
|
+
const pageContext = this.#resolvePageContext();
|
|
28058
|
+
const promise2 = this.#request(
|
|
28059
|
+
session.endpoints.suggestions,
|
|
28060
|
+
{
|
|
28061
|
+
method: "POST",
|
|
28062
|
+
headers: authorizedHeaders(session.token, "text/event-stream"),
|
|
28063
|
+
body: JSON.stringify({
|
|
28064
|
+
...clientContext ? { clientContext } : {},
|
|
28065
|
+
...pageContext === void 0 ? {} : { pageContext },
|
|
28066
|
+
...this.#modelContext ? { modelContext: this.#modelContext } : {}
|
|
28067
|
+
}),
|
|
28068
|
+
signal: controller.signal
|
|
28069
|
+
},
|
|
28070
|
+
"turn_failed"
|
|
28071
|
+
).then(async (response) => {
|
|
28072
|
+
if (response.ok) await this.#consume(response);
|
|
28073
|
+
}).finally(() => {
|
|
28074
|
+
if (this.#initialSuggestions?.controller === controller) {
|
|
28075
|
+
this.#initialSuggestions = void 0;
|
|
28076
|
+
}
|
|
28077
|
+
});
|
|
28078
|
+
this.#initialSuggestions = { controller, promise: promise2 };
|
|
28079
|
+
return promise2;
|
|
28080
|
+
}
|
|
28017
28081
|
async respond(id, resolution) {
|
|
28018
28082
|
if (!id) {
|
|
28019
28083
|
throw clientError("invalid_request", "interaction id is required", false);
|
|
28020
28084
|
}
|
|
28085
|
+
this.#cancelInitialSuggestions();
|
|
28021
28086
|
await this.#singleFlight(async (signal) => {
|
|
28022
28087
|
await this.#runChatOperation(signal, async () => {
|
|
28023
28088
|
const pendingApp = this.#pendingAppInteractions.get(id);
|
|
@@ -28037,6 +28102,7 @@ var DefaultAssistantClient = class {
|
|
|
28037
28102
|
const body = modernEndpoint ? {
|
|
28038
28103
|
id,
|
|
28039
28104
|
action: resolution.action,
|
|
28105
|
+
...session.endpoints.suggestions ? { suggestions: true } : {},
|
|
28040
28106
|
...resolution.action === "accept" && resolution.content !== void 0 ? { content: resolution.content } : {}
|
|
28041
28107
|
} : { id };
|
|
28042
28108
|
this.#emit({ event: "interaction_started", data: { id, action: resolution.action } });
|
|
@@ -28115,7 +28181,7 @@ var DefaultAssistantClient = class {
|
|
|
28115
28181
|
this.#modelContext = copyAssistantModelContext(update);
|
|
28116
28182
|
this.#emit({ event: "model_context_changed", data: { modelContext: this.#modelContext } });
|
|
28117
28183
|
}
|
|
28118
|
-
async requestApp(method, params) {
|
|
28184
|
+
async requestApp(method, params, options) {
|
|
28119
28185
|
const session = this.#session;
|
|
28120
28186
|
if (!session?.endpoints.apps) {
|
|
28121
28187
|
throw clientError(
|
|
@@ -28129,7 +28195,11 @@ var DefaultAssistantClient = class {
|
|
|
28129
28195
|
{
|
|
28130
28196
|
method: "POST",
|
|
28131
28197
|
headers: authorizedHeaders(session.token, "application/json"),
|
|
28132
|
-
body: JSON.stringify({
|
|
28198
|
+
body: JSON.stringify({
|
|
28199
|
+
...options?.bridge ? { bridge: options.bridge } : {},
|
|
28200
|
+
method,
|
|
28201
|
+
params
|
|
28202
|
+
})
|
|
28133
28203
|
},
|
|
28134
28204
|
"app_request_failed"
|
|
28135
28205
|
);
|
|
@@ -28144,6 +28214,7 @@ var DefaultAssistantClient = class {
|
|
|
28144
28214
|
const value = await response.json();
|
|
28145
28215
|
const interaction = parseAppInteraction(value);
|
|
28146
28216
|
if (!interaction) return value;
|
|
28217
|
+
options?.onSuspended?.();
|
|
28147
28218
|
return new Promise((resolve2, reject) => {
|
|
28148
28219
|
const id = interaction.data.id;
|
|
28149
28220
|
const replaced = this.#pendingAppInteractions.get(id);
|
|
@@ -28161,6 +28232,7 @@ var DefaultAssistantClient = class {
|
|
|
28161
28232
|
return this.#session?.endpoints.sandbox;
|
|
28162
28233
|
}
|
|
28163
28234
|
resetSession() {
|
|
28235
|
+
this.#cancelInitialSuggestions();
|
|
28164
28236
|
this.#rejectPendingAppInteractions(
|
|
28165
28237
|
clientError("confirmation_expired", "assistant session is not active", false)
|
|
28166
28238
|
);
|
|
@@ -28169,6 +28241,7 @@ var DefaultAssistantClient = class {
|
|
|
28169
28241
|
}
|
|
28170
28242
|
abort() {
|
|
28171
28243
|
this.#active?.abort();
|
|
28244
|
+
this.#cancelInitialSuggestions();
|
|
28172
28245
|
}
|
|
28173
28246
|
async #sendTurn(message, mayRetry, signal, modelContext) {
|
|
28174
28247
|
const session = this.#session ?? await this.#createSession(signal);
|
|
@@ -28184,7 +28257,8 @@ var DefaultAssistantClient = class {
|
|
|
28184
28257
|
message,
|
|
28185
28258
|
...clientContext ? { clientContext } : {},
|
|
28186
28259
|
...pageContext === void 0 ? {} : { pageContext },
|
|
28187
|
-
...modelContext ? { modelContext } : {}
|
|
28260
|
+
...modelContext ? { modelContext } : {},
|
|
28261
|
+
...session.endpoints.suggestions ? { suggestions: true } : {}
|
|
28188
28262
|
}),
|
|
28189
28263
|
signal
|
|
28190
28264
|
},
|
|
@@ -28373,6 +28447,9 @@ var DefaultAssistantClient = class {
|
|
|
28373
28447
|
this.#active = void 0;
|
|
28374
28448
|
}
|
|
28375
28449
|
}
|
|
28450
|
+
#cancelInitialSuggestions() {
|
|
28451
|
+
this.#initialSuggestions?.controller.abort();
|
|
28452
|
+
}
|
|
28376
28453
|
async #runChatOperation(signal, operation) {
|
|
28377
28454
|
try {
|
|
28378
28455
|
await operation();
|
|
@@ -28453,6 +28530,22 @@ function appendConversationRecovery(input) {
|
|
|
28453
28530
|
input.revealLatest();
|
|
28454
28531
|
}
|
|
28455
28532
|
|
|
28533
|
+
// src/element-error-recovery.ts
|
|
28534
|
+
function renderConversationError(options) {
|
|
28535
|
+
const { appearance, error: error51 } = options;
|
|
28536
|
+
const detail = error51 instanceof AssistantClientError ? error51.detail : { code: options.fallbackCode, retryable: false };
|
|
28537
|
+
const startNewConversation = detail.serviceCode === "session_turn_budget_exhausted";
|
|
28538
|
+
const reconnect = detail.retryable !== false || detail.serviceCode === void 0;
|
|
28539
|
+
const recoveryAction = startNewConversation ? { label: appearance.labels.newConversation, run: () => options.startNewConversation() } : reconnect ? { label: appearance.labels.reconnect, run: () => options.reconnect() } : void 0;
|
|
28540
|
+
appendConversationRecovery({
|
|
28541
|
+
messages: options.messages,
|
|
28542
|
+
message: detail.code === "session_expired" ? appearance.labels.sessionExpired : appearance.labels.unavailable,
|
|
28543
|
+
...recoveryAction === void 0 ? {} : { action: recoveryAction },
|
|
28544
|
+
revealLatest: () => options.revealLatest()
|
|
28545
|
+
});
|
|
28546
|
+
return detail;
|
|
28547
|
+
}
|
|
28548
|
+
|
|
28456
28549
|
// src/input-request-card.ts
|
|
28457
28550
|
function createInputRequestCard(options) {
|
|
28458
28551
|
const card = document.createElement("section");
|
|
@@ -32626,6 +32719,59 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
|
|
|
32626
32719
|
@media (max-width: 560px) { :host([data-mode="floating"]:not([open])) { bottom: 20px; } :host([open]:not([mobile-fullscreen])) .panel { width: calc(100vw - 40px); max-height: calc(100dvh - 40px); } }
|
|
32627
32720
|
</style>`;
|
|
32628
32721
|
|
|
32722
|
+
// src/element-suggestions-controller.ts
|
|
32723
|
+
var AssistantElementSuggestionsController = class {
|
|
32724
|
+
#host;
|
|
32725
|
+
#initialRequested = false;
|
|
32726
|
+
#dynamic;
|
|
32727
|
+
constructor(host) {
|
|
32728
|
+
this.#host = host;
|
|
32729
|
+
}
|
|
32730
|
+
reset() {
|
|
32731
|
+
this.#initialRequested = false;
|
|
32732
|
+
this.clear();
|
|
32733
|
+
}
|
|
32734
|
+
clear() {
|
|
32735
|
+
this.#dynamic = void 0;
|
|
32736
|
+
this.render();
|
|
32737
|
+
}
|
|
32738
|
+
handle(event) {
|
|
32739
|
+
if (event.event === "message_started" || event.event === "interaction_started") {
|
|
32740
|
+
this.clear();
|
|
32741
|
+
return;
|
|
32742
|
+
}
|
|
32743
|
+
if (event.event !== "suggested_prompts") return;
|
|
32744
|
+
this.#dynamic = event.data;
|
|
32745
|
+
this.render();
|
|
32746
|
+
}
|
|
32747
|
+
maybeLoadInitial() {
|
|
32748
|
+
const appearance = this.#host.appearance();
|
|
32749
|
+
if (this.#initialRequested || appearance.suggestedPromptsSource !== "model" || this.#host.hasMessages() || !this.#host.isOpen()) {
|
|
32750
|
+
return;
|
|
32751
|
+
}
|
|
32752
|
+
const client = this.#host.client();
|
|
32753
|
+
if (!client?.loadInitialSuggestions) return;
|
|
32754
|
+
this.#initialRequested = true;
|
|
32755
|
+
void client.loadInitialSuggestions().catch(() => {
|
|
32756
|
+
});
|
|
32757
|
+
}
|
|
32758
|
+
render() {
|
|
32759
|
+
const container = this.#host.root()?.querySelector(".suggested-prompts");
|
|
32760
|
+
if (!container) return;
|
|
32761
|
+
const appearance = this.#host.appearance();
|
|
32762
|
+
const hasMessages = this.#host.hasMessages();
|
|
32763
|
+
const prompts = (this.#dynamic?.phase === "follow_up" || !hasMessages ? this.#dynamic?.prompts : void 0) ?? (!hasMessages && appearance.suggestedPromptsSource === "configured" ? appearance.suggestedPrompts : []);
|
|
32764
|
+
container.replaceChildren();
|
|
32765
|
+
for (const prompt of prompts) {
|
|
32766
|
+
const button2 = document.createElement("button");
|
|
32767
|
+
button2.type = "button";
|
|
32768
|
+
button2.textContent = prompt;
|
|
32769
|
+
button2.addEventListener("click", () => this.#host.send(prompt));
|
|
32770
|
+
container.append(button2);
|
|
32771
|
+
}
|
|
32772
|
+
}
|
|
32773
|
+
};
|
|
32774
|
+
|
|
32629
32775
|
// src/host-appearance.ts
|
|
32630
32776
|
var ROLE_VARIABLES = {
|
|
32631
32777
|
canvas: "--ns-assistant-canvas",
|
|
@@ -32934,6 +33080,147 @@ function applyAssistantElementTheme(input) {
|
|
|
32934
33080
|
return warnings;
|
|
32935
33081
|
}
|
|
32936
33082
|
|
|
33083
|
+
// src/webmcp-bridge.ts
|
|
33084
|
+
var INERT = { registeredToolNames: [], stop: () => {
|
|
33085
|
+
} };
|
|
33086
|
+
async function startWebMcpBridge(ports) {
|
|
33087
|
+
const { client, modelContext, enabled } = ports;
|
|
33088
|
+
if (!enabled || !modelContext) return INERT;
|
|
33089
|
+
let listed;
|
|
33090
|
+
try {
|
|
33091
|
+
listed = await client.requestApp("tools/list", {});
|
|
33092
|
+
} catch {
|
|
33093
|
+
return INERT;
|
|
33094
|
+
}
|
|
33095
|
+
const registrations = [];
|
|
33096
|
+
for (const tool of projectableTools(listed)) {
|
|
33097
|
+
registrations.push({
|
|
33098
|
+
name: tool.name,
|
|
33099
|
+
handle: modelContext.registerTool({
|
|
33100
|
+
name: tool.name,
|
|
33101
|
+
...tool.description === void 0 ? {} : { description: tool.description },
|
|
33102
|
+
...tool.inputSchema === void 0 ? {} : { inputSchema: tool.inputSchema },
|
|
33103
|
+
// Passed through, never inferred: an agent may use `readOnlyHint` to decide it can call
|
|
33104
|
+
// without asking, so a hint this bridge invented would be a hint nobody authored.
|
|
33105
|
+
...tool.annotations === void 0 ? {} : { annotations: tool.annotations },
|
|
33106
|
+
execute: (args) => executeThroughAssistant(client, tool.name, args)
|
|
33107
|
+
})
|
|
33108
|
+
});
|
|
33109
|
+
}
|
|
33110
|
+
let stopped = false;
|
|
33111
|
+
return {
|
|
33112
|
+
get registeredToolNames() {
|
|
33113
|
+
return stopped ? [] : registrations.map((entry) => entry.name);
|
|
33114
|
+
},
|
|
33115
|
+
stop() {
|
|
33116
|
+
if (stopped) return;
|
|
33117
|
+
stopped = true;
|
|
33118
|
+
for (const entry of registrations) entry.handle?.unregister?.();
|
|
33119
|
+
}
|
|
33120
|
+
};
|
|
33121
|
+
}
|
|
33122
|
+
function projectableTools(listed) {
|
|
33123
|
+
const tools = isRecord7(listed) && Array.isArray(listed.tools) ? listed.tools : [];
|
|
33124
|
+
const projectable = [];
|
|
33125
|
+
for (const tool of tools) {
|
|
33126
|
+
if (!isRecord7(tool) || typeof tool.name !== "string" || tool.name.length === 0) continue;
|
|
33127
|
+
const visibility = readVisibility(tool);
|
|
33128
|
+
if (visibility !== void 0 && !(visibility.includes("app") && visibility.includes("model"))) {
|
|
33129
|
+
continue;
|
|
33130
|
+
}
|
|
33131
|
+
projectable.push({
|
|
33132
|
+
name: tool.name,
|
|
33133
|
+
description: typeof tool.description === "string" ? tool.description : void 0,
|
|
33134
|
+
inputSchema: isRecord7(tool.inputSchema) ? tool.inputSchema : void 0,
|
|
33135
|
+
annotations: isRecord7(tool.annotations) ? tool.annotations : void 0
|
|
33136
|
+
});
|
|
33137
|
+
}
|
|
33138
|
+
return projectable;
|
|
33139
|
+
}
|
|
33140
|
+
function readVisibility(tool) {
|
|
33141
|
+
const meta3 = isRecord7(tool._meta) ? tool._meta : void 0;
|
|
33142
|
+
const ui = meta3 && isRecord7(meta3.ui) ? meta3.ui : void 0;
|
|
33143
|
+
const visibility = ui?.visibility;
|
|
33144
|
+
return Array.isArray(visibility) ? visibility.filter((entry) => typeof entry === "string") : void 0;
|
|
33145
|
+
}
|
|
33146
|
+
async function executeThroughAssistant(client, name21, args) {
|
|
33147
|
+
let suspend;
|
|
33148
|
+
const suspended = new Promise((resolve2) => {
|
|
33149
|
+
suspend = resolve2;
|
|
33150
|
+
});
|
|
33151
|
+
try {
|
|
33152
|
+
const call = client.requestApp(
|
|
33153
|
+
"tools/call",
|
|
33154
|
+
{ name: name21, arguments: args },
|
|
33155
|
+
// Attribution only: anyone holding the session token could send this, and it selects a
|
|
33156
|
+
// *narrower* budget than the unmarked path, so declaring it can only cost the declarer.
|
|
33157
|
+
{ bridge: "webmcp", onSuspended: suspend }
|
|
33158
|
+
);
|
|
33159
|
+
void call.catch(() => {
|
|
33160
|
+
});
|
|
33161
|
+
const outcome = await Promise.race([
|
|
33162
|
+
call.then((value) => ({ kind: "result", value })),
|
|
33163
|
+
suspended.then(() => ({ kind: "confirmation" }))
|
|
33164
|
+
]);
|
|
33165
|
+
return outcome.kind === "confirmation" ? CONFIRMATION_REQUIRED : toToolResult(outcome.value);
|
|
33166
|
+
} catch (error51) {
|
|
33167
|
+
return textResult(error51 instanceof Error ? error51.message : "tool call failed", true);
|
|
33168
|
+
}
|
|
33169
|
+
}
|
|
33170
|
+
var CONFIRMATION_REQUIRED = {
|
|
33171
|
+
content: [
|
|
33172
|
+
{
|
|
33173
|
+
type: "text",
|
|
33174
|
+
text: "confirmation required: open the assistant panel on this page to approve this action"
|
|
33175
|
+
}
|
|
33176
|
+
],
|
|
33177
|
+
isError: true
|
|
33178
|
+
};
|
|
33179
|
+
function toToolResult(value) {
|
|
33180
|
+
if (isRecord7(value) && Array.isArray(value.content)) return value;
|
|
33181
|
+
return textResult(JSON.stringify(value ?? null), false);
|
|
33182
|
+
}
|
|
33183
|
+
function textResult(text3, isError) {
|
|
33184
|
+
return { content: [{ type: "text", text: text3 }], isError };
|
|
33185
|
+
}
|
|
33186
|
+
function isRecord7(value) {
|
|
33187
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33188
|
+
}
|
|
33189
|
+
|
|
33190
|
+
// src/element-webmcp-controller.ts
|
|
33191
|
+
var AssistantElementWebMcpController = class {
|
|
33192
|
+
#enabled = false;
|
|
33193
|
+
#bridge;
|
|
33194
|
+
/** Guards against a late `startWebMcpBridge` resolving after the session it belonged to is gone. */
|
|
33195
|
+
#generation = 0;
|
|
33196
|
+
/** Called when resolved configuration arrives; the deployment's opt-in is the only source. */
|
|
33197
|
+
configure(enabled) {
|
|
33198
|
+
this.#enabled = enabled;
|
|
33199
|
+
if (!enabled) this.stop();
|
|
33200
|
+
}
|
|
33201
|
+
start(client) {
|
|
33202
|
+
if (!this.#enabled || !client) return;
|
|
33203
|
+
const modelContext = browserModelContext();
|
|
33204
|
+
if (!modelContext) return;
|
|
33205
|
+
this.stop();
|
|
33206
|
+
const generation = ++this.#generation;
|
|
33207
|
+
void startWebMcpBridge({ client, modelContext, enabled: true }).then((bridge) => {
|
|
33208
|
+
if (generation === this.#generation) this.#bridge = bridge;
|
|
33209
|
+
else bridge.stop();
|
|
33210
|
+
});
|
|
33211
|
+
}
|
|
33212
|
+
stop() {
|
|
33213
|
+
this.#generation += 1;
|
|
33214
|
+
this.#bridge?.stop();
|
|
33215
|
+
this.#bridge = void 0;
|
|
33216
|
+
}
|
|
33217
|
+
};
|
|
33218
|
+
function browserModelContext() {
|
|
33219
|
+
if (typeof document === "undefined") return void 0;
|
|
33220
|
+
const host = document;
|
|
33221
|
+
return typeof host.modelContext?.registerTool === "function" ? host.modelContext : void 0;
|
|
33222
|
+
}
|
|
33223
|
+
|
|
32937
33224
|
// src/presentation-styles.ts
|
|
32938
33225
|
var presentationStyles = `<style>
|
|
32939
33226
|
:host([data-mode="floating"]) {
|
|
@@ -33177,9 +33464,18 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33177
33464
|
#turnInFlight = false;
|
|
33178
33465
|
#fetch;
|
|
33179
33466
|
#hostAppearance;
|
|
33467
|
+
#webmcp = new AssistantElementWebMcpController();
|
|
33180
33468
|
#appearanceWarningKeys = /* @__PURE__ */ new Set();
|
|
33181
33469
|
#appearanceStyleSnapshot = /* @__PURE__ */ new Map();
|
|
33182
33470
|
#scroll = new AssistantElementScrollController();
|
|
33471
|
+
#suggestions = new AssistantElementSuggestionsController({
|
|
33472
|
+
root: () => this.shadowRoot,
|
|
33473
|
+
appearance: () => this.#appearance,
|
|
33474
|
+
client: () => this.#client,
|
|
33475
|
+
hasMessages: () => this.hasAttribute("has-messages"),
|
|
33476
|
+
isOpen: () => this.hasAttribute("open"),
|
|
33477
|
+
send: (prompt) => this.#sendGuarded(prompt)
|
|
33478
|
+
});
|
|
33183
33479
|
sessionEndpoint = "";
|
|
33184
33480
|
/** Public mount: the non-secret embed id `noodle deploy` printed. Mutually exclusive with the above. */
|
|
33185
33481
|
embedId = "";
|
|
@@ -33291,6 +33587,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33291
33587
|
this.#launcher.disconnect();
|
|
33292
33588
|
this.#setBusy(false);
|
|
33293
33589
|
this.#environment.disconnect();
|
|
33590
|
+
this.#webmcp.stop();
|
|
33294
33591
|
}
|
|
33295
33592
|
attributeChangedCallback(name21) {
|
|
33296
33593
|
if (name21 === "theme") this.#applyTheme();
|
|
@@ -33324,6 +33621,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33324
33621
|
}
|
|
33325
33622
|
resetSession() {
|
|
33326
33623
|
this.#conversationGeneration += 1;
|
|
33624
|
+
this.#webmcp.stop();
|
|
33327
33625
|
this.#client?.abort();
|
|
33328
33626
|
this.#unsubscribeClient?.();
|
|
33329
33627
|
this.#client = void 0;
|
|
@@ -33333,12 +33631,14 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33333
33631
|
this.#events.resetConversation();
|
|
33334
33632
|
this.#pendingUserEcho = void 0;
|
|
33335
33633
|
this.removeAttribute("has-messages");
|
|
33634
|
+
this.#suggestions.reset();
|
|
33336
33635
|
this.#setBusy(false);
|
|
33337
33636
|
this.#setSessionState("idle");
|
|
33338
33637
|
}
|
|
33339
33638
|
/** Re-establish the session without replaying a message or interaction decision. */
|
|
33340
33639
|
reconnect() {
|
|
33341
33640
|
this.#conversationGeneration += 1;
|
|
33641
|
+
this.#webmcp.stop();
|
|
33342
33642
|
this.#client?.abort();
|
|
33343
33643
|
this.#unsubscribeClient?.();
|
|
33344
33644
|
this.#client = void 0;
|
|
@@ -33396,6 +33696,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33396
33696
|
this.#turnInFlight = true;
|
|
33397
33697
|
this.#pendingUserEcho = message;
|
|
33398
33698
|
this.#appendMessage("user", message);
|
|
33699
|
+
this.#suggestions.clear();
|
|
33399
33700
|
this.#setBusy(true);
|
|
33400
33701
|
if (this.#sessionState !== "ready") this.#setSessionState("loading");
|
|
33401
33702
|
try {
|
|
@@ -33415,12 +33716,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33415
33716
|
if (generation === this.#conversationGeneration) this.#setBusy(false);
|
|
33416
33717
|
}
|
|
33417
33718
|
}
|
|
33418
|
-
/**
|
|
33419
|
-
* Every listener the element attaches to its own DOM must go through this guard: internal UI
|
|
33420
|
-
* failures surface as `assistant-error` events (already dispatched by the failing path) and must
|
|
33421
|
-
* never escape as unhandled rejections into the embedding page. Programmatic callers use
|
|
33422
|
-
* `sendMessage`/`confirmTool` directly and keep the rejection.
|
|
33423
|
-
*/
|
|
33719
|
+
/** DOM listeners swallow after the failing path dispatches its typed assistant-error event. */
|
|
33424
33720
|
#sendGuarded(text3) {
|
|
33425
33721
|
void this.sendMessage(text3).catch(() => {
|
|
33426
33722
|
});
|
|
@@ -33472,13 +33768,21 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33472
33768
|
}
|
|
33473
33769
|
#primeSession() {
|
|
33474
33770
|
if (!this.sessionEndpoint && !this.embedId) return;
|
|
33475
|
-
if (this.#sessionBootstrap
|
|
33771
|
+
if (this.#sessionBootstrap) return;
|
|
33772
|
+
if (this.#client?.hasSession()) {
|
|
33773
|
+
this.#suggestions.maybeLoadInitial();
|
|
33774
|
+
return;
|
|
33775
|
+
}
|
|
33476
33776
|
if (this.#client?.isBusy?.() === true) return;
|
|
33477
33777
|
if (this.embedId && !this.#pageContext) void this.#automaticPageContext.refresh();
|
|
33478
33778
|
const client = this.#ensureClient();
|
|
33479
33779
|
const generation = this.#conversationGeneration;
|
|
33480
33780
|
this.#setSessionState("loading");
|
|
33481
|
-
const bootstrap = client.connect().
|
|
33781
|
+
const bootstrap = client.connect().then(() => {
|
|
33782
|
+
if (generation === this.#conversationGeneration && this.#client === client) {
|
|
33783
|
+
this.#suggestions.maybeLoadInitial();
|
|
33784
|
+
}
|
|
33785
|
+
}).catch((error51) => {
|
|
33482
33786
|
if (generation !== this.#conversationGeneration || this.#client !== client) return;
|
|
33483
33787
|
this.#dispatchClientError(error51, "session_failed");
|
|
33484
33788
|
this.#presentationGate.reveal();
|
|
@@ -33490,15 +33794,18 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33490
33794
|
#handleClientEvent(event) {
|
|
33491
33795
|
this.dispatchEvent(new CustomEvent("assistant-event", { detail: event }));
|
|
33492
33796
|
this.#events.handle(event);
|
|
33797
|
+
this.#suggestions.handle(event);
|
|
33493
33798
|
if (event.event === "session_started") {
|
|
33494
33799
|
this.#publicConfiguration.disconnect();
|
|
33495
33800
|
this.removeAttribute("data-public-configuration-loading");
|
|
33496
33801
|
this.#presentationGate.reveal();
|
|
33497
33802
|
this.#setSessionState("ready");
|
|
33498
33803
|
this.#messages?.querySelector(".conversation-error")?.remove();
|
|
33804
|
+
this.#webmcp.start(this.#client);
|
|
33499
33805
|
}
|
|
33500
33806
|
if (event.event === "session_expired" || event.event === "session_reset") {
|
|
33501
33807
|
this.#setSessionState("idle");
|
|
33808
|
+
this.#webmcp.stop();
|
|
33502
33809
|
}
|
|
33503
33810
|
if (event.event === "tool_proposed" || event.event === "interaction_proposed" || event.event === "input_requested") {
|
|
33504
33811
|
this.#removeThinking();
|
|
@@ -33602,23 +33909,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33602
33909
|
if (error51 instanceof AssistantClientError && error51.detail.code === "session_failed") {
|
|
33603
33910
|
this.#setSessionState("error");
|
|
33604
33911
|
}
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33614
|
-
|
|
33615
|
-
|
|
33616
|
-
// open tab trying is exactly the load the cap was set to refuse — so the button goes, not just
|
|
33617
|
-
// the alarming words. A spent *session* keeps it: reconnect mints a fresh one, which is the fix.
|
|
33618
|
-
...recoveryAction === void 0 ? {} : { action: recoveryAction },
|
|
33619
|
-
revealLatest: () => this.#revealLatest()
|
|
33620
|
-
});
|
|
33621
|
-
this.#dispatchError(detail);
|
|
33912
|
+
this.#dispatchError(
|
|
33913
|
+
renderConversationError({
|
|
33914
|
+
messages: this.#messages,
|
|
33915
|
+
appearance: this.#appearance,
|
|
33916
|
+
error: error51,
|
|
33917
|
+
fallbackCode,
|
|
33918
|
+
startNewConversation: () => this.#startNewConversation(),
|
|
33919
|
+
reconnect: () => this.reconnect(),
|
|
33920
|
+
revealLatest: () => this.#revealLatest()
|
|
33921
|
+
})
|
|
33922
|
+
);
|
|
33622
33923
|
}
|
|
33623
33924
|
#startNewConversation() {
|
|
33624
33925
|
this.resetSession();
|
|
@@ -33651,7 +33952,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33651
33952
|
}
|
|
33652
33953
|
}
|
|
33653
33954
|
#applyConfiguration(configuration) {
|
|
33955
|
+
this.#webmcp.configure(configuration.assistant?.webmcp?.enabled === true);
|
|
33654
33956
|
this.#appearance = resolveAppearance(configuration);
|
|
33957
|
+
this.#suggestions.render();
|
|
33655
33958
|
this.#syncAppearance();
|
|
33656
33959
|
this.#applyStartOpenPolicy();
|
|
33657
33960
|
}
|
|
@@ -33696,15 +33999,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33696
33999
|
"aria-label",
|
|
33697
34000
|
appearance.labels.close
|
|
33698
34001
|
);
|
|
33699
|
-
|
|
33700
|
-
prompts.replaceChildren();
|
|
33701
|
-
for (const prompt of appearance.suggestedPrompts) {
|
|
33702
|
-
const button2 = document.createElement("button");
|
|
33703
|
-
button2.type = "button";
|
|
33704
|
-
button2.textContent = prompt;
|
|
33705
|
-
button2.addEventListener("click", () => this.#sendGuarded(prompt));
|
|
33706
|
-
prompts.append(button2);
|
|
33707
|
-
}
|
|
34002
|
+
this.#suggestions.render();
|
|
33708
34003
|
queryRequired(this.shadowRoot, "header").hidden = !appearance.behavior.showHeader;
|
|
33709
34004
|
const legal = queryRequired(this.shadowRoot, ".legal");
|
|
33710
34005
|
legal.replaceChildren();
|
|
@@ -33742,14 +34037,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
33742
34037
|
textarea.placeholder = appearance.labels.composerPlaceholder;
|
|
33743
34038
|
textarea.setAttribute("aria-label", appearance.labels.composerPlaceholder);
|
|
33744
34039
|
queryRequired(this.shadowRoot, ".send").textContent = appearance.labels.send;
|
|
33745
|
-
|
|
33746
|
-
for (const prompt of appearance.suggestedPrompts) {
|
|
33747
|
-
const button2 = document.createElement("button");
|
|
33748
|
-
button2.type = "button";
|
|
33749
|
-
button2.textContent = prompt;
|
|
33750
|
-
button2.addEventListener("click", () => this.#sendGuarded(prompt));
|
|
33751
|
-
prompts.append(button2);
|
|
33752
|
-
}
|
|
34040
|
+
this.#suggestions.render();
|
|
33753
34041
|
if (!appearance.behavior.showLauncher)
|
|
33754
34042
|
queryRequired(this.shadowRoot, ".launcher").hidden = true;
|
|
33755
34043
|
if (!appearance.behavior.showHeader)
|