@streamoid/agent 0.6.21 → 0.6.22

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/index.js CHANGED
@@ -15537,26 +15537,53 @@ const useChatStore = create$1((e) => ({
15537
15537
  focusedBrowserSessionId: null
15538
15538
  }),
15539
15539
  setFocusedBrowserSessionId: (t) => e({ focusedBrowserSessionId: t }),
15540
- setArtifaxDreamerSession: (t, n) => e((o) => {
15541
- const s = upsertArtifaxDreamerConversationState(
15542
- o.artifaxDreamerByChatId,
15540
+ /* `reveal` IS THE DIFFERENCE BETWEEN OPENING AND RESTORING.
15541
+ *
15542
+ * Closing the pane is sticky on purpose: `handleToggleCanvas(false)` sets
15543
+ * `canvasOverride: 'closed'` so a session that is still alive does not pop
15544
+ * the pane straight back open. But this setter is also how a canvas OPENS —
15545
+ * the agent's `artifax_iframe_opened`, the transcript card's Open button —
15546
+ * and those were suppressed by the same stale flag. The pane stayed shut
15547
+ * with a live board behind it, the canvas never mounted, and whatever was
15548
+ * waiting on it (the agent's readiness probe, the card) hung: reported as
15549
+ * "click close, open it again and it is stuck opening".
15550
+ *
15551
+ * It cannot be inferred here. The restore on every SSE connect
15552
+ * (`loadArtifaxDreamerSession`) calls this with the same live session, so
15553
+ * clearing the flag unconditionally would reopen the pane on each reconnect
15554
+ * — the very thing the sticky close exists to prevent. So the callers that
15555
+ * mean "open" say so, and only they lift a close. */
15556
+ setArtifaxDreamerSession: (t, n, o) => e((s) => {
15557
+ const l = upsertArtifaxDreamerConversationState(
15558
+ s.artifaxDreamerByChatId,
15543
15559
  t,
15544
15560
  { session: n }
15545
- ), l = canvasPanelId(t), c = n?.active ? upsertPanel(o.panels, {
15546
- id: l,
15561
+ ), c = o?.reveal && n?.active && s.canvasOverride === "closed", f = canvasPanelId(t), h = n?.active ? upsertPanel(s.panels, {
15562
+ id: f,
15547
15563
  kind: "artifax_dreamer",
15548
15564
  chatId: t,
15549
15565
  title: "Canvas",
15550
15566
  width: "wide",
15551
- /* See the matching registration in ChatScreen: the pane draws the
15552
- * header so the canvas has a close button. */
15553
- ownsChrome: !1
15554
- }) : dropPanel(o.panels, l), f = resolveActivePanelId(
15555
- c,
15556
- o.activeChatId,
15557
- t === o.activeChatId && n?.active ? l : o.activePanelId
15567
+ /* THE CANVAS DRAWS ITS OWN HEADER. This said `false`, and because
15568
+ * `upsertPanel` lets an incoming panel overwrite the stored one, a
15569
+ * session arriving flipped the pane back to drawing its own bar —
15570
+ * undoing ChatScreen's `ownsChrome: true` every time a canvas
15571
+ * actually opened. That is the second "Canvas" title and the second
15572
+ * close above the panel's own, reported twice as not knowing which
15573
+ * close does what. The two registrations of this panel have to
15574
+ * agree; ChatScreen's comment carries the full reasoning. */
15575
+ ownsChrome: !0
15576
+ }) : dropPanel(s.panels, f), g = resolveActivePanelId(
15577
+ h,
15578
+ s.activeChatId,
15579
+ t === s.activeChatId && n?.active ? f : s.activePanelId
15558
15580
  );
15559
- return { artifaxDreamerByChatId: s, panels: c, activePanelId: f };
15581
+ return {
15582
+ artifaxDreamerByChatId: l,
15583
+ panels: h,
15584
+ activePanelId: g,
15585
+ ...c ? { canvasOverride: null } : null
15586
+ };
15560
15587
  }),
15561
15588
  clearArtifaxDreamerSession: (t) => e((n) => {
15562
15589
  if (!t) {
@@ -15733,7 +15760,13 @@ const useChatStore = create$1((e) => ({
15733
15760
  techpackSession: null,
15734
15761
  techpackStatus: null,
15735
15762
  panels: [],
15736
- activePanelId: null
15763
+ activePanelId: null,
15764
+ /* Reset means the initial state, and the initial state is "no override".
15765
+ * Leaving it behind carried a closed canvas across a sign-out or a
15766
+ * workspace switch — the pane then refused to open with no session and
15767
+ * no panel to explain why. ChatScreen also clears it per chat; that is
15768
+ * the per-conversation rule, not a substitute for this one. */
15769
+ canvasOverride: null
15737
15770
  })
15738
15771
  }));
15739
15772
  function getActivePanel(e, t) {
@@ -15799,19 +15832,11 @@ function subscribeToChatPrompts(e) {
15799
15832
  };
15800
15833
  }
15801
15834
  function sendToParent(e) {
15802
- if (window.parent && window.parent !== window) {
15803
- const t = getExpectedParentOrigin();
15804
- if (t) {
15805
- window.parent.postMessage(e, t);
15806
- return;
15807
- }
15808
- if (e.type === "chat:ready") {
15809
- window.parent.postMessage(e, "*");
15810
- return;
15811
- }
15812
- console.warn("[CopilotWidget] Skipping postMessage because parent origin is unknown", e.type);
15813
- }
15835
+ if (!(window.parent && window.parent !== window)) return !1;
15836
+ const t = getExpectedParentOrigin();
15837
+ return t ? (window.parent.postMessage(e, t), !0) : e.type === "chat:ready" ? (window.parent.postMessage(e, "*"), !0) : (console.warn("[CopilotWidget] Skipping postMessage because parent origin is unknown", e.type), !1);
15814
15838
  }
15839
+ const PARENT_ORIGIN_UNKNOWN = "the widget could not identify its host page (no parentOrigin query parameter and no usable document.referrer), so the request was never sent";
15815
15840
  function requestParentScreenshot(e = {}, t = {}) {
15816
15841
  const n = typeof e.requestId == "string" && e.requestId ? e.requestId : `${Date.now()}-${Math.random().toString(36).slice(2)}`, o = t.timeoutMs ?? 1e4;
15817
15842
  return new Promise((s, l) => {
@@ -15824,7 +15849,7 @@ function requestParentScreenshot(e = {}, t = {}) {
15824
15849
  ...e,
15825
15850
  requestId: n
15826
15851
  }
15827
- });
15852
+ }) || (window.clearTimeout(c), pendingScreenshotRequests.delete(n), l(new Error(PARENT_ORIGIN_UNKNOWN)));
15828
15853
  });
15829
15854
  }
15830
15855
  function isParentNavigationAvailable() {
@@ -15842,7 +15867,7 @@ function requestParentNavigation(e = {}, t = {}) {
15842
15867
  ...e,
15843
15868
  requestId: n
15844
15869
  }
15845
- });
15870
+ }) || (window.clearTimeout(c), pendingNavigationRequests.delete(n), l(new Error(PARENT_ORIGIN_UNKNOWN)));
15846
15871
  });
