@noodleseed/assistant 1.14.0 → 1.15.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/{chunk-RITHSCTD.js → chunk-RRJIK473.js} +89 -20
- package/dist/chunk-RRJIK473.js.map +1 -0
- package/dist/{chunk-7WXGZJCA.js → chunk-SUHNNWKP.js} +36 -17
- package/dist/{chunk-7WXGZJCA.js.map → chunk-SUHNNWKP.js.map} +1 -1
- package/dist/{client-BKIeuSVS.d.cts → client-B_l59rWg.d.cts} +29 -2
- package/dist/{client-BccAX_om.d.ts → client-MnLfiyo2.d.ts} +29 -2
- package/dist/client.cjs +84 -19
- 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 +540 -0
- package/dist/index.cjs +119 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +2 -2
- package/dist/react/client.cjs +95 -23
- package/dist/react/client.cjs.map +1 -1
- package/dist/react/client.d.cts +3 -3
- package/dist/react/client.d.ts +3 -3
- package/dist/react/client.js +12 -5
- package/dist/react/client.js.map +1 -1
- package/dist/react.cjs +128 -36
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +7 -2
- package/dist/react.d.ts +7 -2
- package/dist/react.js +11 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-RITHSCTD.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -27276,6 +27276,24 @@ function hasToJsonProperty(value) {
|
|
|
27276
27276
|
return false;
|
|
27277
27277
|
}
|
|
27278
27278
|
|
|
27279
|
+
// src/session-source.ts
|
|
27280
|
+
var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
|
|
27281
|
+
var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
|
|
27282
|
+
function resolveSessionSource(options) {
|
|
27283
|
+
const { sessionEndpoint, embedId, serviceUrl } = options;
|
|
27284
|
+
if (Boolean(embedId) === Boolean(sessionEndpoint)) {
|
|
27285
|
+
throw new Error("pass either embedId or sessionEndpoint, not both and not neither");
|
|
27286
|
+
}
|
|
27287
|
+
if (embedId) {
|
|
27288
|
+
const base = (serviceUrl ?? NOODLE_CLOUD_URL).replace(/\/+$/, "");
|
|
27289
|
+
return { kind: "public", url: `${base}${PUBLIC_SESSION_PATH}`, embedId };
|
|
27290
|
+
}
|
|
27291
|
+
return { kind: "exchange", url: sessionEndpoint };
|
|
27292
|
+
}
|
|
27293
|
+
function sessionSourceKey(options) {
|
|
27294
|
+
return `${options.embedId ?? ""}|${options.serviceUrl ?? ""}|${options.sessionEndpoint ?? ""}`;
|
|
27295
|
+
}
|
|
27296
|
+
|
|
27279
27297
|
// src/transport.ts
|
|
27280
27298
|
var AssistantTransportError = class extends Error {
|
|
27281
27299
|
code = "invalid_response";
|
|
@@ -27403,6 +27421,19 @@ function isAbortError2(error51) {
|
|
|
27403
27421
|
}
|
|
27404
27422
|
|
|
27405
27423
|
// src/client.ts
|
|
27424
|
+
async function refusalCode(response) {
|
|
27425
|
+
try {
|
|
27426
|
+
const body = await response.clone().json();
|
|
27427
|
+
const code = body.code;
|
|
27428
|
+
return typeof code === "string" ? code : void 0;
|
|
27429
|
+
} catch {
|
|
27430
|
+
return void 0;
|
|
27431
|
+
}
|
|
27432
|
+
}
|
|
27433
|
+
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
27434
|
+
"daily_turn_budget_exhausted",
|
|
27435
|
+
"daily_session_budget_exhausted"
|
|
27436
|
+
]);
|
|
27406
27437
|
var AssistantClientError = class extends Error {
|
|
27407
27438
|
detail;
|
|
27408
27439
|
constructor(detail, message, options) {
|
|
@@ -27412,7 +27443,7 @@ var AssistantClientError = class extends Error {
|
|
|
27412
27443
|
}
|
|
27413
27444
|
};
|
|
27414
27445
|
var DefaultAssistantClient = class {
|
|
27415
|
-
#
|
|
27446
|
+
#source;
|
|
27416
27447
|
#fetch;
|
|
27417
27448
|
#listeners = /* @__PURE__ */ new Set();
|
|
27418
27449
|
#chat = new AssistantChatStateStore();
|
|
@@ -27424,8 +27455,7 @@ var DefaultAssistantClient = class {
|
|
|
27424
27455
|
#active;
|
|
27425
27456
|
#pendingAppInteractions = /* @__PURE__ */ new Map();
|
|
27426
27457
|
constructor(options) {
|
|
27427
|
-
|
|
27428
|
-
this.#sessionEndpoint = options.sessionEndpoint;
|
|
27458
|
+
this.#source = resolveSessionSource(options);
|
|
27429
27459
|
this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
27430
27460
|
this.#context = options.context ? { ...options.context } : void 0;
|
|
27431
27461
|
this.#modelContext = options.modelContext ? copyAssistantModelContext(options.modelContext) : void 0;
|
|
@@ -27641,11 +27671,14 @@ var DefaultAssistantClient = class {
|
|
|
27641
27671
|
return;
|
|
27642
27672
|
}
|
|
27643
27673
|
if (!response.ok) {
|
|
27674
|
+
const serviceCode = await refusalCode(response);
|
|
27644
27675
|
throw clientError(
|
|
27645
27676
|
"turn_failed",
|
|
27646
27677
|
`Assistant turn failed (${response.status})`,
|
|
27647
27678
|
false,
|
|
27648
|
-
response.status
|
|
27679
|
+
response.status,
|
|
27680
|
+
void 0,
|
|
27681
|
+
serviceCode
|
|
27649
27682
|
);
|
|
27650
27683
|
}
|
|
27651
27684
|
await this.#consume(response);
|
|
@@ -27684,23 +27717,25 @@ var DefaultAssistantClient = class {
|
|
|
27684
27717
|
}
|
|
27685
27718
|
}
|
|
27686
27719
|
async #createSession(signal) {
|
|
27687
|
-
const
|
|
27688
|
-
|
|
27689
|
-
|
|
27690
|
-
|
|
27691
|
-
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
|
|
27695
|
-
|
|
27696
|
-
|
|
27697
|
-
);
|
|
27720
|
+
const source = this.#source;
|
|
27721
|
+
const response = await this.#requestSession(source, {
|
|
27722
|
+
method: "POST",
|
|
27723
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
27724
|
+
body: JSON.stringify(
|
|
27725
|
+
source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
|
|
27726
|
+
),
|
|
27727
|
+
credentials: source.kind === "public" ? "omit" : "same-origin",
|
|
27728
|
+
signal
|
|
27729
|
+
});
|
|
27698
27730
|
if (!response.ok) {
|
|
27731
|
+
const serviceCode = await refusalCode(response);
|
|
27699
27732
|
throw clientError(
|
|
27700
27733
|
"session_failed",
|
|
27701
27734
|
`Assistant session failed (${response.status})`,
|
|
27702
|
-
|
|
27703
|
-
response.status
|
|
27735
|
+
serviceCode === void 0 || !UNRETRYABLE_SERVICE_CODES.has(serviceCode),
|
|
27736
|
+
response.status,
|
|
27737
|
+
void 0,
|
|
27738
|
+
serviceCode
|
|
27704
27739
|
);
|
|
27705
27740
|
}
|
|
27706
27741
|
let value;
|
|
@@ -27757,6 +27792,34 @@ var DefaultAssistantClient = class {
|
|
|
27757
27792
|
);
|
|
27758
27793
|
}
|
|
27759
27794
|
}
|
|
27795
|
+
/**
|
|
27796
|
+
* The mint, with the one failure a public page hits that an in-app embed cannot.
|
|
27797
|
+
*
|
|
27798
|
+
* A page that loads the embed script and then blocks `connect-src` fails here as a bare network
|
|
27799
|
+
* rejection — no status, no body, nothing to read. "Assistant request failed" sends a developer
|
|
27800
|
+
* hunting through their own code; naming the directive and the origin ends the search. The message
|
|
27801
|
+
* covers a plain outage too, because from inside the browser the two are indistinguishable.
|
|
27802
|
+
*/
|
|
27803
|
+
async #requestSession(source, init) {
|
|
27804
|
+
try {
|
|
27805
|
+
return await this.#fetch(source.url, init);
|
|
27806
|
+
} catch (error51) {
|
|
27807
|
+
if (init.signal?.aborted) throw error51;
|
|
27808
|
+
if (source.kind !== "public") {
|
|
27809
|
+
throw clientError("session_failed", "Assistant request failed", true, void 0, {
|
|
27810
|
+
cause: error51
|
|
27811
|
+
});
|
|
27812
|
+
}
|
|
27813
|
+
throw clientError(
|
|
27814
|
+
"session_failed",
|
|
27815
|
+
`Assistant could not reach ${new URL(source.url).origin}. If the page sets a Content-Security-Policy, allow that origin in connect-src (and in script-src and frame-src).`,
|
|
27816
|
+
true,
|
|
27817
|
+
void 0,
|
|
27818
|
+
{ cause: error51 },
|
|
27819
|
+
"blocked_by_page"
|
|
27820
|
+
);
|
|
27821
|
+
}
|
|
27822
|
+
}
|
|
27760
27823
|
async #request(input, init, failureCode) {
|
|
27761
27824
|
try {
|
|
27762
27825
|
return await this.#fetch(input, init);
|
|
@@ -27878,9 +27941,14 @@ async function readStableErrorCode(response) {
|
|
|
27878
27941
|
if (!isRecord4(value) || typeof value.code !== "string") return void 0;
|
|
27879
27942
|
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
27880
27943
|
}
|
|
27881
|
-
function clientError(code, message, retryable, status, options) {
|
|
27944
|
+
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
27882
27945
|
return new AssistantClientError(
|
|
27883
|
-
{
|
|
27946
|
+
{
|
|
27947
|
+
code,
|
|
27948
|
+
...status === void 0 ? {} : { status },
|
|
27949
|
+
retryable,
|
|
27950
|
+
...serviceCode === void 0 ? {} : { serviceCode }
|
|
27951
|
+
},
|
|
27884
27952
|
message,
|
|
27885
27953
|
options
|
|
27886
27954
|
);
|
|
@@ -31971,6 +32039,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
31971
32039
|
#appearanceStyleSnapshot = /* @__PURE__ */ new Map();
|
|
31972
32040
|
#scroll = new AssistantElementScrollController();
|
|
31973
32041
|
sessionEndpoint = "";
|
|
32042
|
+
/** Public mount: the non-secret embed id `noodle deploy` printed. Mutually exclusive with the above. */
|
|
32043
|
+
embedId = "";
|
|
32044
|
+
/** Only for a dev or self-hosted service; the published snippet needs no origin. */
|
|
32045
|
+
serviceUrl = "";
|
|
31974
32046
|
get appearance() {
|
|
31975
32047
|
return this.#hostAppearance;
|
|
31976
32048
|
}
|
|
@@ -32025,6 +32097,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32025
32097
|
connectedCallback() {
|
|
32026
32098
|
if (!this.shadowRoot) this.attachShadow({ mode: "open" });
|
|
32027
32099
|
if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
|
|
32100
|
+
if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
|
|
32101
|
+
if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
|
|
32028
32102
|
this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
|
|
32029
32103
|
this.#media?.addEventListener("change", this.#handleSystemTheme);
|
|
32030
32104
|
document.addEventListener("keydown", this.#handleDocumentKeydown);
|
|
@@ -32196,26 +32270,31 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32196
32270
|
}
|
|
32197
32271
|
};
|
|
32198
32272
|
#ensureClient() {
|
|
32199
|
-
|
|
32273
|
+
const key = sessionSourceKey(this);
|
|
32274
|
+
if (this.#client && this.#clientEndpoint === key) return this.#client;
|
|
32200
32275
|
this.#client?.abort();
|
|
32201
32276
|
this.#unsubscribeClient?.();
|
|
32202
32277
|
this.#sessionBootstrap = void 0;
|
|
32203
|
-
const
|
|
32204
|
-
sessionEndpoint: this.sessionEndpoint,
|
|
32278
|
+
const behavior = {
|
|
32205
32279
|
...this.#context ? { context: this.#context } : {},
|
|
32206
32280
|
clientContext: browserClientContext,
|
|
32207
32281
|
...this.#pageContext ? { pageContext: this.#pageContext } : {},
|
|
32208
32282
|
...this.#modelContext ? { modelContext: this.#modelContext } : {}
|
|
32209
|
-
}
|
|
32283
|
+
};
|
|
32284
|
+
const client = this.embedId ? createAssistantClient({
|
|
32285
|
+
embedId: this.embedId,
|
|
32286
|
+
...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
|
|
32287
|
+
...behavior
|
|
32288
|
+
}) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
|
|
32210
32289
|
this.#client = client;
|
|
32211
|
-
this.#clientEndpoint =
|
|
32290
|
+
this.#clientEndpoint = key;
|
|
32212
32291
|
this.#unsubscribeClient = client.subscribe((event) => {
|
|
32213
32292
|
if (this.#client === client) this.#handleClientEvent(event);
|
|
32214
32293
|
});
|
|
32215
32294
|
return client;
|
|
32216
32295
|
}
|
|
32217
32296
|
#primeSession() {
|
|
32218
|
-
if (!this.sessionEndpoint) return;
|
|
32297
|
+
if (!this.sessionEndpoint && !this.embedId) return;
|
|
32219
32298
|
const client = this.#ensureClient();
|
|
32220
32299
|
const generation = this.#conversationGeneration;
|
|
32221
32300
|
this.#setSessionState("loading");
|
|
@@ -32339,23 +32418,30 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32339
32418
|
}
|
|
32340
32419
|
const detail = error51 instanceof AssistantClientError ? error51.detail : { code: fallbackCode, retryable: false };
|
|
32341
32420
|
this.#appendRecoveryError(
|
|
32342
|
-
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable
|
|
32421
|
+
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable,
|
|
32422
|
+
// A spent or switched-off daily budget offers no retry. Reconnecting cannot succeed, and every
|
|
32423
|
+
// open tab trying is exactly the load the cap was set to refuse — so the button goes, not just
|
|
32424
|
+
// the alarming words. A spent *session* keeps it: reconnect mints a fresh one, which is the fix.
|
|
32425
|
+
detail.retryable !== false || detail.serviceCode === void 0
|
|
32343
32426
|
);
|
|
32344
32427
|
this.#dispatchError(detail);
|
|
32345
32428
|
}
|
|
32346
|
-
#appendRecoveryError(message) {
|
|
32429
|
+
#appendRecoveryError(message, offerRetry = true) {
|
|
32347
32430
|
if (!this.#messages) return;
|
|
32348
32431
|
this.#messages.querySelector(".conversation-error")?.remove();
|
|
32349
32432
|
const error51 = document.createElement("section");
|
|
32350
32433
|
error51.className = "conversation-error";
|
|
32351
|
-
error51.setAttribute("role", "alert");
|
|
32434
|
+
error51.setAttribute("role", offerRetry ? "alert" : "status");
|
|
32352
32435
|
const text3 = document.createElement("p");
|
|
32353
32436
|
text3.textContent = message;
|
|
32354
|
-
|
|
32355
|
-
|
|
32356
|
-
|
|
32357
|
-
|
|
32358
|
-
|
|
32437
|
+
error51.append(text3);
|
|
32438
|
+
if (offerRetry) {
|
|
32439
|
+
const retry = document.createElement("button");
|
|
32440
|
+
retry.type = "button";
|
|
32441
|
+
retry.textContent = this.#appearance.labels.reconnect;
|
|
32442
|
+
retry.addEventListener("click", () => this.reconnect());
|
|
32443
|
+
error51.append(retry);
|
|
32444
|
+
}
|
|
32359
32445
|
this.#messages.append(error51);
|
|
32360
32446
|
this.#revealLatest();
|
|
32361
32447
|
}
|