@pluno/product-agent-web 0.1.191 → 0.1.196

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.
@@ -2,3 +2,18 @@ export declare function isPassiveComposerFocusClick(target: EventTarget | null,
2
2
  export declare function shouldFocusComposerFromSurfaceInteraction(clickCount: number, hasTextSelection: boolean, isInteractiveTarget: boolean): boolean;
3
3
  export declare function isOutsideOverlayClick(target: EventTarget | null, overlay: Element | null, trigger: Element | null): boolean;
4
4
  export declare function focusComposer(composer: HTMLTextAreaElement | null, isAvailable?: boolean): boolean;
5
+ type ComposerKeystrokeEvent = {
6
+ key: string;
7
+ defaultPrevented: boolean;
8
+ isComposing: boolean;
9
+ metaKey: boolean;
10
+ ctrlKey: boolean;
11
+ altKey: boolean;
12
+ getModifierState?: (keyArg: string) => boolean;
13
+ };
14
+ export declare function getComposerKeystroke(event: ComposerKeystrokeEvent, activeTarget: EventTarget | null): string | null;
15
+ export declare function insertComposerKeystroke(value: string, selectionStart: number | null, selectionEnd: number | null, keystroke: string): {
16
+ value: string;
17
+ caret: number;
18
+ };
19
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ProductAgentTaskStatus } from "./taskPageTitle";
2
+ import type { ProductAgentEntityQueryData, ProductAgentAccountProjection, ProductAgentInteraction, ProductAgentInteractionAction, ProductAgentQueryState } from "./runtimeState";
2
3
  export type ProductAgentModel = "anthropic/claude-opus-5" | "anthropic/claude-sonnet-5" | "deepseek/deepseek-chat-v3.1" | "deepseek/deepseek-v4-flash" | "gpt-5.4-nano" | "gpt-5.4-mini" | "gpt-5.4" | "gpt-5.6-luna" | "gpt-5.6-terra" | "gpt-5.6-sol";
3
4
  export type ProductAgentEntrySurface = "extension_sidepanel" | "extension_widget" | "main_web_chat" | "whatsapp" | "automation" | "mcp" | "embedded_widget" | "embedded_custom_ui";
4
5
  export type ProductAgentProductVariant = "personal" | "customer_embedded";
@@ -32,13 +33,13 @@ export declare const PLUNO_PRODUCT_AGENT_WIDGET_HOST_SELECTOR = ".pluno-pa-widge
32
33
  export declare const PLUNO_PRODUCT_AGENT_WIDGET_ROOT_SELECTOR = ".pluno-pa-widget[data-pluno-product-agent-ui-root=\"widget\"]";
33
34
  export declare const PLUNO_PRODUCT_AGENT_WIDGET_PANEL_SELECTOR = ".pluno-pa-widget__panel";
34
35
  export declare const PLUNO_PRODUCT_AGENT_WIDGET_TIMELINE_SELECTOR = ".pluno-pa-widget__timeline";