15847
15872
  }
15848
15873
  const CLAMP_LINES = 3;
@@ -17846,7 +17871,7 @@ function useSSE(e, t, n) {
17846
17871
  }
17847
17872
  if (_t.type === "artifax_iframe_opened") {
17848
17873
  const $t = artifaxEventConversationId(_t, Ve), Ht = normalizeArtifaxDreamerSession(_t);
17849
- Ht && Ae($t, Ht);
17874
+ Ht && Ae($t, Ht, { reveal: !0 });
17850
17875
  return;
17851
17876
  }
17852
17877
  if (_t.type === "artifax_send_prompt") {
@@ -17910,7 +17935,7 @@ function useSSE(e, t, n) {
17910
17935
  return;
17911
17936
  } else if (Ht.event === "artifax_iframe_opened") {
17912
17937
  const xt = artifaxEventConversationId(Ht, Ve), yn = normalizeArtifaxDreamerSession(Ht);
17913
- yn && Ae(xt, yn), $t();
17938
+ yn && Ae(xt, yn, { reveal: !0 }), $t();
17914
17939
  return;
17915
17940
  } else if (Ht.event === "artifax_send_prompt") {
17916
17941
  const xt = artifaxEventConversationId(Ht, Ve), yn = typeof Ht.session_id == "string" ? Ht.session_id : "", zt = typeof Ht.prompt == "string" ? Ht.prompt : "";
@@ -18047,7 +18072,7 @@ function ArtifaxCanvasCard({
18047
18072
  const E = await openArtifaxDreamerSession(n, e, t), M = normalizeArtifaxDreamerSession(
18048
18073
  E
18049
18074
  );
18050
- M && (f(e, M), reportArtifaxCanvasVisibility(n, e, !1));
18075
+ M && (f(e, M, { reveal: !0 }), reportArtifaxCanvasVisibility(n, e, !1));
18051
18076
  } finally {
18052
18077
  l(!1);
18053
18078
  }
@@ -128679,6 +128704,85 @@ for (const [e, t] of [
128679
128704
  });
128680
128705
  for (const e of ["capture_inline_screenshot", "navigate_parent"])
128681
128706
  registry.register({ name: e, service: "host", type: "react", blocking: !0 });
128707
+ function dataUrlToFile(e, t) {
128708
+ const [n, o] = e.split(",", 2), l = n.match(/^data:([^;]+);base64$/)?.[1] || "image/png", c = atob(o || ""), f = new Uint8Array(c.length);
128709
+ for (let h = 0; h < c.length; h += 1)
128710
+ f[h] = c.charCodeAt(h);
128711
+ return new File([f], t, { type: l });
128712
+ }
128713
+ function ownDocumentIsHostPage() {
128714
+ return !(window.parent && window.parent !== window);
128715
+ }
128716
+ async function loadHtml2Canvas() {
128717
+ return (await import("./html2canvas.esm-BWVIUcAF.js")).default;
128718
+ }
128719
+ async function captureIframeScreenshot() {
128720
+ const e = ownDocumentIsHostPage() ? "host_page" : "widget_iframe", t = Math.max(1, Math.ceil(window.innerWidth || 1024)), n = Math.max(1, Math.ceil(window.innerHeight || 768));
128721
+ console.info("[UIComponents] Rasterising own document", { source: e, width: t, height: n });
128722
+ try {
128723
+ const s = await (await loadHtml2Canvas())(document.documentElement, {
128724
+ /* The VIEWPORT, not the whole scrollable document: the question is about
128725
+ * what the user can see. */
128726
+ width: t,
128727
+ height: n,
128728
+ windowWidth: t,
128729
+ windowHeight: n,
128730
+ x: window.scrollX || 0,
128731
+ y: window.scrollY || 0,
128732
+ scale: 1,
128733
+ /* Fetch cross-origin images with CORS so they can be drawn. Without a
128734
+ * permissive `Access-Control-Allow-Origin` html2canvas skips the
128735
+ * individual image rather than tainting the whole canvas, which is the
128736
+ * behaviour that matters: one un-fetchable asset must not cost the
128737
+ * screenshot. */
128738
+ useCORS: !0,
128739
+ logging: !1,
128740
+ /* The chat is not part of the page the question is about, and on a
128741
+ * native mount it sits inside the document being rasterised. Two
128742
+ * selectors because it lives in two places: the panel root, and the
128743
+ * overlays it portals to `document.body` (menus, dialogs, the table-cell
128744
+ * modal), which are outside that root by construction. */
128745
+ ignoreElements: (l) => l.matches?.("[data-streamoid-agent], .sa-surface") ?? !1
128746
+ });
128747
+ return {
128748
+ dataUrl: s.toDataURL("image/png"),
128749
+ width: s.width,
128750
+ height: s.height,
128751
+ source: e
128752
+ };
128753
+ } catch (o) {
128754
+ const s = o instanceof Error ? o.message : String(o);
128755
+ throw console.error("[UIComponents] Own-document rasterisation failed", o), new Error(
128756
+ `The page could not be rasterised into a screenshot (${s}). No picture of the page is available for this turn; use the host application context in this conversation instead.`
128757
+ );
128758
+ }
128759
+ }
128760
+ async function captureScreenshotWithFallback(e) {
128761
+ if (!(window.parent && window.parent !== window))
128762
+ return captureIframeScreenshot();
128763
+ console.info("[UIComponents] Requesting host page screenshot");
128764
+ let n;
128765
+ try {
128766
+ n = await requestParentScreenshot(e, { timeoutMs: 2500 });
128767
+ } catch (o) {
128768
+ const s = o instanceof Error ? o.message : String(o);
128769
+ throw console.error("[UIComponents] Host page screenshot failed", o), new Error(
128770
+ `The host application did not provide a screenshot of the page: ${s}. The widget cannot photograph its host from inside an iframe, so there is no picture of the page for this turn.`
128771
+ );
128772
+ }
128773
+ if (!n.dataUrl)
128774
+ throw new Error(
128775
+ "The host application answered the screenshot request without an image."
128776
+ );
128777
+ return console.info("[UIComponents] Host page screenshot received"), {
128778
+ dataUrl: n.dataUrl,
128779
+ width: n.width,
128780
+ height: n.height,
128781
+ /* The host's own label is kept when it admits to a substitute: it knows
128782
+ * what it managed to photograph, and the backend reads that label. */
128783
+ source: n.source === "summary_canvas" || n.source === "widget_iframe" ? n.source : "host_page"
128784
+ };
128785
+ }
128682
128786
  const DISPLAY_ONLY = [
128683
128787
  "image_output_display_ui_component",
128684
128788
  "video_output_display_ui_component",
@@ -128709,105 +128813,6 @@ const DISPLAY_ONLY = [
128709
128813
  ugc: "UGC",
128710
128814
  "video-gen": "Video Generator"
128711
128815
  }, EMPTY_UI_PROPS = {}, inlineScreenshotResumeInFlight = /* @__PURE__ */ new Set(), inlineScreenshotResumeCompleted = /* @__PURE__ */ new Set(), parentNavigationResumeInFlight = /* @__PURE__ */ new Set(), parentNavigationResumeCompleted = /* @__PURE__ */ new Set();
128712
- function dataUrlToFile(e, t) {
128713
- const [n, o] = e.split(",", 2), l = n.match(/^data:([^;]+);base64$/)?.[1] || "image/png", c = atob(o || ""), f = new Uint8Array(c.length);
128714
- for (let h = 0; h < c.length; h += 1)
128715
- f[h] = c.charCodeAt(h);
128716
- return new File([f], t, { type: l });
128717
- }
128718
- function buildCanvasScreenshot({
128719
- width: e,
128720
- height: t,
128721
- title: n,
128722
- lines: o,
128723
- source: s
128724
- }) {
128725
- const l = document.createElement("canvas");
128726
- l.width = e, l.height = t;
128727
- const c = l.getContext("2d");
128728
- if (!c)
128729
- throw new Error("Canvas rendering is not available");
128730
- const f = document.documentElement.classList.contains("dark");
128731
- return c.fillStyle = f ? "#0b0b0f" : "#f6f7fb", c.fillRect(0, 0, e, t), c.fillStyle = f ? "#f5f5f5" : "#111827", c.font = "600 22px sans-serif", c.fillText(n, 24, 42), c.font = "14px sans-serif", c.fillStyle = f ? "#d1d5db" : "#4b5563", o.slice(0, 18).forEach((h, g) => {
128732
- c.fillText(h.slice(0, 120), 24, 76 + g * 24);
128733
- }), {
128734
- dataUrl: l.toDataURL("image/png"),
128735
- width: e,
128736
- height: t,
128737
- source: s
128738
- };
128739
- }
128740
- function ownDocumentIsHostPage() {
128741
- return !(window.parent && window.parent !== window);
128742
- }
128743
- async function captureIframeScreenshot() {
128744
- const e = ownDocumentIsHostPage() ? "host_page" : "widget_iframe";
128745
- console.info("[UIComponents] Capturing own document screenshot", { source: e });
128746
- const t = Math.max(1, Math.ceil(window.innerWidth || 1024)), n = Math.max(1, Math.ceil(window.innerHeight || 768)), o = document.documentElement.cloneNode(!0);
128747
- o.querySelectorAll("script").forEach((h) => h.remove()), o.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
128748
- const s = new XMLSerializer().serializeToString(o), l = `
128749
- <svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${n}">
128750
- <foreignObject width="100%" height="100%">${s}</foreignObject>
128751
- </svg>
128752
- `, c = new Blob([l], { type: "image/svg+xml;charset=utf-8" }), f = URL.createObjectURL(c);
128753
- try {
128754
- const h = new Image(), g = new Promise((j, E) => {
128755
- const M = window.setTimeout(
128756
- () => E(new Error("Widget iframe screenshot timed out")),
128757
- 2e3
128758
- );
128759
- h.onload = () => {
128760
- window.clearTimeout(M), j();
128761
- }, h.onerror = () => {
128762
- window.clearTimeout(M), E(new Error("Widget iframe screenshot failed to render"));
128763
- };
128764
- });
128765
- h.src = f, await g;
128766
- const b = document.createElement("canvas");
128767
- b.width = t, b.height = n;
128768
- const T = b.getContext("2d");
128769
- if (!T) throw new Error("Canvas rendering is not available");
128770
- return T.drawImage(h, 0, 0, t, n), {
128771
- dataUrl: b.toDataURL("image/png"),
128772
- width: t,
128773
- height: n,
128774
- source: e
128775
- };
128776
- } catch (h) {
128777
- return console.info("[UIComponents] DOM screenshot fallback failed; using summary canvas", h), buildCanvasScreenshot({
128778
- width: t,
128779
- height: n,
128780
- title: document.title || "Copilot Widget",
128781
- lines: [
128782
- `URL: ${window.location.href}`,
128783
- `Visible text: ${(document.body.innerText || "").replace(/\s+/g, " ").trim()}`
128784
- ],
128785
- /* Rasterisation failed and this is a canvas with the page's text drawn
128786
- * onto it. It is not a screenshot, so it does not claim to be one: the
128787
- * backend declines to describe it as the host page. */
128788
- source: "summary_canvas"
128789
- });
128790
- } finally {
128791
- URL.revokeObjectURL(f);
128792
- }
128793
- }
128794
- async function captureScreenshotWithFallback(e) {
128795
- if (window.parent && window.parent !== window)
128796
- try {
128797
- console.info("[UIComponents] Requesting host page screenshot");
128798
- const n = await requestParentScreenshot(e, { timeoutMs: 2500 });
128799
- if (n.dataUrl)
128800
- return console.info("[UIComponents] Host page screenshot received"), {
128801
- dataUrl: n.dataUrl,
128802
- width: n.width,
128803
- height: n.height,
128804
- source: n.source === "summary_canvas" || n.source === "widget_iframe" ? n.source : "host_page"
128805
- };
128806
- } catch (n) {
128807
- console.info("[UIComponents] Host screenshot unavailable; falling back to widget iframe capture", n);
128808
- }
128809
- return captureIframeScreenshot();
128810
- }
128811
128816
  function prettifyComponentName(e) {
128812
128817
  return e ? e.replace(/[:_]+/g, " ").replace(/\s+/g, " ").trim() : "component";
128813
128818
  }
@@ -135019,7 +135024,7 @@ function ChatScreen() {
135019
135024
  const en = normalizeArtifaxDreamerSession(
135020
135025
  Kt
135021
135026
  );
135022
- en ? (ke(Rt, en), Ne(Rt, null)) : (console.error("Canvas: dreamer session mint failed", Kt), Ne(Rt, "error"));
135027
+ en ? (ke(Rt, en, { reveal: !0 }), Ne(Rt, null)) : (console.error("Canvas: dreamer session mint failed", Kt), Ne(Rt, "error"));
135023
135028
  });
135024
135029
  }, [o, mn, Ne, ke]), Pi = useCallback(
135025
135030
  (Rt) => {
@@ -137073,7 +137078,15 @@ function AgentChatShell({ adapters: e }) {
137073
137078
  );
137074
137079
  }
137075
137080
  function AgentPanel({ adapters: e, className: t, style: n }) {
137076
- return /* @__PURE__ */ jsx("div", { className: t, style: { height: "100%", width: "100%", ...n }, children: /* @__PURE__ */ jsx(AgentChatShell, { adapters: e }) });
137081
+ return /* @__PURE__ */ jsx(
137082
+ "div",
137083
+ {
137084
+ "data-streamoid-agent": "panel",
137085
+ className: t,
137086
+ style: { height: "100%", width: "100%", ...n },
137087
+ children: /* @__PURE__ */ jsx(AgentChatShell, { adapters: e })
137088
+ }
137089
+ );
137077
137090
  }
137078
137091
  function groupConversationSearchResults(e) {
137079
137092
  const t = /* @__PURE__ */ new Map(), n = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamoid/agent",
3
- "version": "0.6.21",
3
+ "version": "0.6.22",
4
4
  "description": "Native (no-iframe) embeddable Streamoid agent chat panel. Host renders <AgentPanel adapters={\u2026}/>. Prebuilt bundle \u2014 source + build live in the stream-agent repo.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",