@pluno/product-agent-web 0.1.66 → 0.1.68

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/INTEGRATION.md CHANGED
@@ -57,8 +57,10 @@ const agent = await PlunoProductAgent.init({
57
57
  });
58
58
  ```
59
59
 
60
- Network capture is always enabled. The SDK sends captured request/response metadata and bodies using the built-in
61
- redaction pipeline before data leaves the browser.
60
+ Network capture is always enabled. The SDK passively observes resource URL/timing metadata and adds richer fetch/XHR
61
+ metadata only when it can do so without delaying or consuming host traffic. Small declared-length textual bodies are
62
+ read in a detached, capped task; streaming, unknown-length, large, upload, token, Pluno, and internal bodies are skipped.
63
+ Captured values use the built-in limits and redaction pipeline before data leaves the browser.
62
64
 
63
65
  ## Default Widget
64
66
 
@@ -84,14 +86,50 @@ Supported widget script-tag attributes:
84
86
  - optional `data-pluno-accent-color`
85
87
  - optional `data-pluno-color-scheme`
86
88
  - optional `data-pluno-font-family`
89
+ - optional `data-pluno-scribble-style="false"` to disable the default scribble appearance
87
90
  - optional `data-pluno-position`
88
91
  - optional `data-pluno-auto-mount="false"`
89
92
 
93
+ ### Advanced programmatic widget
94
+
95
+ Keep the script-tag integration as the default. When the customer token endpoint requires bearer auth, CSRF, tenant
96
+ headers, or other custom request logic, import the same hosted ES module and pass a `tokenProvider`:
97
+
98
+ ```ts
99
+ const { mountPlunoProductAgentWidget } = await import(
100
+ "https://app.pluno.ai/product-agent/product-agent-widget.js"
101
+ );
102
+
103
+ const widget = await mountPlunoProductAgentWidget({
104
+ tokenProvider: async () => {
105
+ const response = await fetch("/api/pluno-product-agent-token", {
106
+ method: "POST",
107
+ credentials: "include",
108
+ headers: {
109
+ Authorization: `Bearer ${customerAccessToken}`,
110
+ "X-CSRF-Token": csrfToken,
111
+ "X-Tenant-ID": tenantId,
112
+ },
113
+ });
114
+ if (!response.ok) throw new Error("Could not create Pluno session token");
115
+ return (await response.json()).token;
116
+ },
117
+ });
118
+
119
+ widget.openPanel();
120
+ function removeProductAgentWidget() {
121
+ widget.destroy();
122
+ }
123
+ ```
124
+
125
+ A programmatic import does not auto-mount. Only a widget script tag with Pluno data attributes auto-mounts; callers of
126
+ the hosted module explicitly own the returned widget handle and should call `destroy()` when removing the integration.
127
+
90
128
  ## Security Decisions
91
129
 
92
130
  - Integration secrets are backend-only.
93
131
  - Embed tokens are short-lived Pluno-signed JWTs.
94
132
  - The runtime validates the token origin against the integration allowlist.
95
133
  - The runtime validates the browser WebSocket `Origin` header against the token origin.
96
- - Network capture is always installed by the SDK/widget. There is intentionally no configuration or opt-out for now.
134
+ - Network capture is always installed by the SDK/widget, shared across instances, and removed after final teardown. There is intentionally no configuration or opt-out for now.
97
135
  - Browser-side redaction runs before network events and tool results are sent to Pluno.
package/README.md CHANGED
@@ -33,7 +33,7 @@ agent.on("state", (state) => {
33
33
  agent.sendMessage("Update my settings");
34
34
  ```
35
35
 
36
- The SDK always captures product network activity by patching `fetch` and `XMLHttpRequest`. There is intentionally no opt-out for now.
36
+ The SDK captures passive resource URL/timing metadata with `PerformanceObserver` and adds bounded method, header, status, and small textual body metadata through shared `fetch`/`XMLHttpRequest` instrumentation. Capture is best effort and detached from host requests: streaming, unknown-length, large, upload, token, Pluno, and internal bodies are not read, captured values are capped and redacted, and the instrumentation is removed after the final SDK instance is destroyed.
37
37
 
38
38
  ## Drop-In Widget
39
39
 
@@ -45,9 +45,45 @@ The SDK always captures product network activity by patching `fetch` and `XMLHtt
45
45
  data-pluno-launcher-label="Ask for anything..."
46
46
  data-pluno-accent-color="#7c3aed"
47
47
  data-pluno-font-family='Inter, system-ui, sans-serif'
48
+ data-pluno-scribble-style="false"
48
49
  ></script>
49
50
  ```
50
51
 
