@noodleseed/assistant 1.17.0 → 1.19.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,9 +1,10 @@
1
1
  import {
2
2
  AssistantClientError,
3
3
  copyAssistantModelContext,
4
+ copyAssistantPageContext,
4
5
  createAssistantClient,
5
6
  sessionSourceKey
6
- } from "./chunk-RIKRQFKS.js";
7
+ } from "./chunk-S373FCAI.js";
7
8
  import {
8
9
  _enum,
9
10
  _null,
@@ -3523,6 +3524,8 @@ function radiusValue(radius) {
3523
3524
  }
3524
3525
 
3525
3526
  // src/browser-context.ts
3527
+ var PAGE_MARKDOWN_MAX_BYTES = 12 * 1024;
3528
+ var PAGE_MARKDOWN_TIMEOUT_MS = 2e3;
3526
3529
  function browserClientContext() {
3527
3530
  let timeZone;
3528
3531
  try {
@@ -3535,6 +3538,108 @@ function browserClientContext() {
3535
3538
  ...timeZone ? { timeZone } : {}
3536
3539
  };
3537
3540
  }
3541
+ function browserPageUrl() {
3542
+ try {
3543
+ const location = globalThis.location;
3544
+ if (!location) return void 0;
3545
+ const url = new URL(location.href);
3546
+ if (url.origin === "null") return void 0;
3547
+ return `${url.origin}${url.pathname}`;
3548
+ } catch {
3549
+ return void 0;
3550
+ }
3551
+ }
3552
+ async function browserPageContext(url) {
3553
+ let urlOnly;
3554
+ try {
3555
+ urlOnly = copyAssistantPageContext({ page: { url } });
3556
+ } catch {
3557
+ return void 0;
3558
+ }
3559
+ const controller = new AbortController();
3560
+ const timeout = globalThis.setTimeout(() => controller.abort(), PAGE_MARKDOWN_TIMEOUT_MS);
3561
+ try {
3562
+ const response = await fetch(url, {
3563
+ headers: { Accept: "text/markdown" },
3564
+ credentials: "omit",
3565
+ cache: "no-store",
3566
+ redirect: "error",
3567
+ referrerPolicy: "no-referrer",
3568
+ signal: controller.signal
3569
+ });
3570
+ if (!response.ok || !isSameOrigin(response, url) || !isMarkdown(response)) return urlOnly;
3571
+ const { content, truncated } = await readBoundedUtf8(response);
3572
+ const page = {
3573
+ page: {
3574
+ url,
3575
+ contentType: "text/markdown",
3576
+ content,
3577
+ ...truncated ? { truncated: true } : {}
3578
+ }
3579
+ };
3580
+ return copyAssistantPageContext(page);
3581
+ } catch {
3582
+ return urlOnly;
3583
+ } finally {
3584
+ globalThis.clearTimeout(timeout);
3585
+ }
3586
+ }
3587
+ function isSameOrigin(response, url) {
3588
+ try {
3589
+ return response.url !== "" && new URL(response.url).origin === new URL(url).origin;
3590
+ } catch {
3591
+ return false;
3592
+ }
3593
+ }
3594
+ function isMarkdown(response) {
3595
+ return response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "text/markdown";
3596
+ }
3597
+ async function readBoundedUtf8(response) {
3598
+ const reader = response.body?.getReader();
3599
+ if (!reader) throw new Error("Markdown response has no readable body");
3600
+ const chunks = [];
3601
+ let byteLength = 0;
3602
+ let truncated = false;
3603
+ try {
3604
+ while (true) {
3605
+ const { done, value } = await reader.read();
3606
+ if (done) break;
3607
+ const remaining = PAGE_MARKDOWN_MAX_BYTES - byteLength;
3608
+ if (value.byteLength > remaining) {
3609
+ if (remaining > 0) chunks.push(value.slice(0, remaining));
3610
+ truncated = true;
3611
+ await reader.cancel();
3612
+ break;
3613
+ }
3614
+ chunks.push(value);
3615
+ byteLength += value.byteLength;
3616
+ }
3617
+ } finally {
3618
+ reader.releaseLock();
3619
+ }
3620
+ const bytes = new Uint8Array(chunks.reduce((size, chunk) => size + chunk.byteLength, 0));
3621
+ let offset = 0;
3622
+ for (const chunk of chunks) {
3623
+ bytes.set(chunk, offset);
3624
+ offset += chunk.byteLength;
3625
+ }
3626
+ return { content: decodeUtf8(bytes, truncated), truncated };
3627
+ }
3628
+ function decodeUtf8(bytes, truncated) {
3629
+ const decoder = new TextDecoder("utf-8", { fatal: true });
3630
+ let content;
3631
+ try {
3632
+ content = decoder.decode(bytes, { stream: true });
3633
+ } catch {
3634
+ throw new Error("Markdown response is not valid UTF-8");
3635
+ }
3636
+ try {
3637
+ return content + decoder.decode();
3638
+ } catch {
3639
+ if (truncated) return content;
3640
+ throw new Error("Markdown response is not valid UTF-8");
3641
+ }
3642
+ }
3538
3643
 
3539
3644
  // src/input-request-card.ts
3540
3645
  function createInputRequestCard(options) {
@@ -6938,6 +7043,46 @@ function queryRequired(root, selector) {
6938
7043
  return element;
6939
7044
  }
6940
7045
 
7046
+ // src/element-page-context-controller.ts
7047
+ var AssistantElementPageContextController = class {
7048
+ #snapshot;
7049
+ #load;
7050
+ #isEnabled;
7051
+ constructor(isEnabled) {
7052
+ this.#isEnabled = isEnabled;
7053
+ }
7054
+ current() {
7055
+ const url = browserPageUrl();
7056
+ if (!url || this.#snapshot?.url !== url) {
7057
+ this.#snapshot = void 0;
7058
+ return void 0;
7059
+ }
7060
+ return this.#snapshot.context;
7061
+ }
7062
+ async refresh() {
7063
+ while (true) {
7064
+ const url = browserPageUrl();
7065
+ if (!url) return;
7066
+ if (this.#snapshot?.url === url) return;
7067
+ this.#snapshot = void 0;
7068
+ let load = this.#load;
7069
+ if (!load || load.url !== url) {
7070
+ const promise = browserPageContext(url).then((context) => {
7071
+ if (this.#isEnabled() && browserPageUrl() === url) {
7072
+ this.#snapshot = { url, context };
7073
+ }
7074
+ }).finally(() => {
7075
+ if (this.#load?.promise === promise) this.#load = void 0;
7076
+ });
7077
+ load = { url, promise };
7078
+ this.#load = load;
7079
+ }
7080
+ await load.promise;
7081
+ if (browserPageUrl() === url) return;
7082
+ }
7083
+ }
7084
+ };
7085
+
6941
7086
  // src/element-scroll-controller.ts
6942
7087
  var AssistantElementScrollController = class {
6943
7088
  #region;
@@ -7678,6 +7823,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7678
7823
  #context;
7679
7824
  #modelContext;
7680
7825
  #pageContext;
7826
+ #automaticPageContext = new AssistantElementPageContextController(
7827
+ () => !this.#pageContext
7828
+ );
7681
7829
  #pendingUserEcho;
7682
7830
  #sessionState = "idle";
7683
7831
  #startOpenApplied = false;
@@ -7756,10 +7904,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7756
7904
  if (this.hasAttribute("start-open")) {
7757
7905
  this.setAttribute("open", "");
7758
7906
  this.#startOpenApplied = true;
7759
- this.#primeSession();
7760
7907
  } else if (!this.embedId) {
7761
7908
  this.#primeSession();
7762
7909
  }
7910
+ this.#syncOpenState();
7763
7911
  }
7764
7912
  disconnectedCallback() {
7765
7913
  this.#conversationGeneration += 1;
@@ -7781,7 +7929,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7781
7929
  }
7782
7930
  open() {
7783
7931
  this.setAttribute("open", "");
7784
- this.#primeSession();
7785
7932
  this.dispatchEvent(new CustomEvent("assistant-open"));
7786
7933
  }
7787
7934
  close() {
@@ -7875,6 +8022,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7875
8022
  try {
7876
8023
  await this.#sessionBootstrap;
7877
8024
  if (generation !== this.#conversationGeneration) return;
8025
+ if (this.embedId && !this.#pageContext) await this.#automaticPageContext.refresh();
8026
+ if (generation !== this.#conversationGeneration) return;
7878
8027
  await this.#ensureClient().sendMessage(message);
7879
8028
  } catch (error) {
7880
8029
  if (generation !== this.#conversationGeneration) return;
@@ -7932,7 +8081,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7932
8081
  const behavior = {
7933
8082
  ...this.#context ? { context: this.#context } : {},
7934
8083
  clientContext: browserClientContext,
7935
- ...this.#pageContext ? { pageContext: this.#pageContext } : {},
8084
+ ...this.#pageContext ? { pageContext: this.#pageContext } : this.embedId ? { pageContext: () => this.#automaticPageContext.current() } : {},
7936
8085
  ...this.#modelContext ? { modelContext: this.#modelContext } : {}
7937
8086
  };
7938
8087
  const client = this.embedId ? createAssistantClient({
@@ -7950,6 +8099,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
7950
8099
  #primeSession() {
7951
8100
  if (!this.sessionEndpoint && !this.embedId) return;
7952
8101
  if (this.#sessionBootstrap || this.#client?.hasSession()) return;
8102
+ if (this.embedId && !this.#pageContext) void this.#automaticPageContext.refresh();
7953
8103
  const client = this.#ensureClient();
7954
8104
  const generation = this.#conversationGeneration;
7955
8105
  this.#setSessionState("loading");
@@ -8133,7 +8283,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
8133
8283
  syncAssistantSessionVisualState(this, this.shadowRoot, state, this.#appearance);
8134
8284
  }
8135
8285
  #syncOpenState() {
8136
- this.shadowRoot?.querySelector(".launcher")?.setAttribute("aria-expanded", String(this.hasAttribute("open")));
8286
+ const open = this.hasAttribute("open");
8287
+ this.shadowRoot?.querySelector(".launcher")?.setAttribute("aria-expanded", String(open));
8288
+ if (open && this.isConnected) this.#primeSession();
8137
8289
  }
8138
8290
  #closeAndRestoreFocus() {
8139
8291
  this.close();
@@ -8313,4 +8465,4 @@ export {
8313
8465
  dompurify/dist/purify.es.mjs:
8314
8466
  (*! @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 *)
8315
8467
  */
8316
- //# sourceMappingURL=chunk-MZNENVFY.js.map
8468
+ //# sourceMappingURL=chunk-RFETV5KQ.js.map