@noodleseed/assistant 1.20.0 → 1.22.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 +27 -2
- package/dist/{chunk-DLUINP4W.js → chunk-6TT3R555.js} +101 -42
- package/dist/chunk-6TT3R555.js.map +1 -0
- package/dist/{chunk-52XT7BLR.js → chunk-ZPZHPXIQ.js} +129 -82
- package/dist/chunk-ZPZHPXIQ.js.map +1 -0
- package/dist/{client-7o7n2nqw.d.cts → client-BZ7xPwfW.d.cts} +14 -5
- package/dist/{client-CysBQBdd.d.ts → client-BkJlpN5N.d.ts} +14 -5
- package/dist/client.cjs +127 -80
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/embed.global.js +68 -67
- package/dist/index.cjs +226 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/react/client.cjs +127 -80
- package/dist/react/client.cjs.map +1 -1
- package/dist/react/client.d.cts +1 -1
- package/dist/react/client.d.ts +1 -1
- package/dist/react/client.js +1 -1
- package/dist/react.cjs +227 -120
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +8 -1
- package/dist/react.d.ts +8 -1
- package/dist/react.js +3 -2
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +1 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +15 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-52XT7BLR.js.map +0 -1
- package/dist/chunk-DLUINP4W.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -27030,6 +27030,12 @@ var AssistantChatStateStore = class {
|
|
|
27030
27030
|
case "interaction_started":
|
|
27031
27031
|
this.#beginInteraction(event.data.id);
|
|
27032
27032
|
return;
|
|
27033
|
+
case "resume_started":
|
|
27034
|
+
this.#status = "submitted";
|
|
27035
|
+
this.#error = void 0;
|
|
27036
|
+
this.#startOperation();
|
|
27037
|
+
this.#notify();
|
|
27038
|
+
return;
|
|
27033
27039
|
case "content":
|
|
27034
27040
|
this.#writeText(event.data.delta);
|
|
27035
27041
|
return;
|
|
@@ -27285,6 +27291,78 @@ function isRecord(value) {
|
|
|
27285
27291
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27286
27292
|
}
|
|
27287
27293
|
|
|
27294
|
+
// src/client-errors.ts
|
|
27295
|
+
async function refusalCode(response) {
|
|
27296
|
+
try {
|
|
27297
|
+
const body = await response.clone().json();
|
|
27298
|
+
const code = body.code;
|
|
27299
|
+
return typeof code === "string" ? code : void 0;
|
|
27300
|
+
} catch {
|
|
27301
|
+
return void 0;
|
|
27302
|
+
}
|
|
27303
|
+
}
|
|
27304
|
+
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
27305
|
+
"daily_turn_budget_exhausted",
|
|
27306
|
+
"daily_session_budget_exhausted"
|
|
27307
|
+
]);
|
|
27308
|
+
var AssistantClientError = class extends Error {
|
|
27309
|
+
detail;
|
|
27310
|
+
constructor(detail, message, options) {
|
|
27311
|
+
super(message, options);
|
|
27312
|
+
this.name = "AssistantClientError";
|
|
27313
|
+
this.detail = detail;
|
|
27314
|
+
}
|
|
27315
|
+
};
|
|
27316
|
+
function parseSession(value) {
|
|
27317
|
+
if (!isRecord2(value) || !isRecord2(value.endpoints)) {
|
|
27318
|
+
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
27319
|
+
}
|
|
27320
|
+
const interactions = value.endpoints.interactions;
|
|
27321
|
+
const apps = value.endpoints.apps;
|
|
27322
|
+
const sandbox = value.endpoints.sandbox;
|
|
27323
|
+
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") {
|
|
27324
|
+
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
27325
|
+
}
|
|
27326
|
+
return {
|
|
27327
|
+
token: value.token,
|
|
27328
|
+
expiresAt: value.expiresAt,
|
|
27329
|
+
endpoints: {
|
|
27330
|
+
turns: value.endpoints.turns,
|
|
27331
|
+
toolConfirmations: value.endpoints.toolConfirmations,
|
|
27332
|
+
...typeof interactions === "string" ? { interactions } : {},
|
|
27333
|
+
...typeof apps === "string" ? { apps } : {},
|
|
27334
|
+
...typeof sandbox === "string" ? { sandbox } : {}
|
|
27335
|
+
},
|
|
27336
|
+
...isRecord2(value.configuration) ? { configuration: value.configuration } : {},
|
|
27337
|
+
...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
|
|
27338
|
+
};
|
|
27339
|
+
}
|
|
27340
|
+
function isRecord2(value) {
|
|
27341
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27342
|
+
}
|
|
27343
|
+
async function readStableErrorCode(response) {
|
|
27344
|
+
let value;
|
|
27345
|
+
try {
|
|
27346
|
+
value = await response.json();
|
|
27347
|
+
} catch {
|
|
27348
|
+
return void 0;
|
|
27349
|
+
}
|
|
27350
|
+
if (!isRecord2(value) || typeof value.code !== "string") return void 0;
|
|
27351
|
+
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
27352
|
+
}
|
|
27353
|
+
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
27354
|
+
return new AssistantClientError(
|
|
27355
|
+
{
|
|
27356
|
+
code,
|
|
27357
|
+
...status === void 0 ? {} : { status },
|
|
27358
|
+
retryable,
|
|
27359
|
+
...serviceCode === void 0 ? {} : { serviceCode }
|
|
27360
|
+
},
|
|
27361
|
+
message,
|
|
27362
|
+
options
|
|
27363
|
+
);
|
|
27364
|
+
}
|
|
27365
|
+
|
|
27288
27366
|
// src/events.ts
|
|
27289
27367
|
function toAssistantClientEvent(event) {
|
|
27290
27368
|
const value = event.data;
|
|
@@ -27300,12 +27378,12 @@ function toAssistantClientEvent(event) {
|
|
|
27300
27378
|
}
|
|
27301
27379
|
break;
|
|
27302
27380
|
case "tool_proposed":
|
|
27303
|
-
if (hasString(value, "id") && hasString(value, "tool") && hasOptionalString(value.title) && hasOptionalString(value.description) && hasOptionalJson(value.arguments) && (value.reviewSchema === void 0 ||
|
|
27381
|
+
if (hasString(value, "id") && hasString(value, "tool") && hasOptionalString(value.title) && hasOptionalString(value.description) && hasOptionalJson(value.arguments) && (value.reviewSchema === void 0 || isRecord3(value.reviewSchema)) && hasOptionalString(value.expiresAt) && (value.requiresConfirmation === void 0 || value.requiresConfirmation === true) && hasOptionalTurnId(value)) {
|
|
27304
27382
|
return event;
|
|
27305
27383
|
}
|
|
27306
27384
|
break;
|
|
27307
27385
|
case "input_requested":
|
|
27308
|
-
if (hasString(value, "id") && hasString(value, "message") &&
|
|
27386
|
+
if (hasString(value, "id") && hasString(value, "message") && isRecord3(value.requestedSchema) && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
|
|
27309
27387
|
return event;
|
|
27310
27388
|
}
|
|
27311
27389
|
break;
|
|
@@ -27349,7 +27427,7 @@ function toAssistantClientEvent(event) {
|
|
|
27349
27427
|
return { event: "unrecognized", data: { name: event.event, payload: value } };
|
|
27350
27428
|
}
|
|
27351
27429
|
function isViewAvailableDetail(value) {
|
|
27352
|
-
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 ||
|
|
27430
|
+
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(isHttpsUrl)) && hasOptionalTurnId(value);
|
|
27353
27431
|
}
|
|
27354
27432
|
function isHttpsUrl(value) {
|
|
27355
27433
|
if (typeof value !== "string") return false;
|
|
@@ -27382,11 +27460,11 @@ function isJsonValue(value, depth) {
|
|
|
27382
27460
|
if (Array.isArray(value)) {
|
|
27383
27461
|
return value.length <= 256 && value.every((entry) => isJsonValue(entry, depth + 1));
|
|
27384
27462
|
}
|
|
27385
|
-
if (!
|
|
27463
|
+
if (!isRecord3(value)) return false;
|
|
27386
27464
|
const entries2 = Object.entries(value);
|
|
27387
27465
|
return entries2.length <= 256 && entries2.every(([, entry]) => isJsonValue(entry, depth + 1));
|
|
27388
27466
|
}
|
|
27389
|
-
function
|
|
27467
|
+
function isRecord3(value) {
|
|
27390
27468
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27391
27469
|
}
|
|
27392
27470
|
|
|
@@ -27516,15 +27594,15 @@ function parseFrame(frame) {
|
|
|
27516
27594
|
if (event === "done" && !isValidDonePayload(parsed)) {
|
|
27517
27595
|
throw invalidStream("assistant event stream contained an invalid done event");
|
|
27518
27596
|
}
|
|
27519
|
-
return { event, data:
|
|
27597
|
+
return { event, data: isRecord4(parsed) ? parsed : { value: parsed } };
|
|
27520
27598
|
}
|
|
27521
27599
|
function normalizeLineEndings(input) {
|
|
27522
27600
|
return input.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
|
27523
27601
|
}
|
|
27524
27602
|
function isValidDonePayload(value) {
|
|
27525
|
-
return
|
|
27603
|
+
return isRecord4(value) && (value.turnId === void 0 || typeof value.turnId === "string" && value.turnId.length > 0);
|
|
27526
27604
|
}
|
|
27527
|
-
function
|
|
27605
|
+
function isRecord4(value) {
|
|
27528
27606
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27529
27607
|
}
|
|
27530
27608
|
function invalidStream(message, cause) {
|
|
@@ -27535,27 +27613,6 @@ function isAbortError2(error51) {
|
|
|
27535
27613
|
}
|
|
27536
27614
|
|
|
27537
27615
|
// src/client.ts
|
|
27538
|
-
async function refusalCode(response) {
|
|
27539
|
-
try {
|
|
27540
|
-
const body = await response.clone().json();
|
|
27541
|
-
const code = body.code;
|
|
27542
|
-
return typeof code === "string" ? code : void 0;
|
|
27543
|
-
} catch {
|
|
27544
|
-
return void 0;
|
|
27545
|
-
}
|
|
27546
|
-
}
|
|
27547
|
-
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
27548
|
-
"daily_turn_budget_exhausted",
|
|
27549
|
-
"daily_session_budget_exhausted"
|
|
27550
|
-
]);
|
|
27551
|
-
var AssistantClientError = class extends Error {
|
|
27552
|
-
detail;
|
|
27553
|
-
constructor(detail, message, options) {
|
|
27554
|
-
super(message, options);
|
|
27555
|
-
this.name = "AssistantClientError";
|
|
27556
|
-
this.detail = detail;
|
|
27557
|
-
}
|
|
27558
|
-
};
|
|
27559
27616
|
var DefaultAssistantClient = class {
|
|
27560
27617
|
#source;
|
|
27561
27618
|
#fetch;
|
|
@@ -27590,12 +27647,50 @@ var DefaultAssistantClient = class {
|
|
|
27590
27647
|
hasSession() {
|
|
27591
27648
|
return this.#session !== void 0;
|
|
27592
27649
|
}
|
|
27650
|
+
/** Whether a request holds the single-flight slot, so callers can refuse before mutating UI. */
|
|
27651
|
+
isBusy() {
|
|
27652
|
+
return this.#active !== void 0;
|
|
27653
|
+
}
|
|
27593
27654
|
async connect() {
|
|
27594
27655
|
if (this.#session) return;
|
|
27595
27656
|
await this.#singleFlight(async (signal) => {
|
|
27596
|
-
if (
|
|
27657
|
+
if (this.#session) return;
|
|
27658
|
+
const session = await this.#createSession(signal);
|
|
27659
|
+
if (session.resume) {
|
|
27660
|
+
try {
|
|
27661
|
+
await this.#runChatOperation(signal, () => this.#resumeTurn(session, signal));
|
|
27662
|
+
} catch {
|
|
27663
|
+
}
|
|
27664
|
+
}
|
|
27597
27665
|
});
|
|
27598
27666
|
}
|
|
27667
|
+
/** POST the one-shot resume trigger; a 409 means nothing was pending, which renders as silence. */
|
|
27668
|
+
async #resumeTurn(session, signal) {
|
|
27669
|
+
const response = await this.#request(
|
|
27670
|
+
session.endpoints.turns,
|
|
27671
|
+
{
|
|
27672
|
+
method: "POST",
|
|
27673
|
+
headers: authorizedHeaders(session.token, "text/event-stream"),
|
|
27674
|
+
body: JSON.stringify({ resume: true }),
|
|
27675
|
+
signal
|
|
27676
|
+
},
|
|
27677
|
+
"turn_failed"
|
|
27678
|
+
);
|
|
27679
|
+
if (response.status === 409) return;
|
|
27680
|
+
if (!response.ok) {
|
|
27681
|
+
throw clientError(
|
|
27682
|
+
"turn_failed",
|
|
27683
|
+
`Assistant turn failed (${response.status})`,
|
|
27684
|
+
false,
|
|
27685
|
+
response.status,
|
|
27686
|
+
void 0,
|
|
27687
|
+
await refusalCode(response)
|
|
27688
|
+
);
|
|
27689
|
+
}
|
|
27690
|
+
this.#emit({ event: "resume_started", data: { tool: session.resume?.tool ?? "" } });
|
|
27691
|
+
await this.#consume(response);
|
|
27692
|
+
this.#emit({ event: "message_completed", data: {} });
|
|
27693
|
+
}
|
|
27599
27694
|
async sendMessage(text3) {
|
|
27600
27695
|
const message = text3.trim();
|
|
27601
27696
|
if (!message) return;
|
|
@@ -28000,10 +28095,10 @@ function authorizedHeaders(token, accept) {
|
|
|
28000
28095
|
};
|
|
28001
28096
|
}
|
|
28002
28097
|
function parseAppInteraction(value) {
|
|
28003
|
-
if (!
|
|
28098
|
+
if (!isRecord2(value) || !isRecord2(value.interaction)) return void 0;
|
|
28004
28099
|
const event = value.interaction.event;
|
|
28005
28100
|
const data = value.interaction.data;
|
|
28006
|
-
if (event !== "tool_proposed" && event !== "input_requested" || !
|
|
28101
|
+
if (event !== "tool_proposed" && event !== "input_requested" || !isRecord2(data)) {
|
|
28007
28102
|
return void 0;
|
|
28008
28103
|
}
|
|
28009
28104
|
const parsed = toAssistantClientEvent({ event, data });
|
|
@@ -28023,54 +28118,6 @@ function appInteractionStopped(action) {
|
|
|
28023
28118
|
isError: true
|
|
28024
28119
|
};
|
|
28025
28120
|
}
|
|
28026
|
-
function parseSession(value) {
|
|
28027
|
-
if (!isRecord4(value) || !isRecord4(value.endpoints)) {
|
|
28028
|
-
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
28029
|
-
}
|
|
28030
|
-
const interactions = value.endpoints.interactions;
|
|
28031
|
-
const apps = value.endpoints.apps;
|
|
28032
|
-
const sandbox = value.endpoints.sandbox;
|
|
28033
|
-
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") {
|
|
28034
|
-
throw clientError("session_failed", "Assistant session response is invalid", true);
|
|
28035
|
-
}
|
|
28036
|
-
return {
|
|
28037
|
-
token: value.token,
|
|
28038
|
-
expiresAt: value.expiresAt,
|
|
28039
|
-
endpoints: {
|
|
28040
|
-
turns: value.endpoints.turns,
|
|
28041
|
-
toolConfirmations: value.endpoints.toolConfirmations,
|
|
28042
|
-
...typeof interactions === "string" ? { interactions } : {},
|
|
28043
|
-
...typeof apps === "string" ? { apps } : {},
|
|
28044
|
-
...typeof sandbox === "string" ? { sandbox } : {}
|
|
28045
|
-
},
|
|
28046
|
-
...isRecord4(value.configuration) ? { configuration: value.configuration } : {}
|
|
28047
|
-
};
|
|
28048
|
-
}
|
|
28049
|
-
function isRecord4(value) {
|
|
28050
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28051
|
-
}
|
|
28052
|
-
async function readStableErrorCode(response) {
|
|
28053
|
-
let value;
|
|
28054
|
-
try {
|
|
28055
|
-
value = await response.json();
|
|
28056
|
-
} catch {
|
|
28057
|
-
return void 0;
|
|
28058
|
-
}
|
|
28059
|
-
if (!isRecord4(value) || typeof value.code !== "string") return void 0;
|
|
28060
|
-
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
28061
|
-
}
|
|
28062
|
-
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
28063
|
-
return new AssistantClientError(
|
|
28064
|
-
{
|
|
28065
|
-
code,
|
|
28066
|
-
...status === void 0 ? {} : { status },
|
|
28067
|
-
retryable,
|
|
28068
|
-
...serviceCode === void 0 ? {} : { serviceCode }
|
|
28069
|
-
},
|
|
28070
|
-
message,
|
|
28071
|
-
options
|
|
28072
|
-
);
|
|
28073
|
-
}
|
|
28074
28121
|
|
|
28075
28122
|
// src/input-request-card.ts
|
|
28076
28123
|
function createInputRequestCard(options) {
|
|
@@ -31255,6 +31302,11 @@ var AssistantElementEventController = class {
|
|
|
31255
31302
|
this.#completedTool = void 0;
|
|
31256
31303
|
return;
|
|
31257
31304
|
}
|
|
31305
|
+
if (name21 === "resume_started") {
|
|
31306
|
+
this.#startStream("message");
|
|
31307
|
+
this.#host.element.dispatchEvent(new CustomEvent("assistant-message-started"));
|
|
31308
|
+
return;
|
|
31309
|
+
}
|
|
31258
31310
|
if (name21 === "interaction_completed") {
|
|
31259
31311
|
if (this.#streamKind !== "interaction") return;
|
|
31260
31312
|
this.#finishInteraction(String(data.id ?? ""), String(data.action ?? "accept"));
|
|
@@ -31473,6 +31525,36 @@ function queryRequired(root, selector) {
|
|
|
31473
31525
|
if (!element) throw new Error(`assistant renderer is missing ${selector}`);
|
|
31474
31526
|
return element;
|
|
31475
31527
|
}
|
|
31528
|
+
function assistantElementMarkup(appearance, open, styles) {
|
|
31529
|
+
return `${styles}
|
|
31530
|
+
<button class="launcher" type="button" aria-label="${escapeAttribute(appearance.labels.open)}" aria-controls="assistant-panel" aria-expanded="${String(open)}"><slot name="launcher-icon"></slot><span class="visually-hidden" data-session-status aria-live="polite" hidden></span></button>
|
|
31531
|
+
<section class="panel" id="assistant-panel" role="dialog" aria-label="${escapeAttribute(appearance.brand.name)}">
|
|
31532
|
+
<header><slot name="header-leading"></slot><img class="brand-logo" alt=""><strong></strong><slot name="header-actions"></slot><button class="close" type="button" aria-label="${escapeAttribute(appearance.labels.close)}"></button></header>
|
|
31533
|
+
<div class="welcome"><slot name="empty-state"></slot></div>
|
|
31534
|
+
<div class="suggested-prompts"></div>
|
|
31535
|
+
<div class="messages-region">
|
|
31536
|
+
<div class="messages" aria-live="polite"></div>
|
|
31537
|
+
<button class="new-messages" type="button" aria-label="${escapeAttribute(appearance.labels.newMessages)}" hidden></button>
|
|
31538
|
+
</div>
|
|
31539
|
+
<form><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
|
|
31540
|
+
<nav class="legal" aria-label="Legal"></nav>
|
|
31541
|
+
<slot name="conversation-footer"></slot>
|
|
31542
|
+
</section>`;
|
|
31543
|
+
}
|
|
31544
|
+
function appendLegalLinks(legal, appearance) {
|
|
31545
|
+
for (const [label, href] of [
|
|
31546
|
+
["Privacy", appearance.privacyUrl],
|
|
31547
|
+
["Terms", appearance.termsUrl]
|
|
31548
|
+
]) {
|
|
31549
|
+
if (!href) continue;
|
|
31550
|
+
const link = document.createElement("a");
|
|
31551
|
+
link.href = href;
|
|
31552
|
+
link.target = "_blank";
|
|
31553
|
+
link.rel = "noopener noreferrer";
|
|
31554
|
+
link.textContent = label;
|
|
31555
|
+
legal.append(link);
|
|
31556
|
+
}
|
|
31557
|
+
}
|
|
31476
31558
|
|
|
31477
31559
|
// src/element-page-context-controller.ts
|
|
31478
31560
|
var AssistantElementPageContextController = class {
|
|
@@ -31514,6 +31596,33 @@ var AssistantElementPageContextController = class {
|
|
|
31514
31596
|
}
|
|
31515
31597
|
};
|
|
31516
31598
|
|
|
31599
|
+
// src/element-presentation-gate.ts
|
|
31600
|
+
var AssistantElementPresentationGate = class {
|
|
31601
|
+
#host;
|
|
31602
|
+
#primeSession;
|
|
31603
|
+
#focusComposer;
|
|
31604
|
+
constructor(host, primeSession, focusComposer) {
|
|
31605
|
+
this.#host = host;
|
|
31606
|
+
this.#primeSession = primeSession;
|
|
31607
|
+
this.#focusComposer = focusComposer;
|
|
31608
|
+
}
|
|
31609
|
+
syncOpenState() {
|
|
31610
|
+
const open = this.#host.hasAttribute("open");
|
|
31611
|
+
const presentationReady = this.#host.hasAttribute("data-presentation-ready");
|
|
31612
|
+
const launcher = this.#host.shadowRoot?.querySelector(".launcher");
|
|
31613
|
+
launcher?.setAttribute("aria-expanded", String(open && presentationReady));
|
|
31614
|
+
launcher?.setAttribute("aria-busy", String(open && !presentationReady));
|
|
31615
|
+
if (launcher) launcher.disabled = open && !presentationReady;
|
|
31616
|
+
if (open && this.#host.isConnected) this.#primeSession();
|
|
31617
|
+
}
|
|
31618
|
+
reveal() {
|
|
31619
|
+
if (this.#host.hasAttribute("data-presentation-ready")) return;
|
|
31620
|
+
this.#host.setAttribute("data-presentation-ready", "");
|
|
31621
|
+
this.syncOpenState();
|
|
31622
|
+
if (this.#host.hasAttribute("open")) queueMicrotask(this.#focusComposer);
|
|
31623
|
+
}
|
|
31624
|
+
};
|
|
31625
|
+
|
|
31517
31626
|
// src/element-scroll-controller.ts
|
|
31518
31627
|
var AssistantElementScrollController = class {
|
|
31519
31628
|
#region;
|
|
@@ -31629,9 +31738,10 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
|
|
|
31629
31738
|
.launcher:hover { transform: translateY(-2px) scale(1.03); box-shadow: 0 8px 26px rgba(0,0,0,.16); }
|
|
31630
31739
|
.launcher img { width: 28px; height: 28px; border-radius: 50%; object-fit: contain; }
|
|
31631
31740
|
.panel { display: none; flex-direction: column; width: min(var(--ns-assistant-panel-width, var(--ns-assistant-default-panel-width)), calc(100vw - 40px)); min-height: var(--ns-assistant-min-height, var(--ns-assistant-default-min-height)); max-height: min(var(--ns-assistant-max-height, var(--ns-assistant-default-max-height)), calc(100vh - 40px)); overflow: hidden; color: var(--ns-assistant-panel-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-panel, var(--ns-assistant-default-panel)); border: 1px solid var(--ns-assistant-panel-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-panel-radius, var(--ns-assistant-default-panel-radius)); box-shadow: var(--ns-assistant-shadow, var(--ns-assistant-default-shadow)); transform-origin: bottom right; isolation: isolate; animation: halo-panel-in .28s cubic-bezier(.23,1,.32,1) both; }
|
|
31632
|
-
:host([open]) .panel { display: flex; }
|
|
31741
|
+
:host([open][data-presentation-ready]) .panel { display: flex; }
|
|
31633
31742
|
.panel { position: relative; }
|
|
31634
|
-
:host([open]) .launcher { display: none; }
|
|
31743
|
+
:host([open][data-presentation-ready]) .launcher { display: none; }
|
|
31744
|
+
:host([open]:not([data-presentation-ready])) .launcher { cursor: progress; opacity: .72; transform: none; }
|
|
31635
31745
|
header { display: flex; flex: 0 0 auto; gap: 10px; align-items: center; min-height: 64px; padding: 12px 18px; color: var(--ns-assistant-header-text, inherit); background: var(--ns-assistant-header, transparent); border-bottom: 1px solid color-mix(in srgb, var(--ns-assistant-header-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))) 78%, transparent); }
|
|
31636
31746
|
header strong { flex: 1; font-size: 16px; font-weight: 600; letter-spacing: 0; }
|
|
31637
31747
|
.brand-logo { display: block; max-width: 120px; max-height: 28px; object-fit: contain; }
|
|
@@ -32257,9 +32367,16 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32257
32367
|
#automaticPageContext = new AssistantElementPageContextController(
|
|
32258
32368
|
() => !this.#pageContext
|
|
32259
32369
|
);
|
|
32370
|
+
#presentationGate = new AssistantElementPresentationGate(
|
|
32371
|
+
this,
|
|
32372
|
+
() => this.#primeSession(),
|
|
32373
|
+
() => this.focusComposer()
|
|
32374
|
+
);
|
|
32260
32375
|
#pendingUserEcho;
|
|
32261
32376
|
#sessionState = "idle";
|
|
32262
32377
|
#startOpenApplied = false;
|
|
32378
|
+
#readyDispatched = false;
|
|
32379
|
+
#turnInFlight = false;
|
|
32263
32380
|
#hostAppearance;
|
|
32264
32381
|
#appearanceWarningKeys = /* @__PURE__ */ new Set();
|
|
32265
32382
|
#appearanceStyleSnapshot = /* @__PURE__ */ new Map();
|
|
@@ -32325,20 +32442,24 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32325
32442
|
if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
|
|
32326
32443
|
if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
|
|
32327
32444
|
if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
|
|
32445
|
+
if (!this.sessionEndpoint && !this.embedId) this.#presentationGate.reveal();
|
|
32328
32446
|
this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
|
|
32329
32447
|
this.#media?.addEventListener("change", this.#handleSystemTheme);
|
|
32330
32448
|
document.addEventListener("keydown", this.#handleDocumentKeydown);
|
|
32331
32449
|
document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
|
|
32332
32450
|
if (this.#appearance.behavior.startOpen) this.#applyStartOpenPolicy();
|
|
32333
32451
|
this.#render();
|
|
32334
|
-
|
|
32452
|
+
if (!this.#readyDispatched) {
|
|
32453
|
+
this.#readyDispatched = true;
|
|
32454
|
+
this.dispatchEvent(new CustomEvent("assistant-ready"));
|
|
32455
|
+
}
|
|
32335
32456
|
if (this.hasAttribute("start-open")) {
|
|
32336
32457
|
this.setAttribute("open", "");
|
|
32337
32458
|
this.#startOpenApplied = true;
|
|
32338
32459
|
} else if (!this.embedId) {
|
|
32339
32460
|
this.#primeSession();
|
|
32340
32461
|
}
|
|
32341
|
-
this.#syncOpenState();
|
|
32462
|
+
this.#presentationGate.syncOpenState();
|
|
32342
32463
|
}
|
|
32343
32464
|
disconnectedCallback() {
|
|
32344
32465
|
this.#conversationGeneration += 1;
|
|
@@ -32356,7 +32477,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32356
32477
|
}
|
|
32357
32478
|
attributeChangedCallback(name21) {
|
|
32358
32479
|
if (name21 === "theme") this.#applyTheme();
|
|
32359
|
-
if (name21 === "open") this.#syncOpenState();
|
|
32480
|
+
if (name21 === "open") this.#presentationGate.syncOpenState();
|
|
32360
32481
|
}
|
|
32361
32482
|
open() {
|
|
32362
32483
|
this.setAttribute("open", "");
|
|
@@ -32445,7 +32566,14 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32445
32566
|
async sendMessage(text3) {
|
|
32446
32567
|
const message = text3.trim();
|
|
32447
32568
|
if (!message) return;
|
|
32569
|
+
if (this.#turnInFlight) {
|
|
32570
|
+
throw new AssistantClientError(
|
|
32571
|
+
{ code: "request_in_progress", retryable: true },
|
|
32572
|
+
"an assistant request is already in progress"
|
|
32573
|
+
);
|
|
32574
|
+
}
|
|
32448
32575
|
const generation = this.#conversationGeneration;
|
|
32576
|
+
this.#turnInFlight = true;
|
|
32449
32577
|
this.#pendingUserEcho = message;
|
|
32450
32578
|
this.#appendMessage("user", message);
|
|
32451
32579
|
this.#setBusy(true);
|
|
@@ -32462,6 +32590,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32462
32590
|
this.#dispatchClientError(error51, "turn_failed");
|
|
32463
32591
|
throw error51;
|
|
32464
32592
|
} finally {
|
|
32593
|
+
this.#turnInFlight = false;
|
|
32465
32594
|
if (this.#pendingUserEcho === message) this.#pendingUserEcho = void 0;
|
|
32466
32595
|
if (generation === this.#conversationGeneration) this.#setBusy(false);
|
|
32467
32596
|
}
|
|
@@ -32530,6 +32659,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32530
32659
|
#primeSession() {
|
|
32531
32660
|
if (!this.sessionEndpoint && !this.embedId) return;
|
|
32532
32661
|
if (this.#sessionBootstrap || this.#client?.hasSession()) return;
|
|
32662
|
+
if (this.#client?.isBusy?.() === true) return;
|
|
32533
32663
|
if (this.embedId && !this.#pageContext) void this.#automaticPageContext.refresh();
|
|
32534
32664
|
const client = this.#ensureClient();
|
|
32535
32665
|
const generation = this.#conversationGeneration;
|
|
@@ -32537,6 +32667,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32537
32667
|
const bootstrap = client.connect().catch((error51) => {
|
|
32538
32668
|
if (generation !== this.#conversationGeneration || this.#client !== client) return;
|
|
32539
32669
|
this.#dispatchClientError(error51, "session_failed");
|
|
32670
|
+
this.#presentationGate.reveal();
|
|
32540
32671
|
}).finally(() => {
|
|
32541
32672
|
if (this.#sessionBootstrap === bootstrap) this.#sessionBootstrap = void 0;
|
|
32542
32673
|
});
|
|
@@ -32546,6 +32677,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32546
32677
|
this.dispatchEvent(new CustomEvent("assistant-event", { detail: event }));
|
|
32547
32678
|
this.#events.handle(event);
|
|
32548
32679
|
if (event.event === "session_started") {
|
|
32680
|
+
this.#presentationGate.reveal();
|
|
32549
32681
|
this.#setSessionState("ready");
|
|
32550
32682
|
this.#messages?.querySelector(".conversation-error")?.remove();
|
|
32551
32683
|
}
|
|
@@ -32559,6 +32691,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32559
32691
|
if (event.event === "content" || event.event === "message_completed" || event.event === "interaction_completed" || event.event === "error") {
|
|
32560
32692
|
this.#removeThinking();
|
|
32561
32693
|
}
|
|
32694
|
+
if (event.event === "resume_started") this.#setBusy(true);
|
|
32695
|
+
if (event.event === "message_completed" || event.event === "error") this.#setBusy(false);
|
|
32562
32696
|
}
|
|
32563
32697
|
#setBusy(busy) {
|
|
32564
32698
|
this.toggleAttribute("busy", busy);
|
|
@@ -32713,11 +32847,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32713
32847
|
this.#sessionState = state;
|
|
32714
32848
|
syncAssistantSessionVisualState(this, this.shadowRoot, state, this.#appearance);
|
|
32715
32849
|
}
|
|
32716
|
-
#syncOpenState() {
|
|
32717
|
-
const open = this.hasAttribute("open");
|
|
32718
|
-
this.shadowRoot?.querySelector(".launcher")?.setAttribute("aria-expanded", String(open));
|
|
32719
|
-
if (open && this.isConnected) this.#primeSession();
|
|
32720
|
-
}
|
|
32721
32850
|
#closeAndRestoreFocus() {
|
|
32722
32851
|
this.close();
|
|
32723
32852
|
this.shadowRoot?.querySelector(".launcher")?.focus();
|
|
@@ -32767,7 +32896,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32767
32896
|
queryRequired(this.shadowRoot, "header").hidden = !appearance.behavior.showHeader;
|
|
32768
32897
|
const legal = queryRequired(this.shadowRoot, ".legal");
|
|
32769
32898
|
legal.replaceChildren();
|
|
32770
|
-
|
|
32899
|
+
appendLegalLinks(legal, this.#appearance);
|
|
32771
32900
|
this.#applyTheme();
|
|
32772
32901
|
}
|
|
32773
32902
|
#render() {
|
|
@@ -32783,20 +32912,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32783
32912
|
this.dataset.position = appearance.layout.position;
|
|
32784
32913
|
this.dataset.density = appearance.layout.density;
|
|
32785
32914
|
this.toggleAttribute("mobile-fullscreen", appearance.layout.mobileFullscreen);
|
|
32786
|
-
this.shadowRoot.innerHTML =
|
|
32787
|
-
|
|
32788
|
-
|
|
32789
|
-
|
|
32790
|
-
|
|
32791
|
-
<div class="suggested-prompts"></div>
|
|
32792
|
-
<div class="messages-region">
|
|
32793
|
-
<div class="messages" aria-live="polite"></div>
|
|
32794
|
-
<button class="new-messages" type="button" aria-label="${escapeAttribute(appearance.labels.newMessages)}" hidden></button>
|
|
32795
|
-
</div>
|
|
32796
|
-
<form><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
|
|
32797
|
-
<nav class="legal" aria-label="Legal"></nav>
|
|
32798
|
-
<slot name="conversation-footer"></slot>
|
|
32799
|
-
</section>`;
|
|
32915
|
+
this.shadowRoot.innerHTML = assistantElementMarkup(
|
|
32916
|
+
appearance,
|
|
32917
|
+
this.hasAttribute("open"),
|
|
32918
|
+
`${ASSISTANT_ELEMENT_STYLES}${presentationStyles}`
|
|
32919
|
+
);
|
|
32800
32920
|
queryRequired(this.shadowRoot, "strong").textContent = appearance.brand.name;
|
|
32801
32921
|
const logo = queryRequired(this.shadowRoot, ".brand-logo");
|
|
32802
32922
|
const logoUrl = appearance.brand.logo[this.#resolvedMode()];
|
|
@@ -32823,7 +32943,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32823
32943
|
if (!appearance.behavior.showHeader)
|
|
32824
32944
|
queryRequired(this.shadowRoot, "header").hidden = true;
|
|
32825
32945
|
const legal = queryRequired(this.shadowRoot, ".legal");
|
|
32826
|
-
|
|
32946
|
+
appendLegalLinks(legal, this.#appearance);
|
|
32827
32947
|
this.#messages = this.shadowRoot.querySelector(".messages") ?? void 0;
|
|
32828
32948
|
this.#messages?.append(...conversation);
|
|
32829
32949
|
this.#thinking = this.#messages?.querySelector(".thinking") ?? void 0;
|
|
@@ -32862,20 +32982,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32862
32982
|
});
|
|
32863
32983
|
this.#applyTheme();
|
|
32864
32984
|
}
|
|
32865
|
-
#appendLegalLinks(legal) {
|
|
32866
|
-
for (const [label, href] of [
|
|
32867
|
-
["Privacy", this.#appearance.privacyUrl],
|
|
32868
|
-
["Terms", this.#appearance.termsUrl]
|
|
32869
|
-
]) {
|
|
32870
|
-
if (!href) continue;
|
|
32871
|
-
const link = document.createElement("a");
|
|
32872
|
-
link.href = href;
|
|
32873
|
-
link.target = "_blank";
|
|
32874
|
-
link.rel = "noopener noreferrer";
|
|
32875
|
-
link.textContent = label;
|
|
32876
|
-
legal.append(link);
|
|
32877
|
-
}
|
|
32878
|
-
}
|
|
32879
32985
|
};
|
|
32880
32986
|
function registerNoodleAssistant() {
|
|
32881
32987
|
if (globalThis.customElements && !customElements.get(ASSISTANT_TAG_NAME)) {
|