@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.
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  AssistantClientError,
3
3
  copyAssistantModelContext,
4
- createAssistantClient
5
- } from "./chunk-RITHSCTD.js";
4
+ createAssistantClient,
5
+ sessionSourceKey
6
+ } from "./chunk-NPV64PUX.js";
6
7
  import {
7
8
  _enum,
8
9
  _null,
@@ -6622,6 +6623,28 @@ function renderMarkdown(text2) {
6622
6623
  });
6623
6624
  }
6624
6625
 
6626
+ // src/sign-in-card.ts
6627
+ function createSignInCard(options) {
6628
+ const card = document.createElement("div");
6629
+ card.className = "noodle-assistant-card noodle-assistant-sign-in";
6630
+ card.dataset.tool = options.tool;
6631
+ card.setAttribute("role", "status");
6632
+ const heading = document.createElement("p");
6633
+ heading.className = "noodle-assistant-card-heading";
6634
+ heading.textContent = options.labels.heading;
6635
+ card.append(heading);
6636
+ const action = document.createElement("button");
6637
+ action.type = "button";
6638
+ action.className = "noodle-assistant-card-action";
6639
+ action.textContent = options.labels.action;
6640
+ action.addEventListener("click", () => {
6641
+ action.disabled = true;
6642
+ options.onSignIn();
6643
+ });
6644
+ card.append(action);
6645
+ return card;
6646
+ }
6647
+
6625
6648
  // src/element-event-controller.ts
6626
6649
  var AssistantElementEventController = class {
6627
6650
  #host;
@@ -6716,6 +6739,15 @@ var AssistantElementEventController = class {
6716
6739
  );
6717
6740
  return;
6718
6741
  }
6742
+ if (name === "auth_requested" && typeof data.continuation === "string") {
6743
+ this.#appendSignInRequest(
6744
+ String(data.id ?? ""),
6745
+ String(data.tool ?? "this"),
6746
+ data.continuation,
6747
+ String(data.expiresAt ?? "")
6748
+ );
6749
+ return;
6750
+ }
6719
6751
  if (name === "input_requested" && isRecord(data.requestedSchema)) {
6720
6752
  this.#appendInputRequest(
6721
6753
  String(data.id ?? ""),
@@ -6801,6 +6833,40 @@ var AssistantElementEventController = class {
6801
6833
  })
6802
6834
  );
6803
6835
  }
6836
+ /**
6837
+ * The host application owns the login. This renders the prompt and raises
6838
+ * `assistant-sign-in-requested`; the page signs the visitor in however it already does, then its
6839
+ * backend spends the continuation. Nothing here talks to an identity provider.
6840
+ */
6841
+ #appendSignInRequest(id, tool, continuation, expiresAt) {
6842
+ const messages = this.#host.messages();
6843
+ if (!messages) return;
6844
+ this.#proposalCards.get(id)?.remove();
6845
+ const card = createSignInCard({
6846
+ tool,
6847
+ continuation,
6848
+ expiresAt,
6849
+ labels: {
6850
+ heading: "Sign in to continue",
6851
+ action: "Sign in"
6852
+ },
6853
+ onSignIn: () => this.#host.element.dispatchEvent(
6854
+ new CustomEvent("assistant-sign-in-requested", {
6855
+ detail: { id, tool, continuation, expiresAt },
6856
+ bubbles: true,
6857
+ composed: true
6858
+ })
6859
+ )
6860
+ });
6861
+ messages.append(card);
6862
+ this.#proposalCards.set(id, card);
6863
+ this.#host.revealLatest();
6864
+ this.#host.element.dispatchEvent(
6865
+ new CustomEvent("assistant-sign-in-required", {
6866
+ detail: { id, tool, continuation, expiresAt }
6867
+ })
6868
+ );
6869
+ }
6804
6870
  #appendInputRequest(id, message, requestedSchema) {
6805
6871
  const messages = this.#host.messages();
6806
6872
  if (!messages || !id) return;
@@ -7620,6 +7686,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7620
7686
  #appearanceStyleSnapshot = /* @__PURE__ */ new Map();
7621
7687
  #scroll = new AssistantElementScrollController();
7622
7688
  sessionEndpoint = "";
7689
+ /** Public mount: the non-secret embed id `noodle deploy` printed. Mutually exclusive with the above. */
7690
+ embedId = "";
7691
+ /** Only for a dev or self-hosted service; the published snippet needs no origin. */
7692
+ serviceUrl = "";
7623
7693
  get appearance() {
7624
7694
  return this.#hostAppearance;
7625
7695
  }
