@noodleseed/assistant 1.14.0 → 1.16.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-7WXGZJCA.js → chunk-7L52WJ2E.js} +101 -17
- package/dist/chunk-7L52WJ2E.js.map +1 -0
- package/dist/{chunk-RITHSCTD.js → chunk-NPV64PUX.js} +94 -20
- package/dist/chunk-NPV64PUX.js.map +1 -0
- package/dist/{client-BKIeuSVS.d.cts → client-401BAJqk.d.cts} +42 -3
- package/dist/{client-BccAX_om.d.ts → client-Ii_xTV09.d.ts} +42 -3
- package/dist/client.cjs +89 -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 +189 -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 +100 -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 +198 -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-7WXGZJCA.js.map +0 -1
- package/dist/chunk-RITHSCTD.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -27092,6 +27092,11 @@ function toAssistantClientEvent(event) {
|
|
|
27092
27092
|
return event;
|
|
27093
27093
|
}
|
|
27094
27094
|
break;
|
|
27095
|
+
case "auth_requested":
|
|
27096
|
+
if (hasString(value, "id") && hasString(value, "tool") && hasString(value, "continuation") && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
|
|
27097
|
+
return event;
|
|
27098
|
+
}
|
|
27099
|
+
break;
|
|
27095
27100
|
case "interaction_resolved":
|
|
27096
27101
|
if (hasString(value, "id") && (value.action === void 0 || isInteractionAction(value.action)) && hasOptionalTurnId(value)) {
|
|
27097
27102
|
return event;
|
|
@@ -27276,6 +27281,24 @@ function hasToJsonProperty(value) {
|
|
|
27276
27281
|
return false;
|
|
27277
27282
|
}
|
|
27278
27283
|
|
|
27284
|
+
// src/session-source.ts
|
|
27285
|
+
var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
|
|
27286
|
+
var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
|
|
27287
|
+
function resolveSessionSource(options) {
|
|
27288
|
+
const { sessionEndpoint, embedId, serviceUrl } = options;
|
|
27289
|
+
if (Boolean(embedId) === Boolean(sessionEndpoint)) {
|
|
27290
|
+
throw new Error("pass either embedId or sessionEndpoint, not both and not neither");
|
|
27291
|
+
}
|
|
27292
|
+
if (embedId) {
|
|
27293
|
+
const base = (serviceUrl ?? NOODLE_CLOUD_URL).replace(/\/+$/, "");
|
|
27294
|
+
return { kind: "public", url: `${base}${PUBLIC_SESSION_PATH}`, embedId };
|
|
27295
|
+
}
|
|
27296
|
+
return { kind: "exchange", url: sessionEndpoint };
|
|
27297
|
+
}
|
|
27298
|
+
function sessionSourceKey(options) {
|
|
27299
|
+
return `${options.embedId ?? ""}|${options.serviceUrl ?? ""}|${options.sessionEndpoint ?? ""}`;
|
|
27300
|
+
}
|
|
27301
|
+
|
|
27279
27302
|
// src/transport.ts
|
|
27280
27303
|
var AssistantTransportError = class extends Error {
|
|
27281
27304
|
code = "invalid_response";
|
|
@@ -27403,6 +27426,19 @@ function isAbortError2(error51) {
|
|
|
27403
27426
|
}
|
|
27404
27427
|
|
|
27405
27428
|
// src/client.ts
|
|
27429
|
+
async function refusalCode(response) {
|
|
27430
|
+
try {
|
|
27431
|
+
const body = await response.clone().json();
|
|
27432
|
+
const code = body.code;
|
|
27433
|
+
return typeof code === "string" ? code : void 0;
|
|
27434
|
+
} catch {
|
|
27435
|
+
return void 0;
|
|
27436
|
+
}
|
|
27437
|
+
}
|
|
27438
|
+
var UNRETRYABLE_SERVICE_CODES = /* @__PURE__ */ new Set([
|
|
27439
|
+
"daily_turn_budget_exhausted",
|
|
27440
|
+
"daily_session_budget_exhausted"
|
|
27441
|
+
]);
|
|
27406
27442
|
var AssistantClientError = class extends Error {
|
|
27407
27443
|
detail;
|
|
27408
27444
|
constructor(detail, message, options) {
|
|
@@ -27412,7 +27448,7 @@ var AssistantClientError = class extends Error {
|
|
|
27412
27448
|
}
|
|
27413
27449
|
};
|
|
27414
27450
|
var DefaultAssistantClient = class {
|
|
27415
|
-
#
|
|
27451
|
+
#source;
|
|
27416
27452
|
#fetch;
|
|
27417
27453
|
#listeners = /* @__PURE__ */ new Set();
|
|
27418
27454
|
#chat = new AssistantChatStateStore();
|
|
@@ -27424,8 +27460,7 @@ var DefaultAssistantClient = class {
|
|
|
27424
27460
|
#active;
|
|
27425
27461
|
#pendingAppInteractions = /* @__PURE__ */ new Map();
|
|
27426
27462
|
constructor(options) {
|
|
27427
|
-
|
|
27428
|
-
this.#sessionEndpoint = options.sessionEndpoint;
|
|
27463
|
+
this.#source = resolveSessionSource(options);
|
|
27429
27464
|
this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
27430
27465
|
this.#context = options.context ? { ...options.context } : void 0;
|
|
27431
27466
|
this.#modelContext = options.modelContext ? copyAssistantModelContext(options.modelContext) : void 0;
|
|
@@ -27641,11 +27676,14 @@ var DefaultAssistantClient = class {
|
|
|
27641
27676
|
return;
|
|
27642
27677
|
}
|
|
27643
27678
|
if (!response.ok) {
|
|
27679
|
+
const serviceCode = await refusalCode(response);
|
|
27644
27680
|
throw clientError(
|
|
27645
27681
|
"turn_failed",
|
|
27646
27682
|
`Assistant turn failed (${response.status})`,
|
|
27647
27683
|
false,
|
|
27648
|
-
response.status
|
|
27684
|
+
response.status,
|
|
27685
|
+
void 0,
|
|
27686
|
+
serviceCode
|
|
27649
27687
|
);
|
|
27650
27688
|
}
|
|
27651
27689
|
await this.#consume(response);
|
|
@@ -27684,23 +27722,25 @@ var DefaultAssistantClient = class {
|
|
|
27684
27722
|
}
|
|
27685
27723
|
}
|
|
27686
27724
|
async #createSession(signal) {
|
|
27687
|
-
const
|
|
27688
|
-
|
|
27689
|
-
|
|
27690
|
-
|
|
27691
|
-
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
|
|
27695
|
-
|
|
27696
|
-
|
|
27697
|
-
);
|
|
27725
|
+
const source = this.#source;
|
|
27726
|
+
const response = await this.#requestSession(source, {
|
|
27727
|
+
method: "POST",
|
|
27728
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
27729
|
+
body: JSON.stringify(
|
|
27730
|
+
source.kind === "public" ? { embedId: source.embedId } : this.#context ? { context: this.#context } : {}
|
|
27731
|
+
),
|
|
27732
|
+
credentials: source.kind === "public" ? "omit" : "same-origin",
|
|
27733
|
+
signal
|
|
27734
|
+
});
|
|
27698
27735
|
if (!response.ok) {
|
|
27736
|
+
const serviceCode = await refusalCode(response);
|
|
27699
27737
|
throw clientError(
|
|
27700
27738
|
"session_failed",
|
|
27701
27739
|
`Assistant session failed (${response.status})`,
|
|
27702
|
-
|
|
27703
|
-
response.status
|
|
27740
|
+
serviceCode === void 0 || !UNRETRYABLE_SERVICE_CODES.has(serviceCode),
|
|
27741
|
+
response.status,
|
|
27742
|
+
void 0,
|
|
27743
|
+
serviceCode
|
|
27704
27744
|
);
|
|
27705
27745
|
}
|
|
27706
27746
|
let value;
|
|
@@ -27757,6 +27797,34 @@ var DefaultAssistantClient = class {
|
|
|
27757
27797
|
);
|
|
27758
27798
|
}
|
|
27759
27799
|
}
|
|
27800
|
+
/**
|
|
27801
|
+
* The mint, with the one failure a public page hits that an in-app embed cannot.
|
|
27802
|
+
*
|
|
27803
|
+
* A page that loads the embed script and then blocks `connect-src` fails here as a bare network
|
|
27804
|
+
* rejection — no status, no body, nothing to read. "Assistant request failed" sends a developer
|
|
27805
|
+
* hunting through their own code; naming the directive and the origin ends the search. The message
|
|
27806
|
+
* covers a plain outage too, because from inside the browser the two are indistinguishable.
|
|
27807
|
+
*/
|
|
27808
|
+
async #requestSession(source, init) {
|
|
27809
|
+
try {
|
|
27810
|
+
return await this.#fetch(source.url, init);
|
|
27811
|
+
} catch (error51) {
|
|
27812
|
+
if (init.signal?.aborted) throw error51;
|
|
27813
|
+
if (source.kind !== "public") {
|
|
27814
|
+
throw clientError("session_failed", "Assistant request failed", true, void 0, {
|
|
27815
|
+
cause: error51
|
|
27816
|
+
});
|
|
27817
|
+
}
|
|
27818
|
+
throw clientError(
|
|
27819
|
+
"session_failed",
|
|
27820
|
+
`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).`,
|
|
27821
|
+
true,
|
|
27822
|
+
void 0,
|
|
27823
|
+
{ cause: error51 },
|
|
27824
|
+
"blocked_by_page"
|
|
27825
|
+
);
|
|
27826
|
+
}
|
|
27827
|
+
}
|
|
27760
27828
|
async #request(input, init, failureCode) {
|
|
27761
27829
|
try {
|
|
27762
27830
|
return await this.#fetch(input, init);
|
|
@@ -27878,9 +27946,14 @@ async function readStableErrorCode(response) {
|
|
|
27878
27946
|
if (!isRecord4(value) || typeof value.code !== "string") return void 0;
|
|
27879
27947
|
return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
|
|
27880
27948
|
}
|
|
27881
|
-
function clientError(code, message, retryable, status, options) {
|
|
27949
|
+
function clientError(code, message, retryable, status, options, serviceCode) {
|
|
27882
27950
|
return new AssistantClientError(
|
|
27883
|
-
{
|
|
27951
|
+
{
|
|
27952
|
+
code,
|
|
27953
|
+
...status === void 0 ? {} : { status },
|
|
27954
|
+
retryable,
|
|
27955
|
+
...serviceCode === void 0 ? {} : { serviceCode }
|
|
27956
|
+
},
|
|
27884
27957
|
message,
|
|
27885
27958
|
options
|
|
27886
27959
|
);
|
|
@@ -30973,6 +31046,28 @@ function renderMarkdown(text3) {
|
|
|
30973
31046
|
});
|
|
30974
31047
|
}
|
|
30975
31048
|
|
|
31049
|
+
// src/sign-in-card.ts
|
|
31050
|
+
function createSignInCard(options) {
|
|
31051
|
+
const card = document.createElement("div");
|
|
31052
|
+
card.className = "noodle-assistant-card noodle-assistant-sign-in";
|
|
31053
|
+
card.dataset.tool = options.tool;
|
|
31054
|
+
card.setAttribute("role", "status");
|
|
31055
|
+
const heading = document.createElement("p");
|
|
31056
|
+
heading.className = "noodle-assistant-card-heading";
|
|
31057
|
+
heading.textContent = options.labels.heading;
|
|
31058
|
+
card.append(heading);
|
|
31059
|
+
const action = document.createElement("button");
|
|
31060
|
+
action.type = "button";
|
|
31061
|
+
action.className = "noodle-assistant-card-action";
|
|
31062
|
+
action.textContent = options.labels.action;
|
|
31063
|
+
action.addEventListener("click", () => {
|
|
31064
|
+
action.disabled = true;
|
|
31065
|
+
options.onSignIn();
|
|
31066
|
+
});
|
|
31067
|
+
card.append(action);
|
|
31068
|
+
return card;
|
|
31069
|
+
}
|
|
31070
|
+
|
|
30976
31071
|
// src/element-event-controller.ts
|
|
30977
31072
|
var AssistantElementEventController = class {
|
|
30978
31073
|
#host;
|
|
@@ -31067,6 +31162,15 @@ var AssistantElementEventController = class {
|
|
|
31067
31162
|
);
|
|
31068
31163
|
return;
|
|
31069
31164
|
}
|
|
31165
|
+
if (name21 === "auth_requested" && typeof data.continuation === "string") {
|
|
31166
|
+
this.#appendSignInRequest(
|
|
31167
|
+
String(data.id ?? ""),
|
|
31168
|
+
String(data.tool ?? "this"),
|
|
31169
|
+
data.continuation,
|
|
31170
|
+
String(data.expiresAt ?? "")
|
|
31171
|
+
);
|
|
31172
|
+
return;
|
|
31173
|
+
}
|
|
31070
31174
|
if (name21 === "input_requested" && isRecord5(data.requestedSchema)) {
|
|
31071
31175
|
this.#appendInputRequest(
|
|
31072
31176
|
String(data.id ?? ""),
|
|
@@ -31152,6 +31256,40 @@ var AssistantElementEventController = class {
|
|
|
31152
31256
|
})
|
|
31153
31257
|
);
|
|
31154
31258
|
}
|
|
31259
|
+
/**
|
|
31260
|
+
* The host application owns the login. This renders the prompt and raises
|
|
31261
|
+
* `assistant-sign-in-requested`; the page signs the visitor in however it already does, then its
|
|
31262
|
+
* backend spends the continuation. Nothing here talks to an identity provider.
|
|
31263
|
+
*/
|
|
31264
|
+
#appendSignInRequest(id, tool, continuation, expiresAt) {
|
|
31265
|
+
const messages = this.#host.messages();
|
|
31266
|
+
if (!messages) return;
|
|
31267
|
+
this.#proposalCards.get(id)?.remove();
|
|
31268
|
+
const card = createSignInCard({
|
|
31269
|
+
tool,
|
|
31270
|
+
continuation,
|
|
31271
|
+
expiresAt,
|
|
31272
|
+
labels: {
|
|
31273
|
+
heading: "Sign in to continue",
|
|
31274
|
+
action: "Sign in"
|
|
31275
|
+
},
|
|
31276
|
+
onSignIn: () => this.#host.element.dispatchEvent(
|
|
31277
|
+
new CustomEvent("assistant-sign-in-requested", {
|
|
31278
|
+
detail: { id, tool, continuation, expiresAt },
|
|
31279
|
+
bubbles: true,
|
|
31280
|
+
composed: true
|
|
31281
|
+
})
|
|
31282
|
+
)
|
|
31283
|
+
});
|
|
31284
|
+
messages.append(card);
|
|
31285
|
+
this.#proposalCards.set(id, card);
|
|
31286
|
+
this.#host.revealLatest();
|
|
31287
|
+
this.#host.element.dispatchEvent(
|
|
31288
|
+
new CustomEvent("assistant-sign-in-required", {
|
|
31289
|
+
detail: { id, tool, continuation, expiresAt }
|
|
31290
|
+
})
|
|
31291
|
+
);
|
|
31292
|
+
}
|
|
31155
31293
|
#appendInputRequest(id, message, requestedSchema) {
|
|
31156
31294
|
const messages = this.#host.messages();
|
|
31157
31295
|
if (!messages || !id) return;
|
|
@@ -31971,6 +32109,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
31971
32109
|
#appearanceStyleSnapshot = /* @__PURE__ */ new Map();
|
|
31972
32110
|
#scroll = new AssistantElementScrollController();
|
|
31973
32111
|
sessionEndpoint = "";
|
|
32112
|
+
/** Public mount: the non-secret embed id `noodle deploy` printed. Mutually exclusive with the above. */
|
|
32113
|
+
embedId = "";
|
|
32114
|
+
/** Only for a dev or self-hosted service; the published snippet needs no origin. */
|
|
32115
|
+
serviceUrl = "";
|
|
31974
32116
|
get appearance() {
|
|
31975
32117
|
return this.#hostAppearance;
|
|
31976
32118
|
}
|
|
@@ -32025,6 +32167,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32025
32167
|
connectedCallback() {
|
|
32026
32168
|
if (!this.shadowRoot) this.attachShadow({ mode: "open" });
|
|
32027
32169
|
if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
|
|
32170
|
+
if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
|
|
32171
|
+
if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
|
|
32028
32172
|
this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
|
|
32029
32173
|
this.#media?.addEventListener("change", this.#handleSystemTheme);
|
|
32030
32174
|
document.addEventListener("keydown", this.#handleDocumentKeydown);
|
|
@@ -32196,26 +32340,31 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32196
32340
|
}
|
|
32197
32341
|
};
|
|
32198
32342
|
#ensureClient() {
|
|
32199
|
-
|
|
32343
|
+
const key = sessionSourceKey(this);
|
|
32344
|
+
if (this.#client && this.#clientEndpoint === key) return this.#client;
|
|
32200
32345
|
this.#client?.abort();
|
|
32201
32346
|
this.#unsubscribeClient?.();
|
|
32202
32347
|
this.#sessionBootstrap = void 0;
|
|
32203
|
-
const
|
|
32204
|
-
sessionEndpoint: this.sessionEndpoint,
|
|
32348
|
+
const behavior = {
|
|
32205
32349
|
...this.#context ? { context: this.#context } : {},
|
|
32206
32350
|
clientContext: browserClientContext,
|
|
32207
32351
|
...this.#pageContext ? { pageContext: this.#pageContext } : {},
|
|
32208
32352
|
...this.#modelContext ? { modelContext: this.#modelContext } : {}
|
|
32209
|
-
}
|
|
32353
|
+
};
|
|
32354
|
+
const client = this.embedId ? createAssistantClient({
|
|
32355
|
+
embedId: this.embedId,
|
|
32356
|
+
...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
|
|
32357
|
+
...behavior
|
|
32358
|
+
}) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
|
|
32210
32359
|
this.#client = client;
|
|
32211
|
-
this.#clientEndpoint =
|
|
32360
|
+
this.#clientEndpoint = key;
|
|
32212
32361
|
this.#unsubscribeClient = client.subscribe((event) => {
|
|
32213
32362
|
if (this.#client === client) this.#handleClientEvent(event);
|
|
32214
32363
|
});
|
|
32215
32364
|
return client;
|
|
32216
32365
|
}
|
|
32217
32366
|
#primeSession() {
|
|
32218
|
-
if (!this.sessionEndpoint) return;
|
|
32367
|
+
if (!this.sessionEndpoint && !this.embedId) return;
|
|
32219
32368
|
const client = this.#ensureClient();
|
|
32220
32369
|
const generation = this.#conversationGeneration;
|
|
32221
32370
|
this.#setSessionState("loading");
|
|
@@ -32339,23 +32488,30 @@ var NoodleAssistantElement = class extends HTMLElementBase {
|
|
|
32339
32488
|
}
|
|
32340
32489
|
const detail = error51 instanceof AssistantClientError ? error51.detail : { code: fallbackCode, retryable: false };
|
|
32341
32490
|
this.#appendRecoveryError(
|
|
32342
|
-
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable
|
|
32491
|
+
detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable,
|
|
32492
|
+
// A spent or switched-off daily budget offers no retry. Reconnecting cannot succeed, and every
|
|
32493
|
+
// open tab trying is exactly the load the cap was set to refuse — so the button goes, not just
|
|
32494
|
+
// the alarming words. A spent *session* keeps it: reconnect mints a fresh one, which is the fix.
|
|
32495
|
+
detail.retryable !== false || detail.serviceCode === void 0
|
|
32343
32496
|
);
|
|
32344
32497
|
this.#dispatchError(detail);
|
|
32345
32498
|
}
|
|
32346
|
-
#appendRecoveryError(message) {
|
|
32499
|
+
#appendRecoveryError(message, offerRetry = true) {
|
|
32347
32500
|
if (!this.#messages) return;
|
|
32348
32501
|
this.#messages.querySelector(".conversation-error")?.remove();
|
|
32349
32502
|
const error51 = document.createElement("section");
|
|
32350
32503
|
error51.className = "conversation-error";
|
|
32351
|
-
error51.setAttribute("role", "alert");
|
|
32504
|
+
error51.setAttribute("role", offerRetry ? "alert" : "status");
|
|
32352
32505
|
const text3 = document.createElement("p");
|
|
32353
32506
|
text3.textContent = message;
|
|
32354
|
-
|
|
32355
|
-
|
|
32356
|
-
|
|
32357
|
-
|
|
32358
|
-
|
|
32507
|
+
error51.append(text3);
|
|
32508
|
+
if (offerRetry) {
|
|
32509
|
+
const retry = document.createElement("button");
|
|
32510
|
+
retry.type = "button";
|
|
32511
|
+
retry.textContent = this.#appearance.labels.reconnect;
|
|
32512
|
+
retry.addEventListener("click", () => this.reconnect());
|
|
32513
|
+
error51.append(retry);
|
|
32514
|
+
}
|
|
32359
32515
|
this.#messages.append(error51);
|
|
32360
32516
|
this.#revealLatest();
|
|
32361
32517
|
}
|