@newheap/platform-ai-chat 0.5.0 → 0.6.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.
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@newheap/platform-ai-chat",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "peerDependencies": {
5
5
  "@angular/cdk": "^20.2.14",
6
6
  "@angular/common": "^20.3.28",
7
7
  "@angular/core": "^20.3.28",
8
8
  "@angular/router": "^20.3.28",
9
+ "@microsoft/signalr": "^10.0.11",
9
10
  "@ngx-translate/core": "^17.0.0",
10
11
  "dompurify": "^3.4.0",
11
12
  "marked": "^18.0.0",
@@ -19,6 +20,9 @@
19
20
  "./i18n/*.json": {
20
21
  "default": "./i18n/*.json"
21
22
  },
23
+ "./push/*": {
24
+ "default": "./push/*"
25
+ },
22
26
  "./package.json": {
23
27
  "default": "./package.json"
24
28
  },
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Web Push worker of @newheap/platform-ai-chat.
3
+ *
4
+ * Copy this file into the application's assets, for example to /nh-assistant/, and set
5
+ * `push.serviceWorkerUrl` in `provideNhAssistant`. The assistant registers it with its own
6
+ * scope, so it never controls pages and does not conflict with an application service worker.
7
+ *
8
+ * It shows a notification only when no window of the application is focused; otherwise it
9
+ * forwards the message to the open windows, which already show the conversation live. A click
10
+ * focuses an open window and opens the conversation there, or opens `app` with the conversation
11
+ * in the URL fragment. Notifications carry a conversation id and short texts only.
12
+ */
13
+ const settings = new URL(self.location.href).searchParams;
14
+ const appUrl = settings.get('app') || '/';
15
+ const iconUrl = settings.get('icon') || undefined;
16
+
17
+ self.addEventListener('install', () => self.skipWaiting());
18
+ self.addEventListener('activate', event => event.waitUntil(self.clients.claim()));
19
+
20
+ self.addEventListener('push', event => {
21
+ let payload = null;
22
+ try {
23
+ payload = event.data ? event.data.json() : null;
24
+ } catch {
25
+ payload = null;
26
+ }
27
+ if (!payload || payload.type !== 'nh-assistant' || typeof payload.conversationId !== 'string') {
28
+ return;
29
+ }
30
+
31
+ event.waitUntil((async () => {
32
+ const windows = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
33
+ if (windows.some(client => client.focused && client.visibilityState === 'visible')) {
34
+ for (const client of windows) {
35
+ client.postMessage({ type: 'nh-assistant.push', kind: payload.kind, conversationId: payload.conversationId });
36
+ }
37
+ return;
38
+ }
39
+
40
+ await self.registration.showNotification(String(payload.title || ''), {
41
+ body: String(payload.body || ''),
42
+ tag: String(payload.tag || `nh-assistant:${payload.conversationId}`),
43
+ renotify: true,
44
+ icon: iconUrl,
45
+ data: { conversationId: payload.conversationId }
46
+ });
47
+ })());
48
+ });
49
+
50
+ self.addEventListener('notificationclick', event => {
51
+ event.notification.close();
52
+ const conversationId = event.notification.data && event.notification.data.conversationId;
53
+ if (typeof conversationId !== 'string') {
54
+ return;
55
+ }
56
+
57
+ event.waitUntil((async () => {
58
+ const windows = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
59
+ const target = windows.find(client => client.focused) || windows[0];
60
+ if (target) {
61
+ await target.focus();
62
+ target.postMessage({ type: 'nh-assistant.open-conversation', conversationId });
63
+ return;
64
+ }
65
+
66
+ const url = new URL(appUrl, self.location.origin);
67
+ url.hash = `nh-assistant-conversation=${encodeURIComponent(conversationId)}`;
68
+ await self.clients.openWindow(url.href);
69
+ })());
70
+ });
@@ -1,6 +1,7 @@
1
- import { AssistantStatus, AgentSummary, Conversation, ClientContext, AssistantPreferences, ToolCatalogEntry, AdminAgentInput, McpServerInput } from '@newheap/platform-ai-chat';
2
- import * as _angular_core from '@angular/core';
1
+ import { AssistantStatus, AgentSummary, Conversation, ClientContext, AssistantPreferences, ToolCatalogEntry, AdminAgentInput, McpServerInput, LiveConversationChanged, LiveConversationRead, LiveConversationRemoved, LiveConversationEvent, NhAssistantLiveService, NhAssistantLiveState } from '@newheap/platform-ai-chat';
2
+ import * as i0 from '@angular/core';
3
3
  import { InjectionToken, EnvironmentProviders } from '@angular/core';
4
+ import { Observable } from 'rxjs';
4
5
 
