@pluno/product-agent-web 0.1.21 → 0.1.23

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.d.ts CHANGED
@@ -44,6 +44,7 @@ export type ProductAgentState = {
44
44
  assistantDraft: string;
45
45
  isThinking: boolean;
46
46
  lastError: string | null;
47
+ lastErrorCode: string | null;
47
48
  };
48
49
  export type ProductAgentInitOptions = {
49
50
  token?: string;
@@ -55,6 +56,8 @@ export type ProductAgentInitOptions = {
55
56
  autoConnect?: boolean;
56
57
  initialStarterPrompts?: string[];
57
58
  webSocketFactory?: ProductAgentWebSocketFactory;
59
+ restorePersistedState?: boolean;
60
+ expectedPersistedSessionId?: string | null;
58
61
  };
59
62
  export type ProductAgentWebSocket = {
60
63
  readyState: number;
@@ -6,7 +6,7 @@ class q {
6
6
  ...this.state,
7
7
  starterPrompts: et(t.initialStarterPrompts)
8
8
  };
9
- const s = It(t.clientId);
9
+ const s = t.restorePersistedState === !1 ? null : It(t.clientId, t.expectedPersistedSessionId);
10
10
  s && (this.state = {
11
11
  ...this.state,
12
12
  ...s,
@@ -15,7 +15,7 @@ class q {
15
15
  assistantDraft: "",
16
16
  isThinking: !1,
17
17
  lastError: null
18
- }), this.queuedClientEvents = Et(t.clientId);
18
+ }), this.queuedClientEvents = _t(t.clientId);
19
19
  }
20
20
  options;
21
21
  listeners = {};
@@ -45,7 +45,8 @@ class q {
45
45
  messages: [],
46
46
  assistantDraft: "",
47
47
  isThinking: !1,
48
- lastError: null
48
+ lastError: null,
49
+ lastErrorCode: null
49
50
  };