35
- export declare const PLUNO_PRODUCT_AGENT_SDK_PREVIEW_HOST_SELECTOR = ".pluno-pa-widget-host[data-pluno-product-agent-ui=\"widget\"][data-pluno-sdk-preview-channel]";
36
36
  export type ProductAgentMessage = {
37
37
  id: string;
38
38
  role: "user" | "assistant" | "tool" | "system";
39
39
  phase?: "commentary" | "final_answer";
40
40
  content: string;
41
41
  createdAt: string;
42
+ displaySequence?: number;
42
43
  causalSequence?: number;
43
44
  respondsToUserMessageId?: string;
44
45
  runId?: string;
@@ -142,6 +143,7 @@ export type ProductAgentState = {
142
143
  assistantDraftPhase: "commentary" | "final_answer" | null;
143
144
  assistantDraftRespondsToUserMessageId: string | null;
144
145
  assistantDraftRunId: string | null;
146
+ activeResponseUserMessageId?: string | null;
145
147
  pendingMessageStatus: "sending" | "reconnecting" | null;
146
148
  turnPhase: ProductAgentTurnPhase | null;
147
149
  isThinking: boolean;
@@ -171,6 +173,21 @@ export type ProductAgentInitOptions = {
171
173
  automationRunId?: string;
172
174
  keepRunsActiveOnSessionNavigation?: boolean;
173
175
  capturePageContent?: boolean;
176
+ accountLoader?: () => Promise<ProductAgentAccountProjection | null>;
177
+ initialAccount?: ProductAgentAccountProjection | null;
178
+ runtimeAdapters?: ProductAgentRuntimeAdapters;
179
+ };
180
+ export type ProductAgentRuntimeAdapters = {
181
+ integrationAuthStatusLoader?: (request: ProductAgentIntegrationAuthRequest) => Promise<"pending" | "completed" | "cancelled" | "expired">;
182
+ integrationAuthHandler?: (request: ProductAgentIntegrationAuthRequest) => Promise<void>;
183
+ personalChannelConnectionHandler?: (request: ProductAgentPersonalChannelConnectionRequest) => Promise<void>;
184
+ tabGroups?: {
185
+ getState: () => Promise<{
186
+ permissionNeeded: boolean;
187
+ }>;
188
+ enable: () => Promise<void>;
189
+ dismiss: () => Promise<void>;
190
+ };
174
191
  };
175
192
  export type ProductAgentWebSocket = {
176
193
  readyState: number;
@@ -223,7 +240,7 @@ type NetworkCaptureManager = {
223
240
  };
224
241
  export declare const PRODUCT_AGENT_PROVIDER_INPUT_ATTACHMENT_ACCEPT = ".pdf,.png,.jpg,.jpeg,.webp";
225
242
  export declare function calculateReconnectDelay(attempt: number, random?: () => number): number;
226
- export declare class PlunoProductAgent {
243
+ export declare class ProductAgentSessionEngine {
227
244
  private readonly options;
228
245
  private readonly listeners;
229
246
  private socket;
@@ -249,6 +266,18 @@ export declare class PlunoProductAgent {
249
266
  private activeComposerWarmupScope;
250
267
  private pendingWarmupAckTimer;
251
268
  private pendingSessionHistoryRequests;
269
+ private readonly sessionHistoryManager;
270
+ private readonly sessionRecoveryPoller;
271
+ private account;
272
+ private usingPaidCreditFallback;
273
+ private readonly accountListeners;
274
+ private accountRefresh;
275
+ private runtimeInteractions;
276
+ private readonly interactionListeners;
277
+ private readonly interactionManager;
278
+ private integrationAuthStatusByRequestId;
279
+ private integrationAuthErrorByRequestId;
280
+ private tabGroupsPermissionNeeded;
252
281
  private pendingSessionPinRequests;
253
282
  private pendingSessionRenameRequests;
254
283
  private sessionLoadRequestId;
@@ -259,7 +288,6 @@ export declare class PlunoProductAgent {
259
288
  private readonly transportIdentity;
260
289
  private retryAttemptsByClientMessageId;
261
290
  private retryTimersByClientMessageId;
262
- private recoveryExhaustionRequestedClientMessageIds;
263
291
  private readonly reportedHealthSignalKeys;
264
292
  private readonly clientMessageIdsByUserMessageItemId;
265
293
  private readonly backgroundSessionIds;
@@ -303,7 +331,7 @@ export declare class PlunoProductAgent {
303
331
  private lastStarterPromptPageUrl;
304
332
  private state;
305
333
  private constructor();
306
- static init(options: ProductAgentInitOptions): Promise<PlunoProductAgent>;
334
+ static init(options: ProductAgentInitOptions): Promise<ProductAgentSessionEngine>;
307
335
  on<T extends EventName>(eventName: T, listener: Listener<T>): () => void;
308
336
  getState(): ProductAgentState;
309
337
  stageProactiveSuggestionQuestion(question: string): void;
@@ -337,14 +365,30 @@ export declare class PlunoProductAgent {
337
365
  limit?: number;
338
366
  pinned?: boolean;
339
367
  }): Promise<ProductAgentSessionHistoryPage>;
368
+ getSessionHistoryState(): ProductAgentQueryState<ProductAgentEntityQueryData<ProductAgentSessionHistoryEntry>>;
369
+ subscribeSessionHistory(listener: (state: ProductAgentQueryState<ProductAgentEntityQueryData<ProductAgentSessionHistoryEntry>>) => void): () => void;
370
+ refreshSessionHistory(): Promise<void>;
371
+ loadMoreSessionHistory(): Promise<void>;
340
372
  setSessionPinned(sessionId: string, pinned: boolean): Promise<void>;
341
373
  renameSession(sessionId: string, title: string): Promise<void>;
374
+ getInteractions(): readonly ProductAgentInteraction[];
375
+ subscribeInteractions(listener: (interactions: readonly ProductAgentInteraction[]) => void): () => void;
376
+ actOnInteraction(interactionId: string, action: ProductAgentInteractionAction, expectedRevision: number, input?: {
377
+ feedback?: string;
378
+ snoozeUntil?: string;
379
+ }): Promise<void>;
380
+ private refreshRuntimeInteractions;
381
+ private rebuildRuntimeInteractions;
382
+ getAccount(): ProductAgentAccountProjection | null;
383
+ subscribeAccount(listener: (account: ProductAgentAccountProjection | null) => void): () => void;
384
+ refreshAccount(): Promise<void>;
342
385
  loadSession(sessionId: string): void;
343
386
  private canLoadSessionHistoryOverHttp;
344
387
  private loadSessionHistoryOverHttp;
345
388
  private subscribeToSessionBeforeHistory;
346
389
  private autoLoadSessionActivity;
347
390
  private fetchSessionHistoryPageUntilCurrent;
391
+ private fetchSessionHistoryPageOnce;
348
392
  private failSessionHistoryRequest;
349
393
  private send;
350
394
  private getUploadToken;
@@ -363,18 +407,19 @@ export declare class PlunoProductAgent {
363
407
  private findStoppedTurn;
364
408
  private findStoppedTurnForEvent;
365
409
  private rememberDurablyStoppedTurn;
410
+ private forgetSupersededTaskTabClosedTurn;
366
411
  private shouldIgnoreStoppedTurnEvent;
367
412
  private filterStoppedSnapshotItems;
368
413
  private handleServerEvent;
414
+ private applyAccountSubmissionError;
369
415
  private updatePersonalModelFromSession;
416
+ private updateAccountFallbackFromSession;
370
417
  private shouldIgnoreBackgroundSessionEvent;
371
418
  private shouldIgnoreDetachedSessionEvent;
372
419
  private shouldDeferDetachedSessionEvent;
373
420
  private rememberCurrentRunAsDetached;
374
421
  private rememberCurrentRunAsBackground;
375
- private retryAfterRetryableError;
376
- private retryAfterInterruptedRun;
377
- private requestSessionRecoveryExhaustion;
422
+ private handleRetryableRecoveryError;
378
423
  private clearRetryTimers;
379
424
  private rememberUserMessageClientMessageIds;
380
425
  private rejectPendingSessionHistoryRequests;
@@ -388,6 +433,7 @@ export declare class PlunoProductAgent {
388
433
  private clearThinkingWatchdog;
389
434
  private handleRunAck;
390
435
  private promotePendingDelivery;
436
+ private shouldDeferPendingDeliveryPromotion;
391
437
  private schedulePendingDeliveryAck;
392
438
  private retryPendingDelivery;
393
439
  private failPendingDelivery;
@@ -395,7 +441,6 @@ export declare class PlunoProductAgent {
395
441
  private handleRunSteered;
396
442
  private scheduleRunAckWatchdog;
397
443
  private recoverMissedRunAck;
398
- private scheduleRunAckResyncRetry;
399
444
  private clearRunAckResyncRetryTimer;
400
445
  private clearRunAckTimers;
401
446
  private recoverStuckThinking;
@@ -420,6 +465,8 @@ export declare class PlunoProductAgent {
420
465
  private replaceTimedOutSocket;
421
466
  private setState;
422
467
  private setReconnectingState;
468
+ private startSessionRecoveryPolling;
469
+ private applySessionRecoverySnapshot;
423
470
  private emit;
424
471
  }
425
472
  declare global {
@@ -428,5 +475,20 @@ declare global {
428
475
  }
429
476
  }
430
477
  export declare function getProductAgentOriginAccessErrorMessage(message: string, runtimeOrigin?: string): string | null;
478
+ export declare function normalizeProductAgentSessionItems(value: unknown, backendUrl?: string): ProductAgentMessage[];
431
479
  export declare function validateProductAgentProviderInputFile(file: File): void;
480
+ export { createProductAgentQueryState, mergeProductAgentEntityPages, normalizeProductAgentEntityPage, ProductAgentQueryController, resolveProductAgentComposerAction, resolveProductAgentWidgetPresentation, selectProductAgentQueryEntities, } from "./runtimeState";
481
+ export { ProductAgentSessionHistoryManager } from "./sessionHistoryState";
482
+ export type { ProductAgentSessionHistoryEntity, ProductAgentSessionHistoryLoader, } from "./sessionHistoryState";
483
+ export { createLocalStorageInteractionDecisionStorage, ProductAgentInteractionManager, readLocalStorageInteractionDecisions, } from "./interactionManager";
484
+ export type { ProductAgentInteractionDecisionStorage, ProductAgentInteractionDefinition, } from "./interactionManager";
485
+ export type { ProductAgentAuthProjection, ProductAgentComposerAction, ProductAgentEntityQueryData, ProductAgentInteraction, ProductAgentInteractionAction, ProductAgentInteractionPresentation, ProductAgentInteractionScope, ProductAgentInteractionStatus, ProductAgentPromptAction, ProductAgentPromptDecision, ProductAgentPromptDecisionScope, ProductAgentQueryState, ProductAgentQueryStatus, ProductAgentSafeUser, ProductAgentSafeWorkspace, ProductAgentSubmissionGate, ProductAgentWidgetPresentation, } from "./runtimeState";
486
+ export { createProductAgentTaskPageTitleDocument, formatProductAgentTaskPageTitle, ProductAgentTaskPageTitleController, } from "./taskPageTitle";
487
+ export { isProductAgentRuntimeCommand, ProductAgentRemoteRuntimeClient } from "./runtimeClient";
488
+ export type { ProductAgentRuntimeClient, ProductAgentRuntimeClientAdapter, ProductAgentRuntimeCommand, ProductAgentRuntimeCommandResult, } from "./runtimeClient";
489
+ export type PlunoProductAgent = ProductAgentSessionEngine;
490
+ export declare const PlunoProductAgent: Readonly<{
491
+ init: (options: ProductAgentInitOptions) => Promise<ProductAgentSessionEngine>;
492
+ }>;
493
+ export declare function createProductAgentRuntime(options: ProductAgentInitOptions): Promise<ProductAgentSessionEngine>;
432
494
  export default PlunoProductAgent;
@@ -0,0 +1,38 @@
1
+ import type { ProductAgentInteractionAction, ProductAgentInteractionScope, ProductAgentPromptDecision } from "./runtimeState";
2
+ export type ProductAgentInteractionDefinition = {
3
+ key: string;
4
+ category: string;
5
+ scope: ProductAgentInteractionScope;
6
+ allowedActions: readonly ProductAgentInteractionAction[];
7
+ laterDelayMs?: number;
8
+ };
9
+ export type ProductAgentInteractionDecisionStorage = {
10
+ load: () => Promise<ProductAgentPromptDecision[]>;
11
+ save: (decisions: ProductAgentPromptDecision[]) => Promise<void>;
12
+ };
13
+ export declare class ProductAgentInteractionManager {
14
+ private readonly storage;
15
+ private decisions;
16
+ private scheduledEligibility;
17
+ private initialized;
18
+ private actionQueue;
19
+ constructor(storage: ProductAgentInteractionDecisionStorage, initialDecisions?: readonly ProductAgentPromptDecision[] | null);
20
+ initialize(): Promise<void>;
21
+ isEligible(interaction: ProductAgentInteractionDefinition, now?: number): boolean;
22
+ getDecision(interaction: ProductAgentInteractionDefinition): ProductAgentPromptDecision | null;
23
+ getStatus(interaction: ProductAgentInteractionDefinition, now?: number): "presentable" | "scheduled" | "snoozed" | "resolved" | "suppressed";
24
+ scheduleEligibility(interaction: ProductAgentInteractionDefinition, eligibleAt: number): number;
25
+ getNextEligibilityAt(now?: number): number | null;
26
+ act(interaction: ProductAgentInteractionDefinition, action: ProductAgentInteractionAction, options?: {
27
+ snoozeUntil?: Date;
28
+ now?: Date;
29
+ }): Promise<ProductAgentPromptDecision>;
30
+ private applyAction;
31
+ private isDecisionEligible;
32
+ private getNextEligibleAt;
33
+ private getDecisionKey;
34
+ private getExactKey;
35
+ private getCategoryKey;
36
+ }
37
+ export declare function createLocalStorageInteractionDecisionStorage(storage: Storage, storageKey: string): ProductAgentInteractionDecisionStorage;
38
+ export declare function readLocalStorageInteractionDecisions(storage: Storage, storageKey: string): ProductAgentPromptDecision[];
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function d(s){return{key:s,status:"not_requested",data:null,error:null,requestId:0,updatedAt:null}}class m{constructor(t,e,i=(n,r)=>r){this.loader=e,this.merge=i,this.state=d(t)}loader;merge;state;listeners=new Set;inFlight=null;trailingLoad=null;getState(){return this.state}subscribe(t){return this.listeners.add(t),t(this.state),()=>this.listeners.delete(t)}setKey(t){t!==this.state.key&&(this.state=d(t),this.trailingLoad=null,this.publish())}reset(){this.state=d(this.state.key),this.trailingLoad=null,this.publish()}invalidate(){this.state={...this.state,status:this.state.data===null?"not_requested":"ready",updatedAt:null},this.publish()}load(t="replace",e=null){return this.trailingLoad={mode:t,cursor:e},this.inFlight?this.inFlight:(this.inFlight=this.runLoads().finally(()=>{this.inFlight=null}),this.inFlight)}update(t){this.state={...this.state,data:t(this.state.data)},this.publish()}async runLoads(){for(;this.trailingLoad;){const t=this.trailingLoad;this.trailingLoad=null;const e=this.state.key,i=this.state.requestId+1;this.state={...this.state,status:t.mode==="append"?"loading_more":this.state.data===null?"loading":"refreshing",error:null,requestId:i},this.publish();try{const n=await this.loader({key:e,cursor:t.cursor});if(e!==this.state.key||i!==this.state.requestId)continue;this.state={...this.state,status:"ready",data:this.merge(this.state.data,n,t.mode),error:null,updatedAt:Date.now()},this.publish()}catch(n){if(e!==this.state.key||i!==this.state.requestId)continue;this.state={...this.state,status:"error",error:n instanceof Error?n.message:"The query failed."},this.publish()}}}publish(){for(const t of this.listeners)t(this.state)}}function h(s){const t={},e=[];for(const i of s.entities)t[i.id]||e.push(i.id),t[i.id]=i;return{entitiesById:t,ids:e,nextCursor:s.nextCursor}}function P(s,t,e){if(e==="replace"||s===null)return t;const i={...s.entitiesById,...t.entitiesById},n=[...s.ids];for(const r of t.ids)s.entitiesById[r]||n.push(r);return{entitiesById:i,ids:n,nextCursor:t.nextCursor}}function S(s){return s?s.ids.map(t=>s.entitiesById[t]).filter(t=>!!t):[]}function E(s){return s.visible?s.minimized?{state:"minimized"}:{state:s.open?"visible_open":"visible_closed"}:{state:"hidden"}}function M(s,t){return t==="limit_exceeded"?{allowed:!1,reason:"credits_exhausted",actions:["upgrade","earn_credits"]}:t==="subscription_required"||t==="subscription_inactive"||t==="payment_issue"||t==="subscription_invalid"||t==="payment_required"?{allowed:!1,reason:"payment_issue",actions:["upgrade"]}:s??{allowed:!0,reason:"allowed",actions:[]}}function O(s){if(!s||typeof s!="object")return!1;const t=s.paidCreditFallbackModel;return typeof t=="string"&&t.length>0}function _(s){return s?s.submissionGate?.allowed===!1?{state:"action_required",reason:s.submissionGate.reason}:s.usingPaidCreditFallback===!0?{state:"paid_credit_fallback"}:s.creditLimit!==null&&s.creditLimit>0&&s.creditsRemaining!==null&&s.creditsRemaining>0&&s.creditsRemaining/s.creditLimit<=.2?{state:"low_credits"}:s.creditsUsed!==null?{state:"usage"}:{state:"hidden"}:{state:"hidden"}}function D(s){const t=s.hasDraftMessage||s.hasPreSubmittedAttachment;return s.hasRunningAssistantTurn&&!t?{action:"stop",disabled:!s.canStopRunningTurn}:{action:"send",disabled:!t||!s.canSubmitMessage}}class x{constructor(t,e,i=20){this.loadPage=e,this.limit=i,this.recentQuery=new m(`${t}:recent`,async({cursor:n})=>h(await this.loadPage({cursor:n,limit:this.limit,pinned:!1}).then(r=>({entities:r.sessions,nextCursor:r.nextCursor}))),P),this.pinnedQuery=new m(`${t}:pinned`,async()=>h({entities:await this.loadAllPinned(),nextCursor:null})),this.recentQuery.subscribe(()=>this.publish()),this.pinnedQuery.subscribe(()=>this.publish())}loadPage;limit;recentQuery;pinnedQuery;listeners=new Set;optimisticEntities=new Map;activityOverrides=new Map;pinnedOverrides=new Map;titleOverrides=new Map;refreshSequence=0;completedRefreshSequence=0;activityReconciliationScheduled=!1;getState(){const t=this.recentQuery.getState(),e=this.pinnedQuery.getState(),i=[...g(e.data),...g(t.data)],n=new Map(i.map(o=>[o.id,o])),r=Array.from(n.values()).map(o=>this.applyOverrides(o,this.completedRefreshSequence)),a=Array.from(this.optimisticEntities.values()).filter(o=>!n.has(o.id)).reverse(),l=t.data||e.data||a.length>0?h({entities:[...a,...r],nextCursor:t.data?.nextCursor??null}):null;return{key:t.key.slice(0,-7),status:C(t,e),data:l,error:t.error??e.error,requestId:Math.max(t.requestId,e.requestId),updatedAt:Math.max(t.updatedAt??0,e.updatedAt??0)||null}}subscribe(t){return this.listeners.add(t),t(this.getState()),()=>this.listeners.delete(t)}getEntity(t){return this.getState().data?.entitiesById[t]??null}setKey(t){this.optimisticEntities.clear(),this.activityOverrides.clear(),this.pinnedOverrides.clear(),this.titleOverrides.clear(),this.refreshSequence=0,this.completedRefreshSequence=0,this.activityReconciliationScheduled=!1,this.recentQuery.setKey(`${t}:recent`),this.pinnedQuery.setKey(`${t}:pinned`)}refresh(){const t=++this.refreshSequence,e=Promise.all([this.recentQuery.load("replace"),this.pinnedQuery.load("replace")]).then(()=>{this.completedRefreshSequence=Math.max(this.completedRefreshSequence,t),this.reconcileDurableEntities()});return e.then(()=>{!this.hasUnconfirmedActivityOverride()||this.activityReconciliationScheduled||(this.activityReconciliationScheduled=!0,this.refresh().finally(()=>{this.activityReconciliationScheduled=!1}))}),e}loadMore(t){return this.recentQuery.load("append",t)}addOptimistic(t){this.optimisticEntities.set(t.id,t),this.updateProjection()}removeOptimistic(t){this.optimisticEntities.delete(t)&&this.updateProjection()}acknowledgeOptimistic(t,e){this.optimisticEntities.delete(t),this.optimisticEntities.set(e.id,e),this.updateProjection()}setActivity(t,e){this.activityOverrides.set(t,{isActive:e,confirmAfterRefresh:this.completedRefreshSequence+1,releaseAfterRefresh:this.completedRefreshSequence+2}),this.updateEntity(t,i=>({...i,isActive:e}))}setPinned(t,e){const i={pinned:e,confirmAfterRefresh:Number.POSITIVE_INFINITY};return this.pinnedOverrides.set(t,i),this.updateEntity(t,n=>({...n,isPinned:e})),i}confirmPinned(t,e){this.pinnedOverrides.get(t)===e&&(e.confirmAfterRefresh=this.completedRefreshSequence+1)}rollbackPinned(t,e,i){this.pinnedOverrides.get(t)===e&&(this.pinnedOverrides.delete(t),this.updateEntity(t,n=>({...n,isPinned:i})))}setTitle(t,e){this.titleOverrides.set(t,e),this.updateEntity(t,i=>({...i,customTitle:e}))}confirmTitle(t){this.titleOverrides.delete(t)}reconcileDurableEntities(){const t=[...g(this.pinnedQuery.getState().data),...g(this.recentQuery.getState().data)],e=new Set(t.map(i=>i.id));for(const i of this.optimisticEntities.keys())e.has(i)&&this.optimisticEntities.delete(i);for(const i of t)this.applyOverrides(i,this.completedRefreshSequence);this.publish()}async loadAllPinned(){const t=[];let e=null;do{const i=await this.loadPage({cursor:e,limit:this.limit,pinned:!0});t.push(...i.sessions),e=i.nextCursor}while(e);return t}applyOverrides(t,e){let i=t;const n=this.activityOverrides.get(t.id);n&&(e>=n.confirmAfterRefresh&&t.isActive===n.isActive?this.activityOverrides.delete(t.id):e>=n.releaseAfterRefresh?this.activityOverrides.delete(t.id):i={...i,isActive:n.isActive});const r=this.pinnedOverrides.get(t.id);return r&&(e>=r.confirmAfterRefresh&&t.isPinned===!0===r.pinned?this.pinnedOverrides.delete(t.id):i={...i,isPinned:r.pinned}),this.titleOverrides.has(t.id)&&(i={...i,customTitle:this.titleOverrides.get(t.id)??null}),i}updateProjection(){this.publish()}updateEntity(t,e){const i=this.optimisticEntities.get(t);i&&this.optimisticEntities.set(t,e(i)),this.publish()}hasUnconfirmedActivityOverride(){return Array.from(this.activityOverrides.values()).some(t=>this.completedRefreshSequence<t.releaseAfterRefresh)}publish(){for(const t of this.listeners)t(this.getState())}}function g(s){return s?s.ids.map(t=>s.entitiesById[t]).filter(t=>!!t):[]}function C(s,t){return s.status==="error"||t.status==="error"?"error":s.status==="loading"||t.status==="loading"?"loading":s.status==="refreshing"||t.status==="refreshing"?"refreshing":s.status==="loading_more"?"loading_more":s.status==="ready"||t.status==="ready"?"ready":"not_requested"}class R{constructor(t,e=null){this.storage=t,e&&(this.decisions=new Map(e.filter(v).map(i=>[this.getDecisionKey(i),i])),this.initialized=!0)}storage;decisions=new Map;scheduledEligibility=new Map;initialized=!1;actionQueue=Promise.resolve();async initialize(){if(this.initialized)return;const t=await this.storage.load();this.decisions=new Map(t.filter(v).map(e=>[this.getDecisionKey(e),e])),this.initialized=!0}isEligible(t,e=Date.now()){const i=this.scheduledEligibility.get(this.getExactKey(t));return i!==void 0&&i>e?!1:this.isDecisionEligible(this.decisions.get(this.getExactKey(t)),e)&&this.isDecisionEligible(this.decisions.get(this.getCategoryKey(t)),e)}getDecision(t){return this.decisions.get(this.getExactKey(t))??this.decisions.get(this.getCategoryKey(t))??null}getStatus(t,e=Date.now()){const i=this.scheduledEligibility.get(this.getExactKey(t));if(i!==void 0&&i>e)return"scheduled";const n=this.getDecision(t);return n?n.action==="never"||n.action==="dont_show_again"?"suppressed":n.action==="dismiss"?"resolved":n.nextEligibleAt&&Date.parse(n.nextEligibleAt)>e?"snoozed":"presentable":"presentable"}scheduleEligibility(t,e){const i=this.getExactKey(t),n=this.scheduledEligibility.get(i);return n!==void 0?n:(this.scheduledEligibility.set(i,e),e)}getNextEligibilityAt(t=Date.now()){for(const[i,n]of this.scheduledEligibility)n<=t&&this.scheduledEligibility.delete(i);const e=[...this.scheduledEligibility.values(),...Array.from(this.decisions.values()).map(i=>i.nextEligibleAt?Date.parse(i.nextEligibleAt):Number.NaN).filter(i=>Number.isFinite(i)&&i>t)];return e.length>0?Math.min(...e):null}act(t,e,i={}){const n=this.actionQueue.then(()=>this.applyAction(t,e,i));return this.actionQueue=n.then(()=>{},()=>{}),n}async applyAction(t,e,i){if(!t.allowedActions.includes(e))throw new Error(`Interaction ${t.key} does not allow ${e}.`);if(!I(e))throw new Error(`Interaction action ${e} is not a persistence decision.`);const n=i.now??new Date,r={promptKey:t.key,category:t.category,action:e,scope:t.scope,decidedAt:n.toISOString(),nextEligibleAt:this.getNextEligibleAt(t,e,n,i.snoozeUntil)},a=e==="dont_show_again"?this.getCategoryKey(t):this.getExactKey(t),l=new Map(this.decisions);return l.set(a,r),await this.storage.save([...l.values()]),this.decisions=l,r}isDecisionEligible(t,e){return t?t.action==="dismiss"||t.action==="never"||t.action==="dont_show_again"?!1:!t.nextEligibleAt||Date.parse(t.nextEligibleAt)<=e:!0}getNextEligibleAt(t,e,i,n){if(e==="later")return new Date(i.getTime()+(t.laterDelayMs??3600*1e3)).toISOString();if(e==="snooze"){if(!n||n.getTime()<=i.getTime())throw new Error("Snooze requires a future next-eligible time.");return n.toISOString()}return null}getDecisionKey(t){const e=f(t.scope);return t.action==="dont_show_again"?`${e}:category:${t.category}`:`${e}:interaction:${t.promptKey}`}getExactKey(t){return`${f(t.scope)}:interaction:${t.key}`}getCategoryKey(t){return`${f(t.scope)}:category:${t.category}`}}function L(s,t){return{load:async()=>w(s,t),save:async e=>s.setItem(t,JSON.stringify(e))}}function w(s,t){const e=s.getItem(t);if(!e)return[];const i=JSON.parse(e);return Array.isArray(i)?i.filter(v):[]}function f(s){return`${s.level}:${s.key}`}function I(s){return s==="dismiss"||s==="later"||s==="snooze"||s==="never"||s==="dont_show_again"}function v(s){if(!s||typeof s!="object")return!1;const t=s;return typeof t.promptKey=="string"&&typeof t.category=="string"&&typeof t.decidedAt=="string"&&(t.nextEligibleAt===null||typeof t.nextEligibleAt=="string")&&I(t.action)&&!!(t.scope&&typeof t.scope=="object"&&typeof t.scope.level=="string"&&typeof t.scope.key=="string")}const q={working:"⏳",completed:"✅",failed:"❗",stopped:"⏹️"},k=/^(?:⌛|⏳|✅|❗|⏹️) Pluno(?:: )?/;function T(s,t){if(!t)return p(s);const e=p(s),i=`${q[t]} Pluno`;return e?`${i}: ${e}`:i}class Q{constructor(t){this.titleDocument=t}titleDocument;status=null;baseTitle="";lastAppliedTitle=null;stopObserving=null;liveStatusVersion=0;setStatus(t){this.liveStatusVersion+=1,this.applyStatus(t)}clear(){this.liveStatusVersion+=1,this.applyClear()}async syncStatus(t){const e=this.liveStatusVersion,i=await t();i===void 0||e!==this.liveStatusVersion||(i?this.applyStatus(i):this.applyClear())}applyStatus(t){const e=this.titleDocument.getTitle();e!==this.lastAppliedTitle&&(this.baseTitle=p(e)),this.status=t,this.stopObserving||(this.stopObserving=this.titleDocument.observeTitle(()=>this.handleTitleChanged())),this.applyTitle()}applyClear(){const t=this.titleDocument.getTitle(),e=this.lastAppliedTitle!==null&&t===this.lastAppliedTitle,i=this.lastAppliedTitle===null?p(t):t;this.status=null,this.lastAppliedTitle=null,this.stopObserving?.(),this.stopObserving=null,e?this.titleDocument.setTitle(this.baseTitle):i!==t&&this.titleDocument.setTitle(i)}handleTitleChanged(){!this.status||this.titleDocument.getTitle()===this.lastAppliedTitle||this.applyTitle()}applyTitle(){if(!this.status)return;const t=T(this.baseTitle,this.status);this.lastAppliedTitle=t,this.titleDocument.getTitle()!==t&&this.titleDocument.setTitle(t)}}function $(s){return{getTitle:()=>s.title,setTitle:t=>{s.title=t},observeTitle:t=>{const e=new MutationObserver(t);return e.observe(s.head,{childList:!0,subtree:!0,characterData:!0}),()=>e.disconnect()}}}function p(s){return s.replace(k,"")}function K(s){if(!s||typeof s!="object"||!("type"in s))return!1;const t=s;switch(t.type){case"runtime.connect":case"session.new":case"run.stop":case"run.retry":return!0;case"runtime.warmup":return t.reason==="panel_open"||t.reason==="extension_install"||t.reason==="composer_input";case"runtime.widget_lifecycle":return(t.action==="opened"||t.action==="closed"||t.action==="minimized")&&typeof t.trigger=="string";case"session.load":return typeof t.sessionId=="string"&&t.sessionId.length>0;case"session.list":return(t.cursor===void 0||t.cursor===null||typeof t.cursor=="string")&&(t.limit===void 0||typeof t.limit=="number")&&(t.pinned===void 0||typeof t.pinned=="boolean");case"session.set_model":return typeof t.model=="string";case"session.pin":return typeof t.sessionId=="string"&&typeof t.pinned=="boolean";case"session.rename":return typeof t.sessionId=="string"&&typeof t.title=="string";case"interaction.act":return typeof t.interactionId=="string"&&typeof t.action=="string"&&typeof t.expectedRevision=="number";case"conversation.stage_proactive_suggestion":return typeof t.question=="string"&&typeof t.clientMessageId=="string";case"conversation.stage":return typeof t.content=="string"&&typeof t.clientMessageId=="string"&&(t.attachments===void 0||Array.isArray(t.attachments));case"conversation.fail_staged":return typeof t.clientMessageId=="string"&&typeof t.message=="string";case"conversation.send":return typeof t.content=="string"&&typeof t.clientMessageId=="string"&&(t.attachments===void 0||Array.isArray(t.attachments));default:return!1}}class F{constructor(t,e,i){this.adapter=e,this.state=y(t),this.model=i}adapter;listeners={};attachmentFiles=new Map;sessionHistoryListeners=new Set;interactionListeners=new Set;accountListeners=new Set;state;sessionHistoryState=d("unavailable");model;interactions=[];account=null;stagedProactiveSuggestionClientMessageId=null;operationQueue=Promise.resolve();destroyed=!1;on(t,e){const i=this.listeners[t]??new Set;return i.add(e),this.listeners[t]=i,()=>i.delete(e)}getState(){return y(this.state)}updateProjection(t,e,i,n,r){if(!this.destroyed){if(this.state=y(t),e!==void 0&&(this.model=e),i){this.sessionHistoryState=A(i);for(const a of this.sessionHistoryListeners)a(this.getSessionHistoryState())}if(n){this.interactions=n.map(a=>({...a}));for(const a of this.interactionListeners)a(this.getInteractions())}if(r!==void 0){this.account=r?{...r}:null;for(const a of this.accountListeners)a(this.getAccount())}this.emit("state",this.getState())}}async connect(){await this.dispatch({type:"runtime.connect"})}destroy(){if(!this.destroyed){this.destroyed=!0,this.attachmentFiles.clear(),this.sessionHistoryListeners.clear(),this.interactionListeners.clear(),this.accountListeners.clear();for(const t of Object.values(this.listeners))t?.clear();this.adapter.onDisconnect?.()}}warmup(t="panel_open"){return this.dispatch({type:"runtime.warmup",reason:t}).then(()=>{})}recordWidgetLifecycle(t,e){this.dispatch({type:"runtime.widget_lifecycle",action:t,trigger:e})}sendMessage(t,e={}){const i=t.trim(),n=e.attachments??[];if(!i&&n.length===0)return Promise.resolve(null);const r=e.clientMessageId??(e.proactiveSuggestionQuestion&&this.stagedProactiveSuggestionClientMessageId?this.stagedProactiveSuggestionClientMessageId:crypto.randomUUID()),a=this.state,l={id:`local-${r}`,role:"user",content:i,createdAt:new Date().toISOString(),...n.length>0?{attachments:n}:{},clientMessageId:r};return this.state={...this.state,messages:[...this.state.messages,l],pendingMessageStatus:"sending",turnPhase:this.state.isThinking?this.state.turnPhase:"sending",taskStatus:"working",isRetrying:!1,lastError:null},this.emit("state",this.getState()),this.enqueueOperation(()=>this.sendMessageNow(i,{...e,clientMessageId:r},a))}async sendMessageNow(t,e,i){const n=t,r=e.clientMessageId;let a=e.attachments??[],l=!1;try{if(await this.adapter.dispatch({type:"conversation.stage",content:n,clientMessageId:r,proactiveSuggestionQuestion:e.proactiveSuggestionQuestion,attachments:a.length>0?a:void 0}),l=!0,a.length>0){if(!this.adapter.uploadAttachment)throw new Error("The runtime does not support attachments.");const c=[];for(const u of a){if(u.sandboxPath||u.storageKey){c.push(u);continue}const b=u.id?this.attachmentFiles.get(u.id):void 0;if(!b)throw new Error(`Attachment bytes are unavailable for ${u.name}`);c.push(await this.adapter.uploadAttachment(b,u,{clientMessageId:r}))}a=c}const o=await this.adapter.dispatch({type:"conversation.send",content:n,clientMessageId:r,initiatedBy:e.initiatedBy,invocation:e.invocation,proactiveSuggestionQuestion:e.proactiveSuggestionQuestion,attachments:a.length>0?a:void 0});for(const c of e.attachments??[])c.id&&this.attachmentFiles.delete(c.id);return r===this.stagedProactiveSuggestionClientMessageId&&(this.stagedProactiveSuggestionClientMessageId=null),o?.clientMessageId??r}catch(o){throw this.state={...this.state,messages:this.state.messages.filter(c=>c.id!==`local-${r}`&&c.id!==`optimistic-user-message:${r}`),pendingMessageStatus:i.pendingMessageStatus,turnPhase:i.turnPhase,isThinking:i.isThinking,taskStatus:i.taskStatus},this.emit("state",this.getState()),l&&await this.adapter.dispatch({type:"conversation.fail_staged",clientMessageId:r,message:o instanceof Error?o.message:String(o)}),o}}getModel(){return this.model}setModel(t){return this.dispatch({type:"session.set_model",model:t}).then(()=>{})}retryLastMessage(){return this.dispatch({type:"run.retry"}),!0}createLocalAttachment(t){const e=crypto.randomUUID();return this.attachmentFiles.set(e,t),{id:e,name:t.name||"attachment",mimeType:t.type||"application/octet-stream",sizeBytes:t.size}}stop(){return this.dispatch({type:"run.stop"}).then(()=>{})}startNewSession(t={}){return t.notifyTransport===!1?Promise.resolve():this.dispatch({type:"session.new"}).then(()=>{})}async listSessions(t={}){await this.dispatch({type:"session.list",...t});const e=this.sessionHistoryState.data;return{sessions:S(e),nextCursor:e?.nextCursor??null}}getSessionHistoryState(){return A(this.sessionHistoryState)}subscribeSessionHistory(t){return this.sessionHistoryListeners.add(t),t(this.getSessionHistoryState()),()=>this.sessionHistoryListeners.delete(t)}refreshSessionHistory(){return this.dispatch({type:"session.list",limit:20}).then(()=>{})}loadMoreSessionHistory(){const t=this.sessionHistoryState.data?.nextCursor??null;return t?this.dispatch({type:"session.list",cursor:t,limit:20}).then(()=>{}):Promise.resolve()}setSessionPinned(t,e){return this.dispatch({type:"session.pin",sessionId:t,pinned:e}).then(()=>{})}renameSession(t,e){return this.dispatch({type:"session.rename",sessionId:t,title:e}).then(()=>{})}getInteractions(){return this.interactions.map(t=>({...t}))}subscribeInteractions(t){return this.interactionListeners.add(t),t(this.getInteractions()),()=>this.interactionListeners.delete(t)}actOnInteraction(t,e,i,n={}){return this.dispatch({type:"interaction.act",interactionId:t,action:e,expectedRevision:i,...n}).then(()=>{})}getAccount(){return this.account?{...this.account}:null}subscribeAccount(t){return this.accountListeners.add(t),t(this.getAccount()),()=>this.accountListeners.delete(t)}refreshAccount(){return Promise.resolve()}loadSession(t){return this.dispatch({type:"session.load",sessionId:t}).then(()=>{})}stageProactiveSuggestionQuestion(t){const e=t.trim();if(!e)return Promise.resolve();const i=this.stagedProactiveSuggestionClientMessageId??crypto.randomUUID();return this.stagedProactiveSuggestionClientMessageId=i,this.dispatch({type:"conversation.stage_proactive_suggestion",question:e,clientMessageId:i}).then(()=>{})}dispatch(t){return this.enqueueOperation(()=>this.adapter.dispatch(t))}enqueueOperation(t){const e=this.operationQueue.then(t);return this.operationQueue=e.then(()=>{},()=>{}),e}emit(t,e){const i=this.listeners[t];for(const n of i??[])n(e)}}function y(s){return{...s,starterPrompts:[...s.starterPrompts],appearance:s.appearance?{...s.appearance}:null,messages:s.messages.map(t=>{const e={...t,attachments:t.attachments?.map(r=>({...r}))},i=t.assistantDraftItemId,n=t.clientMessageId;return i&&Object.defineProperty(e,"assistantDraftItemId",{value:i,enumerable:!1}),n&&Object.defineProperty(e,"clientMessageId",{value:n,enumerable:!1}),e}),activeScheduledFollowUps:s.activeScheduledFollowUps?.map(t=>({...t}))??null}}function A(s){return{...s,data:s.data?h({entities:S(s.data).map(t=>({...t})),nextCursor:s.data.nextCursor}):null}}exports.ProductAgentInteractionManager=R;exports.ProductAgentQueryController=m;exports.ProductAgentRemoteRuntimeClient=F;exports.ProductAgentSessionHistoryManager=x;exports.ProductAgentTaskPageTitleController=Q;exports.createLocalStorageInteractionDecisionStorage=L;exports.createProductAgentQueryState=d;exports.createProductAgentTaskPageTitleDocument=$;exports.formatProductAgentTaskPageTitle=T;exports.hasProductAgentPaidCreditFallback=O;exports.isProductAgentRuntimeCommand=K;exports.mergeProductAgentEntityPages=P;exports.normalizeProductAgentEntityPage=h;exports.readLocalStorageInteractionDecisions=w;exports.reconcileProductAgentSubmissionGate=M;exports.resolveProductAgentBillingPresentation=_;exports.resolveProductAgentComposerAction=D;exports.resolveProductAgentWidgetPresentation=E;exports.selectProductAgentQueryEntities=S;