52
+ Scribble style is enabled by default. Set `data-pluno-scribble-style="false"` in the snippet, or `scribbleStyle: false`
53
+ for programmatic mounts, to use the standard widget appearance.
54
+
55
+ This script-tag setup is the recommended integration. If your token endpoint needs custom browser-side authentication,
56
+ import the same hosted widget module and provide the request logic programmatically:
57
+
58
+ ```ts
59
+ const { mountPlunoProductAgentWidget } = await import(
60
+ "https://app.pluno.ai/product-agent/product-agent-widget.js"
61
+ );
62
+
63
+ const widget = await mountPlunoProductAgentWidget({
64
+ tokenProvider: async () => {
65
+ const response = await fetch("/api/pluno-product-agent-token", {
66
+ method: "POST",
67
+ credentials: "include",
68
+ headers: {
69
+ Authorization: `Bearer ${customerAccessToken}`,
70
+ "X-CSRF-Token": csrfToken,
71
+ "X-Tenant-ID": tenantId,
72
+ },
73
+ });
74
+ if (!response.ok) throw new Error("Could not create Pluno session token");
75
+ return (await response.json()).token;
76
+ },
77
+ });
78
+
79
+ function removeProductAgentWidget() {
80
+ widget.destroy();
81
+ }
82
+ ```
83
+
84
+ Importing the hosted module does not mount anything by itself. `mountPlunoProductAgentWidget` returns a handle containing
85
+ the underlying `agent`, `openPanel()`, appearance/auth update methods, and `destroy()`.
86
+
51
87
  The token endpoint must be implemented by the customer backend. It should authenticate the current user, then call Pluno&apos;s `/api/product-agent/embed/token` endpoint with:
52
88
 
53
89
  - `public_key`
package/dist/index.d.ts CHANGED
@@ -130,19 +130,45 @@ export type ProductAgentEventMap = {
130
130
  };
131
131
  type EventName = keyof ProductAgentEventMap;
132
132
  type Listener<T extends EventName> = (payload: ProductAgentEventMap[T]) => void;
133
+ type CapturedRequest = {
134
+ requestId: string;
135
+ url: string;
136
+ method: string;
137
+ requestHeaders?: Record<string, string>;
138
+ requestBody?: string;
139
+ resourceType: "fetch" | "xhr" | "resource";
140
+ responseStatus?: number;
141
+ responseHeaders?: Record<string, string>;
142
+ responseBody?: unknown;
143
+ errorText?: string;
144
+ startedAt: string;
145
+ durationMs?: number;
146
+ };
147
+ type NetworkCaptureSubscriber = (event: CapturedRequest) => void;
148
+ type NetworkCaptureManager = {
149
+ subscribers: Set<NetworkCaptureSubscriber>;
150
+ destroy: () => void;
151
+ };
152
+ export declare function calculateReconnectDelay(attempt: number, random?: () => number): number;
133
153
  export declare class PlunoProductAgent {
134
154
  private readonly options;
135
155
  private readonly listeners;
136
156
  private socket;
137
157
  private reconnectTimer;
138
158
  private heartbeatTimer;
159
+ private heartbeatAckTimer;
160
+ private socketConnectTimer;
161
+ private socketAuthTimer;
162
+ private reconnectAttempts;
163
+ private connectionAttemptId;
164
+ private connectionInProgress;
139
165
  private token;
140
166
  private networkCaptureCleanup;
141
167
  private networkBatchTimer;
142
168
  private queuedNetworkEvents;
143
169
  private queuedClientEvents;
144
170
  private pendingSessionHistoryRequests;
145
- private readonly transportId;
171
+ private readonly transportIdentity;
146
172
  private retryAttemptsByClientMessageId;
147
173
  private retryTimersByClientMessageId;
148
174
  private thinkingWatchdogTimer;
@@ -158,6 +184,8 @@ export declare class PlunoProductAgent {
158
184
  private readonly attachmentFiles;
159
185
  private readonly attachmentBlobCache;
160
186
  private sessionUserFilesApiCleanup;
187
+ private readonly activeBrowserToolCalls;
188
+ private pageLifecycleCleanup;
161
189
  private transientStarterPromptsRequestInFlight;
162
190
  private transientStarterPromptsRequestAttempted;
163
191
  private locationChangeCleanup;
@@ -214,6 +242,7 @@ export declare class PlunoProductAgent {
214
242
  private recoverStuckThinking;
215
243
  private resyncThinkingSession;
216
244
  private executeToolCall;
245
+ private startPageLifecycleRecovery;
217
246
  private installSessionUserFilesApi;
218
247
  private cleanupSessionUserFilesApi;
219
248
  private executeRuntimeHelper;
@@ -222,12 +251,17 @@ export declare class PlunoProductAgent {
222
251
  private scheduleReconnect;
223
252
  private startHeartbeat;
224
253
  private stopHeartbeat;
254
+ private clearHeartbeatAckTimer;
255
+ private clearSocketConnectTimer;
256
+ private clearSocketAuthTimer;
257
+ private clearSocketPhaseTimers;
258
+ private replaceTimedOutSocket;
225
259
  private setState;
226
260
  private emit;
227
261
  }
228
262
  declare global {
229
263
  interface Window {
230
- __plunoProductAgentInstance?: PlunoProductAgent;
264
+ __plunoProductAgentNetworkCapture?: NetworkCaptureManager;
231
265
  }
232
266
  }
233
267
  export declare function getProductAgentOriginAccessErrorMessage(message: string, runtimeOrigin?: string): string | null;