@@ -7674,6 +7744,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7674
7744
  connectedCallback() {
7675
7745
  if (!this.shadowRoot) this.attachShadow({ mode: "open" });
7676
7746
  if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
7747
+ if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
7748
+ if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
7677
7749
  this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
7678
7750
  this.#media?.addEventListener("change", this.#handleSystemTheme);
7679
7751
  document.addEventListener("keydown", this.#handleDocumentKeydown);
@@ -7845,26 +7917,31 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7845
7917
  }
7846
7918
  };
7847
7919
  #ensureClient() {
7848
- if (this.#client && this.#clientEndpoint === this.sessionEndpoint) return this.#client;
7920
+ const key = sessionSourceKey(this);
7921
+ if (this.#client && this.#clientEndpoint === key) return this.#client;
7849
7922
  this.#client?.abort();
7850
7923
  this.#unsubscribeClient?.();
7851
7924
  this.#sessionBootstrap = void 0;
7852
- const client = createAssistantClient({
7853
- sessionEndpoint: this.sessionEndpoint,
7925
+ const behavior = {
7854
7926
  ...this.#context ? { context: this.#context } : {},
7855
7927
  clientContext: browserClientContext,
7856
7928
  ...this.#pageContext ? { pageContext: this.#pageContext } : {},
7857
7929
  ...this.#modelContext ? { modelContext: this.#modelContext } : {}
7858
- });
7930
+ };
7931
+ const client = this.embedId ? createAssistantClient({
7932
+ embedId: this.embedId,
7933
+ ...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
7934
+ ...behavior
7935
+ }) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
7859
7936
  this.#client = client;
7860
- this.#clientEndpoint = this.sessionEndpoint;
7937
+ this.#clientEndpoint = key;
7861
7938
  this.#unsubscribeClient = client.subscribe((event) => {
7862
7939
  if (this.#client === client) this.#handleClientEvent(event);
7863
7940
  });
7864
7941
  return client;
7865
7942
  }
7866
7943
  #primeSession() {
7867
- if (!this.sessionEndpoint) return;
7944
+ if (!this.sessionEndpoint && !this.embedId) return;
7868
7945
  const client = this.#ensureClient();
7869
7946
  const generation = this.#conversationGeneration;
7870
7947
  this.#setSessionState("loading");
@@ -7988,23 +8065,30 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7988
8065
  }
7989
8066
  const detail = error instanceof AssistantClientError ? error.detail : { code: fallbackCode, retryable: false };
7990
8067
  this.#appendRecoveryError(
7991
- detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable
8068
+ detail.code === "session_expired" ? this.#appearance.labels.sessionExpired : this.#appearance.labels.unavailable,
8069
+ // A spent or switched-off daily budget offers no retry. Reconnecting cannot succeed, and every
8070
+ // open tab trying is exactly the load the cap was set to refuse — so the button goes, not just
8071
+ // the alarming words. A spent *session* keeps it: reconnect mints a fresh one, which is the fix.
8072
+ detail.retryable !== false || detail.serviceCode === void 0
7992
8073
  );
7993
8074
  this.#dispatchError(detail);
7994
8075
  }
7995
- #appendRecoveryError(message) {
8076
+ #appendRecoveryError(message, offerRetry = true) {
7996
8077
  if (!this.#messages) return;
7997
8078
  this.#messages.querySelector(".conversation-error")?.remove();
7998
8079
  const error = document.createElement("section");
7999
8080
  error.className = "conversation-error";
8000
- error.setAttribute("role", "alert");
8081
+ error.setAttribute("role", offerRetry ? "alert" : "status");
8001
8082
  const text2 = document.createElement("p");
8002
8083
  text2.textContent = message;
8003
- const retry = document.createElement("button");
8004
- retry.type = "button";
8005
- retry.textContent = this.#appearance.labels.reconnect;
8006
- retry.addEventListener("click", () => this.reconnect());
8007
- error.append(text2, retry);
8084
+ error.append(text2);
8085
+ if (offerRetry) {
8086
+ const retry = document.createElement("button");
8087
+ retry.type = "button";
8088
+ retry.textContent = this.#appearance.labels.reconnect;
8089
+ retry.addEventListener("click", () => this.reconnect());
8090
+ error.append(retry);
8091
+ }
8008
8092
  this.#messages.append(error);
8009
8093
  this.#revealLatest();
8010
8094
  }
@@ -8221,4 +8305,4 @@ export {
8221
8305
  dompurify/dist/purify.es.mjs:
8222
8306
  (*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE *)
8223
8307
  */
8224
- //# sourceMappingURL=chunk-7WXGZJCA.js.map
8308
+ //# sourceMappingURL=chunk-7L52WJ2E.js.map