50
51
  static async init(t) {
51
52
  const s = new q({
@@ -80,7 +81,7 @@ class q {
80
81
  throw new Error("Pluno requires a token or tokenProvider");
81
82
  const t = Z(this.options.backendUrl), s = this.options.webSocketFactory?.(t) ?? new WebSocket(t);
82
83
  this.socket = s, s.addEventListener("open", () => {
83
- this.socket === s && (l("web-sdk.agent", "Pluno socket opened", {
84
+ this.socket === s && (u("web-sdk.agent", "Pluno socket opened", {
84
85
  queuedClientEventCount: this.queuedClientEvents.length,
85
86
  isThinking: this.state.isThinking
86
87
  }), this.token && this.sendNow({
@@ -92,7 +93,7 @@ class q {
92
93
  }), s.addEventListener("message", (n) => {
93
94
  this.socket === s && this.handleServerEvent(Q(n.data));
94
95
  }), s.addEventListener("close", (n) => {
95
- this.socket === s && (this.stopHeartbeat(), this.socket = null, this.state.status !== "closed" && (l("web-sdk.agent", "Pluno socket closed; scheduling reconnect", {
96
+ this.socket === s && (this.stopHeartbeat(), this.socket = null, this.state.status !== "closed" && (u("web-sdk.agent", "Pluno socket closed; scheduling reconnect", {
96
97
  code: typeof n?.code == "number" ? n.code : null,
97
98
  reason: typeof n?.reason == "string" ? n.reason : "",
98
99
  wasClean: typeof n?.wasClean == "boolean" ? n.wasClean : null,
@@ -100,7 +101,7 @@ class q {
100
101
  isThinking: this.state.isThinking
101
102
  }), this.setState({ status: "reconnecting", lastError: null }), this.scheduleReconnect()));
102
103
  }), s.addEventListener("error", () => {
103
- this.socket !== s || this.state.status === "closed" || (l("web-sdk.agent", "Pluno socket error; scheduling reconnect", {
104
+ this.socket !== s || this.state.status === "closed" || (u("web-sdk.agent", "Pluno socket error; scheduling reconnect", {
104
105
  queuedClientEventCount: this.queuedClientEvents.length,
105
106
  isThinking: this.state.isThinking
106
107
  }), this.stopHeartbeat(), this.socket = null, this.setState({ status: "reconnecting", lastError: null }), this.scheduleReconnect(), s.readyState !== WebSocket.CLOSED && s.readyState !== WebSocket.CLOSING && s.close());
@@ -170,7 +171,7 @@ class q {
170
171
  try {
171
172
  return s.send(JSON.stringify(t)), !0;
172
173
  } catch (n) {
173
- return l("web-sdk.agent", "Pluno socket send failed; scheduling reconnect", {
174
+ return u("web-sdk.agent", "Pluno socket send failed; scheduling reconnect", {
174
175
  message: n instanceof Error ? n.message : String(n),
175
176
  queuedClientEventCount: this.queuedClientEvents.length,
176
177
  isThinking: this.state.isThinking
@@ -194,13 +195,13 @@ class q {
194
195
  handleServerEvent(t) {
195
196
  if (J(t) && this.markThinkingProgress(), t.type === "auth.ok") {
196
197
  const s = st(t), n = Object.prototype.hasOwnProperty.call(t, "appearance");
197
- l("web-sdk.agent", "Received auth.ok appearance", {
198
+ u("web-sdk.agent", "Received auth.ok appearance", {
198
199
  hasAppearance: n,
199
200
  rawAppearance: t.appearance,
200
201
  normalizedAppearance: s
201
202
  }), this.runtimeHelperJavascript = nt(t.runtimeHelpers)?.javascript ?? null;
202
203
  const r = tt(t, "starterPrompts"), i = Object.prototype.hasOwnProperty.call(t, "starterPrompts"), a = {
203
- user: ut(t.user),
204
+ user: lt(t.user),
204
205
  status: "connected"
205
206
  };
206
207
  (i || this.state.starterPrompts.length === 0) && (a.starterPrompts = r), (n || s) && (a.appearance = s), this.setState(a), this.state.sessionId && this.send({
@@ -215,7 +216,7 @@ class q {
215
216
  const s = Array.isArray(t.items) ? t.items : [];
216
217
  Tt(s) && this.clearRetryTimers(), this.setState({
217
218
  sessionId: P(t.session, "id") ?? this.state.sessionId,
218
- messages: lt(s),
219
+ messages: ut(s),
219
220
  ...b(s) ? { assistantDraft: "", isThinking: !1 } : {}
220
221
  }), b(s) && this.clearThinkingWatchdog();
221
222
  return;
@@ -251,7 +252,12 @@ class q {
251
252
  if (this.retryAfterRetryableError(t))
252
253
  return;
253
254
  const s = new Error(typeof t.message == "string" ? t.message.replace(/Product Agent/g, "Pluno") : "Pluno error");
254
- this.setState({ status: "error", isThinking: !1, lastError: s.message }), this.clearThinkingWatchdog(), this.emit("error", s);
255
+ this.setState({
256
+ status: "error",
257
+ isThinking: !1,
258
+ lastError: s.message,
259
+ lastErrorCode: typeof t.code == "string" ? t.code : null
260
+ }), this.clearThinkingWatchdog(), this.emit("error", s);
255
261
  }
256
262
  }
257
263
  retryAfterRetryableError(t) {
@@ -292,14 +298,14 @@ class q {
292
298
  return;
293
299
  const t = this.activeClientMessageId;
294
300
  if (!t) {
295
- l("web-sdk.agent", "Pluno thinking watchdog resyncing without active message id", {
301
+ u("web-sdk.agent", "Pluno thinking watchdog resyncing without active message id", {
296
302
  sessionId: this.state.sessionId
297
303
  }), this.resyncThinkingSession(), this.scheduleThinkingWatchdog(6e4);
298
304
  return;
299
305
  }
300
306
  const s = this.thinkingWatchdogResyncAttemptsByClientMessageId[t] ?? 0, n = this.thinkingWatchdogRetryAttemptsByClientMessageId[t] ?? 0;
301
307
  if (s <= n) {
302
- this.thinkingWatchdogResyncAttemptsByClientMessageId[t] = s + 1, l("web-sdk.agent", "Pluno thinking watchdog resyncing session", {
308
+ this.thinkingWatchdogResyncAttemptsByClientMessageId[t] = s + 1, u("web-sdk.agent", "Pluno thinking watchdog resyncing session", {
303
309
  sessionId: this.state.sessionId,
304
310
  clientMessageId: t,
305
311
  resyncAttempts: s + 1
@@ -307,7 +313,7 @@ class q {
307
313
  return;
308
314
  }
309
315
  if (n >= 2) {
310
- l("web-sdk.agent", "Pluno thinking watchdog retry limit reached; continuing resync", {
316
+ u("web-sdk.agent", "Pluno thinking watchdog retry limit reached; continuing resync", {
311
317
  sessionId: this.state.sessionId,
312
318
  clientMessageId: t,
313
319
  retryAttempts: n
@@ -318,7 +324,7 @@ class q {
318
324
  status: this.state.status === "reconnecting" ? "reconnecting" : "connected",
319
325
  isThinking: !0,
320
326
  lastError: null
321
- }), l("web-sdk.agent", "Pluno thinking watchdog retrying last user message", {
327
+ }), u("web-sdk.agent", "Pluno thinking watchdog retrying last user message", {
322
328
  sessionId: this.state.sessionId,
323
329
  clientMessageId: t,
324
330
  retryAttempts: n + 1
@@ -369,7 +375,7 @@ class q {
369
375
  return;
370
376
  const t = window.fetch.bind(window), s = XMLHttpRequest.prototype.open, n = XMLHttpRequest.prototype.setRequestHeader, r = XMLHttpRequest.prototype.send;
371
377
  window.fetch = async (i, a) => {
372
- const o = Date.now(), c = new Date(o).toISOString(), u = i instanceof Request ? i : null, y = typeof i == "string" ? i : i instanceof URL ? i.toString() : u?.url ?? "", w = (a?.method ?? u?.method ?? "GET").toUpperCase(), A = O(new Headers(a?.headers ?? u?.headers ?? void 0)), C = await Rt(u, a);
378
+ const o = Date.now(), c = new Date(o).toISOString(), l = i instanceof Request ? i : null, y = typeof i == "string" ? i : i instanceof URL ? i.toString() : l?.url ?? "", w = (a?.method ?? l?.method ?? "GET").toUpperCase(), A = O(new Headers(a?.headers ?? l?.headers ?? void 0)), C = await Rt(l, a);
373
379
  try {
374
380
  const d = await t(i, a);
375
381
  return this.enqueueNetworkEvent({
@@ -398,7 +404,7 @@ class q {
398
404
  durationMs: Date.now() - o
399
405
  }), d;
400
406
  }
401
- }, XMLHttpRequest.prototype.open = function(a, o, c, u, y) {
407
+ }, XMLHttpRequest.prototype.open = function(a, o, c, l, y) {
402
408
  return this.__plunoMeta = {
403
409
  requestId: T("xhr"),
404
410
  method: String(a ?? "GET").toUpperCase(),
@@ -406,12 +412,12 @@ class q {
406
412
  requestHeaders: {},
407
413
  startedAtMs: Date.now(),
408
414
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
409
- }, s.call(this, a, o, c ?? !0, u ?? void 0, y ?? void 0);
415
+ }, s.call(this, a, o, c ?? !0, l ?? void 0, y ?? void 0);
410
416
  }, XMLHttpRequest.prototype.setRequestHeader = function(a, o) {
411
417
  const c = this.__plunoMeta;
412
418
  if (c) {
413
- const u = c.requestHeaders ?? {};
414
- u[a] = o, c.requestHeaders = u;
419
+ const l = c.requestHeaders ?? {};
420
+ l[a] = o, c.requestHeaders = l;
415
421
  }
416
422
  return n.call(this, a, o);
417
423
  }, XMLHttpRequest.prototype.send = function(a) {
@@ -462,7 +468,7 @@ class q {
462
468
  scheduleReconnect() {
463
469
  this.reconnectTimer !== null || this.state.status === "closed" || (this.reconnectTimer = window.setTimeout(() => {
464
470
  this.reconnectTimer = null, this.connect().catch((t) => {
465
- l("web-sdk.agent", "Pluno reconnect failed; retrying", {
471
+ u("web-sdk.agent", "Pluno reconnect failed; retrying", {
466
472
  message: t instanceof Error ? t.message : String(t),
467
473
  queuedClientEventCount: this.queuedClientEvents.length,
468
474
  isThinking: this.state.isThinking
@@ -479,13 +485,17 @@ class q {
479
485
  this.heartbeatTimer !== null && (window.clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
480
486
  }
481
487
  setState(t) {
482
- this.state = { ...this.state, ...t }, _t(this.options.clientId, this.state), this.emit("state", this.getState());
488
+ this.state = {
489
+ ...this.state,
490
+ ...t,
491
+ ...t.lastError === null && t.lastErrorCode === void 0 ? { lastErrorCode: null } : {}
492
+ }, Et(this.options.clientId, this.state), this.emit("state", this.getState());
483
493
  }
484
494
  emit(t, s) {
485
495
  this.listeners[t]?.forEach((r) => r(s));
486
496
  }
487
497
  }
488
- function l(e, t, s) {
498
+ function u(e, t, s) {
489
499
  if (typeof window > "u")
490
500
  return;
491
501
  const n = window, r = {
@@ -521,7 +531,7 @@ async function M(e) {
521
531
  result: await Promise.race([
522
532
  new t(e.javascript)(),
523
533
  new Promise(
524
- (c, u) => window.setTimeout(() => u(new Error(`execute_code timed out after ${s}ms`)), s)
534
+ (c, l) => window.setTimeout(() => l(new Error(`execute_code timed out after ${s}ms`)), s)
525
535
  )
526
536
  ]),
527
537
  console: r,
@@ -631,7 +641,7 @@ function at() {
631
641
  function ct() {
632
642
  return crypto.randomUUID();
633
643
  }
634
- function ut(e) {
644
+ function lt(e) {
635
645
  if (!e || typeof e != "object")
636
646
  return null;
637
647
  const t = e, s = typeof t.id == "string" ? t.id : null;
@@ -642,8 +652,8 @@ function ut(e) {
642
652
  avatarUrl: typeof t.avatarUrl == "string" ? t.avatarUrl : null
643
653
  } : null;
644
654
  }
645
- function lt(e) {
646
- return Array.isArray(e) ? _(
655
+ function ut(e) {
656
+ return Array.isArray(e) ? E(
647
657
  e.map(U).filter((t) => t !== null)
648
658
  ) : [];
649
659
  }
@@ -726,9 +736,9 @@ function ft(e) {
726
736
  function yt(e, t) {
727
737
  const s = mt(e, t), n = s.findIndex((i) => i.id === t.id);
728
738
  if (n === -1)
729
- return _([...s, t]);
739
+ return E([...s, t]);
730
740
  const r = [...s];
731
- return r[n] = t, _(r);
741
+ return r[n] = t, E(r);
732
742
  }
733
743
  function mt(e, t) {
734
744
  if (t.role !== "user")
@@ -775,7 +785,7 @@ function I(e) {
775
785
  const t = e;
776
786
  return t.type === "message" && t.role === "assistant" ? !0 : t.type === "run_error" ? t.stage !== "tool_execution" : t.type === "run_status" && (t.status === "stopped" || t.status === "interrupted");
777
787
  }
778
- function _(e) {
788
+ function E(e) {
779
789
  return e.filter((t, s) => St(t) ? !kt(e, s) : !0);
780
790
  }
781
791
  function St(e) {
@@ -794,21 +804,24 @@ function kt(e, t) {
794
804
  function P(e, t) {
795
805
  return e && typeof e == "object" && typeof e[t] == "string" ? e[t] : null;
796
806
  }
797
- function It(e) {
807
+ function It(e, t) {
798
808
  try {
799
- const t = window.localStorage.getItem(`${H}${e}`);
800
- if (!t)
809
+ const s = window.localStorage.getItem(`${H}${e}`);
810
+ if (!s)
811
+ return null;
812
+ const n = JSON.parse(s), r = typeof n.sessionId == "string" ? n.sessionId : null;
813
+ if (t !== void 0 && r !== t)
801
814
  return null;
802
- const s = JSON.parse(t), n = Array.isArray(s.messages) ? s.messages.map(At).filter((r) => !!r) : [];
815
+ const i = Array.isArray(n.messages) ? n.messages.map(At).filter((a) => !!a) : [];
803
816
  return {
804
- sessionId: typeof s.sessionId == "string" ? s.sessionId : null,
805
- messages: n
817
+ sessionId: r,
818
+ messages: i
806
819
  };
807
820
  } catch {
808
821
  return null;
809
822
  }
810
823
  }
811
- function _t(e, t) {
824
+ function Et(e, t) {
812
825
  try {
813
826
  window.localStorage.setItem(
814
827
  `${H}${e}`,
@@ -821,7 +834,7 @@ function _t(e, t) {
821
834
  return;
822
835
  }
823
836
  }
824
- function Et(e) {
837
+ function _t(e) {
825
838
  try {
826
839
  const t = window.localStorage.getItem(`${k}${e}`);
827
840
  if (!t)
@@ -953,7 +966,7 @@ function p(e) {
953
966
  function T(e) {
954
967
  return `${e}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
955
968
  }
956
- const E = "[REDACTED_SECRET]", h = "[REDACTED_TOKEN]", Pt = "[REDACTED_SIGNED_URL]", Dt = 20, Ot = /^(access_token|accessToken|refresh_token|refreshToken|id_token|idToken|authToken|token|api_key|apiKey|apikey|key|secret|signature|sig|password|passwd|pwd|code|state|session|jwt|csrf|csrfToken|xsrf|xsrfToken)$/i, j = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, G = /\bBasic\s+[A-Za-z0-9+/=-]{12,}/gi, v = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, $ = /\b(access_token|accessToken|refresh_token|refreshToken|id_token|idToken|authToken|api_key|apiKey|apikey|client_secret|clientSecret|password|secret|token|jwt|csrf|csrfToken|xsrf|xsrfToken)\b\s*[:=]\s*["']?[^"',&\s}]+/gi, Ht = /\bhttps?:\/\/[^\s"'<>]+(?:X-Amz-Signature|X-Goog-Signature|Signature|sig=)[^\s"'<>]*/gi, qt = /\bhttps?:\/\/[^\s"'<>]+/gi, Ut = /[),.;\]]+$/;
969
+ const _ = "[REDACTED_SECRET]", h = "[REDACTED_TOKEN]", Pt = "[REDACTED_SIGNED_URL]", Dt = 20, Ot = /^(access_token|accessToken|refresh_token|refreshToken|id_token|idToken|authToken|token|api_key|apiKey|apikey|key|secret|signature|sig|password|passwd|pwd|code|state|session|jwt|csrf|csrfToken|xsrf|xsrfToken)$/i, j = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, G = /\bBasic\s+[A-Za-z0-9+/=-]{12,}/gi, v = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, $ = /\b(access_token|accessToken|refresh_token|refreshToken|id_token|idToken|authToken|api_key|apiKey|apikey|client_secret|clientSecret|password|secret|token|jwt|csrf|csrfToken|xsrf|xsrfToken)\b\s*[:=]\s*["']?[^"',&\s}]+/gi, Ht = /\bhttps?:\/\/[^\s"'<>]+(?:X-Amz-Signature|X-Goog-Signature|Signature|sig=)[^\s"'<>]*/gi, qt = /\bhttps?:\/\/[^\s"'<>]+/gi, Ut = /[),.;\]]+$/;
957
970
  function f(e) {
958
971
  return S(e, 0, /* @__PURE__ */ new WeakSet());
959
972
  }
@@ -970,7 +983,7 @@ function S(e, t, s) {
970
983
  return e.map((r) => S(r, t + 1, s));
971
984
  const n = {};
972
985
  for (const [r, i] of Object.entries(e))
973
- Wt(r) ? n[r] = E : n[r] = r.toLowerCase() === "url" ? xt(i) : S(i, t + 1, s);
986
+ Wt(r) ? n[r] = _ : n[r] = r.toLowerCase() === "url" ? xt(i) : S(i, t + 1, s);
974
987
  return n;
975
988
  }
976
989
  function Wt(e) {
@@ -991,10 +1004,10 @@ function X(e) {
991
1004
  }
992
1005
  }
993
1006
  function Bt(e) {
994
- return e.replace(Ht, Pt).replace(qt, jt).replace(j, `Bearer ${h}`).replace(G, `Basic ${h}`).replace(v, h).replace($, (t, s) => `${s}: ${E}`);
1007
+ return e.replace(Ht, Pt).replace(qt, jt).replace(j, `Bearer ${h}`).replace(G, `Basic ${h}`).replace(v, h).replace($, (t, s) => `${s}: ${_}`);
995
1008
  }
996
1009
  function Lt(e) {
997
- return e.replace(j, `Bearer ${h}`).replace(G, `Basic ${h}`).replace(v, h).replace($, (t, s) => `${s}: ${E}`);
1010
+ return e.replace(j, `Bearer ${h}`).replace(G, `Basic ${h}`).replace(v, h).replace($, (t, s) => `${s}: ${_}`);
998
1011
  }
999
1012
  function jt(e) {
1000
1013
  const t = e.match(Ut)?.[0] ?? "", s = t ? e.slice(0, -t.length) : e;