5
6
  /** Streams text as `message.delta` events, split into word-sized pieces. */
6
7
  interface NhAssistantMockTextStep {
@@ -112,6 +113,25 @@ interface NhAssistantMockAdminScenario {
112
113
  /** Hosts that may receive the user's token (`forward-user-token`). Default: none. */
113
114
  forwardUserTokenHosts?: string[];
114
115
  }
116
+ /** A person in the simulated organization. */
117
+ interface NhAssistantMockPerson {
118
+ actorId: string;
119
+ displayName: string;
120
+ /** Short distinguishing text the directory shows, such as a department. */
121
+ detail?: string;
122
+ }
123
+ /**
124
+ * Sharing, live updates and notifications of the mock. The mock always offers invitation links
125
+ * and live updates; with `directory` it also offers direct invitations.
126
+ */
127
+ interface NhAssistantMockCollaboration {
128
+ /** The signed-in user. Default `{ actorId: 'mock-user', displayName: 'You' }`. */
129
+ currentUser?: NhAssistantMockPerson;
130
+ /** Colleagues the directory finds; enables direct invitations. */
131
+ directory?: NhAssistantMockPerson[];
132
+ /** Default 20. */
133
+ maxParticipants?: number;
134
+ }
115
135
  /** The scenario the mock API plays without a back-end. */
116
136
  interface NhAssistantMockScenario {
117
137
  /** Default `true`. */
@@ -126,6 +146,7 @@ interface NhAssistantMockScenario {
126
146
  /** Stored preferences of the caller. Default: default style, informal, normal length. */
127
147
  preferences?: AssistantPreferences;
128
148
  admin?: NhAssistantMockAdminScenario;
149
+ collaboration?: NhAssistantMockCollaboration;
129
150
  }
130
151
  /** One request the mock API received, for assertions in tests. */
131
152
  interface NhAssistantMockRequest {
@@ -136,6 +157,22 @@ interface NhAssistantMockRequest {
136
157
  }
137
158
 
138
159
  declare const NH_ASSISTANT_MOCK_SCENARIO: InjectionToken<NhAssistantMockScenario>;
160
+ /** Path the mock reports as hub; the mock live service answers it in memory. */
161
+ declare const NH_ASSISTANT_MOCK_HUB_PATH = "/hub/assistant-mock";
162
+ /** One live update of the mock, as the assistant hub would send it. */
163
+ type NhAssistantMockLiveMessage = {
164
+ kind: 'changed';
165
+ data: LiveConversationChanged;
166
+ } | {
167
+ kind: 'read';
168
+ data: LiveConversationRead;
169
+ } | {
170
+ kind: 'removed';
171
+ data: LiveConversationRemoved;
172
+ } | {
173
+ kind: 'event';
174
+ data: LiveConversationEvent;
175
+ };
139
176
  /**
140
177
  * In-memory implementation of the assistant HTTP API and its event streams. It keeps
141
178
  * conversations, plays scripted turns as contract events through a real
@@ -152,12 +189,27 @@ declare class NhAssistantMockBackend {
152
189
  private readonly enabledState;
153
190
  private readonly admin;
154
191
  private readonly canAdministerState;
192
+ private readonly currentUser;
193
+ private readonly members;
194
+ private readonly readPositions;
195
+ private readonly shareTokens;
196
+ private readonly activeActors;
197
+ private readonly liveSubject;
198
+ private pushEnabled;
155
199
  private sequence;
156
200
  /** Requests received so far, oldest first. */
157
201
  readonly requests: NhAssistantMockRequest[];
158
- readonly enabled: _angular_core.Signal<boolean>;
159
- readonly canAdminister: _angular_core.Signal<boolean>;
202
+ readonly enabled: i0.Signal<boolean>;
203
+ readonly canAdminister: i0.Signal<boolean>;
204
+ /** Live updates for the mock live service, in the order the hub would send them. */
205
+ readonly live$: Observable<NhAssistantMockLiveMessage>;
160
206
  constructor();
207
+ /**
208
+ * Plays a scripted turn as another person in the conversation, as if a colleague with an
209
+ * invitation sent a message: the conversation becomes shared, and the message and the
210
+ * answer arrive as live updates. Resolves when the turn ends.
211
+ */
212
+ simulateParticipantTurn(conversationId: string, person: NhAssistantMockPerson, text: string): Promise<void>;
161
213
  /** Switches the simulated `NewHeap:AI:Assistant:Enabled` flag. */
162
214
  setEnabled(enabled: boolean): void;
163
215
  /** Switches whether the caller passes the admin policy (`canAdminister`, `admin/*`). */
@@ -177,22 +229,61 @@ declare class NhAssistantMockBackend {
177
229
  private stream;
178
230
  private findTurn;
179
231
  private update;
232
+ /** The conversation as the signed-in user sees it. */
233
+ private view;
234
+ private summary;
235
+ private markRead;
236
+ private candidates;
237
+ private invite;
238
+ private addMember;
239
+ private removeMember;
240
+ private emitChanged;
241
+ private emitLive;
242
+ private maxParticipants;
243
+ private error;
180
244
  private nextId;
181
245
  private result;
182
246
  private json;
183
247
  private empty;
184
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<NhAssistantMockBackend, never>;
185
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<NhAssistantMockBackend>;
248
+ static ɵfac: i0.ɵɵFactoryDeclaration<NhAssistantMockBackend, never>;
249
+ static ɵprov: i0.ɵɵInjectableDeclaration<NhAssistantMockBackend>;
250
+ }
251
+
252
+ type NhAssistantLiveSurface = Pick<NhAssistantLiveService, 'changed$' | 'read$' | 'removed$' | 'events$' | 'resync$' | 'state' | 'connect' | 'stop'>;
253
+ /**
254
+ * Stands in for the SignalR live service: it delivers the mock back-end's live updates in
255
+ * memory, so shared conversations, other tabs and parallel turns behave as with a real hub.
256
+ */
257
+ declare class NhAssistantMockLiveService implements NhAssistantLiveSurface {
258
+ private readonly backend;
259
+ private readonly changedSubject;
260
+ private readonly readSubject;
261
+ private readonly removedSubject;
262
+ private readonly eventSubject;
263
+ private readonly resyncSubject;
264
+ private readonly stateSignal;
265
+ private subscription?;
266
+ readonly changed$: Observable<LiveConversationChanged>;
267
+ readonly read$: Observable<LiveConversationRead>;
268
+ readonly removed$: Observable<LiveConversationRemoved>;
269
+ readonly events$: Observable<LiveConversationEvent>;
270
+ readonly resync$: Observable<void>;
271
+ readonly state: i0.Signal<NhAssistantLiveState>;
272
+ constructor();
273
+ connect(hubPath: string | null): Promise<void>;
274
+ stop(): Promise<void>;
275
+ static ɵfac: i0.ɵɵFactoryDeclaration<NhAssistantMockLiveService, never>;
276
+ static ɵprov: i0.ɵɵInjectableDeclaration<NhAssistantMockLiveService>;
186
277
  }
187
278
 
188
279
  /**
189
280
  * Replaces the assistant transport with an in-memory back-end that plays `script`.
190
281
  * Register it after `provideNhAssistant(...)` in the same injector. Every client feature
191
- * (status, agents, conversations, streamed turns, approvals, cancellation) then works
192
- * without a server. Inject `NhAssistantMockBackend` to switch the feature flag or to
193
- * inspect the received requests.
282
+ * (status, agents, conversations, streamed turns, approvals, cancellation, sharing, read
283
+ * state and live updates) then works without a server. Inject `NhAssistantMockBackend` to
284
+ * switch the feature flag, simulate a colleague or inspect the received requests.
194
285
  */
195
286
  declare function provideNhAssistantMockApi(script: NhAssistantMockScenario): EnvironmentProviders;
196
287
 
197
- export { NH_ASSISTANT_MOCK_SCENARIO, NhAssistantMockBackend, provideNhAssistantMockApi };
198
- export type { NhAssistantMockAdminAgent, NhAssistantMockAdminScenario, NhAssistantMockApprovalStep, NhAssistantMockErrorStep, NhAssistantMockFailStep, NhAssistantMockMcpServer, NhAssistantMockRemoteTool, NhAssistantMockRequest, NhAssistantMockScenario, NhAssistantMockStep, NhAssistantMockTextStep, NhAssistantMockTiming, NhAssistantMockToolStep, NhAssistantMockTurn };
288
+ export { NH_ASSISTANT_MOCK_HUB_PATH, NH_ASSISTANT_MOCK_SCENARIO, NhAssistantMockBackend, NhAssistantMockLiveService, provideNhAssistantMockApi };
289
+ export type { NhAssistantMockAdminAgent, NhAssistantMockAdminScenario, NhAssistantMockApprovalStep, NhAssistantMockCollaboration, NhAssistantMockErrorStep, NhAssistantMockFailStep, NhAssistantMockLiveMessage, NhAssistantMockMcpServer, NhAssistantMockPerson, NhAssistantMockRemoteTool, NhAssistantMockRequest, NhAssistantMockScenario, NhAssistantMockStep, NhAssistantMockTextStep, NhAssistantMockTiming, NhAssistantMockToolStep, NhAssistantMockTurn };