@interopio/io-assist-ng 0.0.1 → 1.0.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/README.internal.md +865 -0
- package/README.md +511 -140
- package/README.user.md +159 -0
- package/dist/README.md +511 -140
- package/dist/fesm2022/interopio-io-assist-ng.mjs +4388 -1942
- package/dist/fesm2022/interopio-io-assist-ng.mjs.map +1 -1
- package/dist/index.d.ts +123 -8
- package/dist/styles.css +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
# io.Assist — Internal Developer Documentation
|
|
2
|
+
|
|
3
|
+
**Audience:** Developers working on or extending `@interopio/io-assist-ng`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Architecture Overview](#architecture-overview)
|
|
10
|
+
- [Project Structure](#project-structure)
|
|
11
|
+
- [Public API Surface](#public-api-surface)
|
|
12
|
+
- [Configuration & Validation](#configuration--validation)
|
|
13
|
+
- [Provider Registration](#provider-registration)
|
|
14
|
+
- [Root Component — IoAssist](#root-component--ioassist)
|
|
15
|
+
- [State Management (NgRx Store)](#state-management-ngrx-store)
|
|
16
|
+
- [Store Shape](#store-shape)
|
|
17
|
+
- [Facades](#facades)
|
|
18
|
+
- [Effects](#effects)
|
|
19
|
+
- [Services](#services)
|
|
20
|
+
- [IOConnectService](#ioconnectservice)
|
|
21
|
+
- [IOIntelWebService](#iointelwebservice)
|
|
22
|
+
- [AgentService](#agentservice)
|
|
23
|
+
- [AgentManagerService](#agentmanagerservice)
|
|
24
|
+
- [SamplingService](#samplingservice)
|
|
25
|
+
- [ElicitationService](#elicitationservice)
|
|
26
|
+
- [McpAppsService](#mcpappsservice)
|
|
27
|
+
- [ThreadService](#threadservice)
|
|
28
|
+
- [ToolsService](#toolsservice)
|
|
29
|
+
- [PromptService](#promptservice)
|
|
30
|
+
- [WorkingContextService](#workingcontextservice)
|
|
31
|
+
- [OverlayService](#overlayservice)
|
|
32
|
+
- [ConsumerAssetLoaderService](#consumerassetloaderservice)
|
|
33
|
+
- [ResponsiveUIService](#responsiveuiservice)
|
|
34
|
+
- [LoggerService](#loggerservice)
|
|
35
|
+
- [ComponentEffectManagerService](#componenteffectmanagerservice)
|
|
36
|
+
- [SubscriptionCleanupService](#subscriptioncleanupservice)
|
|
37
|
+
- [CopyToClipboardService](#copytoclipboardservice)
|
|
38
|
+
- [Components](#components)
|
|
39
|
+
- [Chat Layout](#chat-layout)
|
|
40
|
+
- [Header](#header)
|
|
41
|
+
- [Input Area](#input-area)
|
|
42
|
+
- [Messages](#messages)
|
|
43
|
+
- [Threads](#threads)
|
|
44
|
+
- [Tools](#tools)
|
|
45
|
+
- [Prompts](#prompts)
|
|
46
|
+
- [Working Context Panel](#working-context-panel)
|
|
47
|
+
- [MCP App Resource](#mcp-app-resource)
|
|
48
|
+
- [Shared UI Primitives](#shared-ui-primitives)
|
|
49
|
+
- [Directives](#directives)
|
|
50
|
+
- [AG-UI Streaming Pipeline](#ag-ui-streaming-pipeline)
|
|
51
|
+
- [Response Stream Store](#response-stream-store)
|
|
52
|
+
- [Sampling & Elicitation Flow](#sampling--elicitation-flow)
|
|
53
|
+
- [MCP Apps Integration](#mcp-apps-integration)
|
|
54
|
+
- [Styling](#styling)
|
|
55
|
+
- [Testing](#testing)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Architecture Overview
|
|
60
|
+
|
|
61
|
+
`@interopio/io-assist-ng` is a standalone Angular library (no NgModule). The consumer calls `provideIoAssist(config)` once in their app config and drops `<io-assist [config]="dynamicConfig()">` into a template. Everything else — store, effects, services, UI — is bootstrapped internally.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Consumer App
|
|
65
|
+
└── provideIoAssist(config) ← registers DI providers
|
|
66
|
+
├── IO_ASSIST_CONFIG token ← validated static config
|
|
67
|
+
├── IO_ASSIST_DYNAMIC_CONFIG ← WritableSignal<IoAssistDynamicConfig> (seeded empty)
|
|
68
|
+
├── provideIoConnect(...) ← io.Connect Angular integration
|
|
69
|
+
├── provideStore(appReducers) ← NgRx store with 8 slices
|
|
70
|
+
├── provideEffects(...) ← 7 effect classes
|
|
71
|
+
└── provideStoreDevtools()
|
|
72
|
+
|
|
73
|
+
└── <io-assist [config]="dynamicConfig()"> ← root component
|
|
74
|
+
├── input.required<IoAssistDynamicConfig>() ← runtime user + headers
|
|
75
|
+
├── effect() → IO_ASSIST_DYNAMIC_CONFIG.set(config)
|
|
76
|
+
├── IOConnectService ← io.Connect readiness + theme sync
|
|
77
|
+
├── IOIntelWebService ← ai-web API init
|
|
78
|
+
├── AppLifecycleFacade ← lifecycle state signals
|
|
79
|
+
└── <chat /> ← full UI tree
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Key layers:**
|
|
83
|
+
|
|
84
|
+
| Layer | Responsibility |
|
|
85
|
+
|-------|---------------|
|
|
86
|
+
| **Static Config / Validation** | Zod schema validates `IoAssistStaticConfig` at provider registration time |
|
|
87
|
+
| **Dynamic Config** | `IoAssistDynamicConfig` passed as component input, synced to `IO_ASSIST_DYNAMIC_CONFIG` signal |
|
|
88
|
+
| **NgRx Store** | 8 feature slices with reducers, selectors, and facades |
|
|
89
|
+
| **Effects** | Async operations (API calls, streaming, lifecycle) |
|
|
90
|
+
| **Services** | Business logic, API integration, UI orchestration |
|
|
91
|
+
| **Components** | Standalone Angular components with signal-based reactivity |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Project Structure
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
src/
|
|
99
|
+
├── public-api.ts # Package exports
|
|
100
|
+
├── styles.css # Tailwind entry point
|
|
101
|
+
└── lib/
|
|
102
|
+
├── io-assist.component.ts # Root <io-assist> component
|
|
103
|
+
├── io-assist.component.html # Root template
|
|
104
|
+
├── io-assist.providers.ts # provideIoAssist() function
|
|
105
|
+
├── io-assist.config.ts # Config types + IO_ASSIST_CONFIG token
|
|
106
|
+
├── io-assist.schema.ts # Zod validation schema
|
|
107
|
+
├── io-assist.types.ts # Prompt/icon types
|
|
108
|
+
├── components/
|
|
109
|
+
│ ├── chat/ # Main chat layout
|
|
110
|
+
│ ├── header/ # App header with thread toggle
|
|
111
|
+
│ ├── input-area/ # Message input + action bar
|
|
112
|
+
│ ├── messages/ # Message rendering (user, assistant, tool)
|
|
113
|
+
│ ├── threads/ # Thread history panel
|
|
114
|
+
│ ├── tool/ # Tool list + management
|
|
115
|
+
│ ├── prompt/ # Prompt library + favorites
|
|
116
|
+
│ └── working-context-panel/ # Working context inspector
|
|
117
|
+
└── shared/
|
|
118
|
+
├── components/ # Reusable UI primitives
|
|
119
|
+
├── constants/ # UI strings, config constants
|
|
120
|
+
├── directives/ # Animation + accent border directives
|
|
121
|
+
├── enums/ # Loading state, message roles, etc.
|
|
122
|
+
├── services/ # All business logic services
|
|
123
|
+
├── store/ # NgRx store (8 feature slices)
|
|
124
|
+
├── styles/ # Shared SCSS/Tailwind utilities
|
|
125
|
+
├── types/ # Shared type definitions
|
|
126
|
+
└── utils/ # Icon parsing, sanitization utilities
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Public API Surface
|
|
132
|
+
|
|
133
|
+
File: `src/public-api.ts`
|
|
134
|
+
|
|
135
|
+
Only these are exported — everything else is internal:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
export * from './lib/io-assist.component'; // IoAssist component
|
|
139
|
+
export * from './lib/io-assist.providers'; // provideIoAssist()
|
|
140
|
+
export * from './lib/io-assist.types'; // IoAssistPrompt, IoAssistPromptCategory, IconResource, IconType
|
|
141
|
+
export * from './lib/io-assist.config'; // IoAssistStaticConfig, IoAssistDynamicConfig, AIWebConfig, IoAssistUserConfig, IO_ASSIST_CONFIG, IO_ASSIST_DYNAMIC_CONFIG
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Configuration & Validation
|
|
147
|
+
|
|
148
|
+
### Config types
|
|
149
|
+
|
|
150
|
+
File: `src/lib/io-assist.config.ts`
|
|
151
|
+
|
|
152
|
+
io.Assist uses two separate configuration types:
|
|
153
|
+
|
|
154
|
+
**Static config** — passed to `provideIoAssist()` at bootstrap. Does not include user identity.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
type IoAssistStaticConfig = {
|
|
158
|
+
connectConfig: IOConnectNgSettings; // io.Connect platform settings
|
|
159
|
+
aiWebConfig: AIWebConfig; // { agentServer, mcp? }
|
|
160
|
+
defaultAgentName?: string;
|
|
161
|
+
workingContext?: {
|
|
162
|
+
factory: IoIntelWorkingContextFactoryFunction;
|
|
163
|
+
config?: IoIntelWorkingContext.Config;
|
|
164
|
+
};
|
|
165
|
+
defaultPrompts?: IoAssistPromptCategory[];
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
type AIWebConfig = {
|
|
169
|
+
agentServer: Omit<IoAiWeb.WebConfig['agentServer'], 'headers'>; // headers excluded
|
|
170
|
+
mcp?: IoAiWeb.WebConfig['mcp'];
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Dynamic config** — passed as `[config]` input to `<io-assist>` at runtime. Holds the active user's identity and request headers.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
type IoAssistUserConfig = { id: string; name?: string };
|
|
178
|
+
|
|
179
|
+
type IoAssistDynamicConfig = {
|
|
180
|
+
user: IoAssistUserConfig;
|
|
181
|
+
agentServer?: {
|
|
182
|
+
headers?: Record<string, string>; // request headers for every agent call (e.g. auth tokens)
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### DI tokens
|
|
188
|
+
|
|
189
|
+
| Token | Type | Set by |
|
|
190
|
+
|-------|------|--------|
|
|
191
|
+
| `IO_ASSIST_CONFIG` | `InjectionToken<IoAssistStaticConfig>` | `provideIoAssist()` (once, at bootstrap) |
|
|
192
|
+
| `IO_ASSIST_DYNAMIC_CONFIG` | `InjectionToken<WritableSignal<IoAssistDynamicConfig>>` | `provideIoAssist()` seeds with `{ user: { id: '' } }`; overwritten on each `IoAssist` component input change via `effect()` |
|
|
193
|
+
|
|
194
|
+
### Zod validation
|
|
195
|
+
|
|
196
|
+
File: `src/lib/io-assist.schema.ts`
|
|
197
|
+
|
|
198
|
+
Two schemas are defined:
|
|
199
|
+
|
|
200
|
+
**`IoAssistStaticConfigSchema`** — validated at `provideIoAssist()` time. Key validations:
|
|
201
|
+
|
|
202
|
+
- `aiWebConfig.agentServer.baseUrl` — valid URL
|
|
203
|
+
- `mcp.mcpApps.sandboxProxyUrl` — non-empty string if `mcpApps` provided
|
|
204
|
+
- `mcp.clientsConfig.capabilities.elicitation.handler` — function type
|
|
205
|
+
- `defaultPrompts[].prompts[].name` — non-empty string
|
|
206
|
+
- `defaultPrompts[].prompts[].iconResource.type` — enum `'svg' | 'url' | 'data-url'`
|
|
207
|
+
|
|
208
|
+
If validation fails, `provideIoAssist()` throws `"Invalid IoAssist configuration provided."`.
|
|
209
|
+
|
|
210
|
+
**`IoAssistDynamicConfigSchema`** — validated in `IoAssist.ngOnInit()`. Key validations:
|
|
211
|
+
|
|
212
|
+
- `user.id` — non-empty string (required)
|
|
213
|
+
- `agentServer.headers` — optional record of strings
|
|
214
|
+
|
|
215
|
+
If validation fails, `ngOnInit` throws a `ZodError`.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Provider Registration
|
|
220
|
+
|
|
221
|
+
File: `src/lib/io-assist.providers.ts`
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
function provideIoAssist(config: IoAssistStaticConfig): EnvironmentProviders
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Registers:
|
|
228
|
+
|
|
229
|
+
1. `IO_ASSIST_CONFIG` — the Zod-validated static config
|
|
230
|
+
2. `IO_ASSIST_DYNAMIC_CONFIG` — `useFactory: () => signal<IoAssistDynamicConfig>({ user: { id: '' } })` (empty seed; overwritten by the `IoAssist` component input)
|
|
231
|
+
3. `provideBrowserGlobalErrorListeners()` — Angular global error handling
|
|
232
|
+
4. `provideZonelessChangeDetection()` — zoneless Angular (signal-based)
|
|
233
|
+
5. `provideStore(appReducers)` — NgRx store with all 8 reducers
|
|
234
|
+
6. `provideEffects(...)` — 7 effect classes
|
|
235
|
+
7. `provideStoreDevtools({ maxAge: 25 })` — Redux DevTools integration
|
|
236
|
+
8. `provideIoConnect(config.connectConfig)` — io.Connect Angular bootstrap
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Root Component — IoAssist
|
|
241
|
+
|
|
242
|
+
File: `src/lib/io-assist.component.ts`
|
|
243
|
+
|
|
244
|
+
Selector: `<io-assist>`
|
|
245
|
+
|
|
246
|
+
**Input:**
|
|
247
|
+
|
|
248
|
+
| Input | Type | Description |
|
|
249
|
+
|-------|------|-------------|
|
|
250
|
+
| `config` | `IoAssistDynamicConfig` (required) | Active user identity + optional per-request headers. Validated in `ngOnInit`. |
|
|
251
|
+
|
|
252
|
+
**Lifecycle:**
|
|
253
|
+
|
|
254
|
+
1. `effect()` — syncs the `config` input into the `IO_ASSIST_DYNAMIC_CONFIG` writable signal whenever the input changes.
|
|
255
|
+
2. `ngOnInit` — validates `config` via `IoAssistDynamicConfigSchema.parse()` (throws `ZodError` if invalid), writes config to `IO_ASSIST_DYNAMIC_CONFIG`, then registers the io.Connect readiness effect.
|
|
256
|
+
3. `ngAfterViewInit` — calls `ConsumerAssetLoaderService.loadConsumerAssets()` to inject fonts and Prism.js into the DOM.
|
|
257
|
+
|
|
258
|
+
**Template states:**
|
|
259
|
+
|
|
260
|
+
- **Pending** — shows spinner while io.Connect initializes and core services start
|
|
261
|
+
- **Error** — shows error message with "Refresh" button
|
|
262
|
+
- **Ready** — renders `<chat />` (the full UI tree)
|
|
263
|
+
|
|
264
|
+
**Signals used:**
|
|
265
|
+
|
|
266
|
+
| Signal | Source | Purpose |
|
|
267
|
+
|--------|--------|---------|
|
|
268
|
+
| `isIoReady` | `IOConnectService.isIoConnectReady` | io.Connect platform ready |
|
|
269
|
+
| `isAppCoreServicesStarted` | `AppLifecycleFacade` | ai-web initialized + agents/prompts loaded |
|
|
270
|
+
| `appCoreServicesError` | `AppLifecycleFacade` | Error message if init failed |
|
|
271
|
+
| `isAppCoreServicesPending` | `AppLifecycleFacade` | Currently initializing |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## State Management (NgRx Store)
|
|
276
|
+
|
|
277
|
+
### Store Shape
|
|
278
|
+
|
|
279
|
+
File: `src/lib/shared/store/app.state.ts`
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
type AppState = {
|
|
283
|
+
threadsStore: ThreadReducerStateType;
|
|
284
|
+
appLifecycleStore: AppLifecycleStateType;
|
|
285
|
+
promptStore: PromptReducerStateType;
|
|
286
|
+
toolStore: ToolReducerStateType;
|
|
287
|
+
messageStore: MessageReducerStateType;
|
|
288
|
+
responseStreamStore: ResponseStreamReducerStateType;
|
|
289
|
+
agentStore: AgentReducerStateType;
|
|
290
|
+
workingContextStore: WorkingContextReducerStateType;
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Facades
|
|
295
|
+
|
|
296
|
+
Every store slice has a facade that wraps selectors as Angular signals and provides typed dispatch methods. Components inject facades, not the raw store.
|
|
297
|
+
|
|
298
|
+
**AgentFacade** — `store/agent/agent.facade.ts`
|
|
299
|
+
|
|
300
|
+
| Signal | Description |
|
|
301
|
+
|--------|-------------|
|
|
302
|
+
| `availableAgents` | All agents from the server |
|
|
303
|
+
| `selectedAgent` | Currently active agent |
|
|
304
|
+
|
|
305
|
+
| Method | Description |
|
|
306
|
+
|--------|-------------|
|
|
307
|
+
| `dispatchListAvailableAgents()` | Fetch agents from server |
|
|
308
|
+
| `dispatchSelectAgent(agentId)` | Switch active agent |
|
|
309
|
+
|
|
310
|
+
**AppLifecycleFacade** — `store/app-lifecycle/app-lifecycle.facade.ts`
|
|
311
|
+
|
|
312
|
+
| Signal | Description |
|
|
313
|
+
|--------|-------------|
|
|
314
|
+
| `isAppCoreServicesStarted` | Core services initialized successfully |
|
|
315
|
+
| `appCoreServicesLoadingState` | Loading state enum value |
|
|
316
|
+
| `isPendingAppCoreServicesOperation` | Currently initializing |
|
|
317
|
+
| `appCoreServicesErrorMessage` | Error message or null |
|
|
318
|
+
|
|
319
|
+
| Method | Description |
|
|
320
|
+
|--------|-------------|
|
|
321
|
+
| `dispatchInitAppCoreServices()` | Trigger initialization |
|
|
322
|
+
|
|
323
|
+
**MessageFacade** — `store/message/message.facade.ts`
|
|
324
|
+
|
|
325
|
+
| Signal | Description |
|
|
326
|
+
|--------|-------------|
|
|
327
|
+
| `allMessages` | All messages for active thread |
|
|
328
|
+
| `messageLength` | Message count |
|
|
329
|
+
| `lastUserMessage` | Most recent user message |
|
|
330
|
+
| `isGeneratingResponse` | Stream in progress |
|
|
331
|
+
| `isLoadingMessagesFromThread` | Loading thread messages |
|
|
332
|
+
| `toolTraceState` | Tool trace expand/collapse state |
|
|
333
|
+
| `isLastResponseSuccess` | Last response completed without error |
|
|
334
|
+
|
|
335
|
+
| Method | Description |
|
|
336
|
+
|--------|-------------|
|
|
337
|
+
| `dispatchGetResponse(params, threadId, isStream?, agent?)` | Send user message and get agent response |
|
|
338
|
+
| `dispatchReloadResponse(params, threadId, isStream?, agent?)` | Regenerate last response |
|
|
339
|
+
| `dispatchClearMessages()` | Clear all messages |
|
|
340
|
+
| `dispatchFetchMessagesFromThread(thread)` | Load messages from an existing thread |
|
|
341
|
+
| `dispatchAbortResponseGeneration(threadId)` | Cancel active stream |
|
|
342
|
+
| `dispatchToggleToolTrace(stateForMessageId)` | Expand/collapse tool trace |
|
|
343
|
+
|
|
344
|
+
**ThreadFacade** — `store/thread/thread.facade.ts`
|
|
345
|
+
|
|
346
|
+
| Signal | Description |
|
|
347
|
+
|--------|-------------|
|
|
348
|
+
| `allThreads` | All threads for current user |
|
|
349
|
+
| `activeThreadId` | Currently active thread ID |
|
|
350
|
+
| `activeThread` | Currently active thread object |
|
|
351
|
+
| `isFetchingThreads` | Loading threads |
|
|
352
|
+
|
|
353
|
+
| Method | Description |
|
|
354
|
+
|--------|-------------|
|
|
355
|
+
| `dispatchFetchThreads(agentId)` | Fetch all threads for agent + user |
|
|
356
|
+
| `dispatchRenameThread(thread, newTitle)` | Rename a thread |
|
|
357
|
+
| `dispatchDeleteThread(thread)` | Delete a thread |
|
|
358
|
+
| `dispatchChangeActiveThread(threadId)` | Switch active thread |
|
|
359
|
+
|
|
360
|
+
**ResponseStreamFacade** — `store/response-stream/response-stream.facade.ts`
|
|
361
|
+
|
|
362
|
+
| Signal | Description |
|
|
363
|
+
|--------|-------------|
|
|
364
|
+
| `allStreams` | All per-thread stream states |
|
|
365
|
+
| `threadsCurrentlyStreaming` | Thread IDs with active streams |
|
|
366
|
+
| `threadsWithCompletionNotification` | Threads with background completion notifications |
|
|
367
|
+
| `hasAnyCompletionNotification` | Any thread has a background notification |
|
|
368
|
+
|
|
369
|
+
| Method | Description |
|
|
370
|
+
|--------|-------------|
|
|
371
|
+
| `isThreadStreaming(threadId)` | Check if specific thread is streaming |
|
|
372
|
+
| `createIsStreamingSignal(threadIdSignal)` | Reactive signal from thread ID signal |
|
|
373
|
+
| `dispatchStartThreadStream(threadId, userMessage)` | Start tracking a stream |
|
|
374
|
+
| `dispatchCompleteThreadStream(threadId, shouldNotify)` | Mark stream complete |
|
|
375
|
+
|
|
376
|
+
**ToolFacade** — `store/tool/tool.facade.ts`
|
|
377
|
+
|
|
378
|
+
| Signal | Description |
|
|
379
|
+
|--------|-------------|
|
|
380
|
+
| `allTools` | All available tools |
|
|
381
|
+
| `enabledTools` | Currently enabled tools |
|
|
382
|
+
|
|
383
|
+
| Method | Description |
|
|
384
|
+
|--------|-------------|
|
|
385
|
+
| `dispatchFetchTools()` | Fetch all tools from MCP servers |
|
|
386
|
+
| `dispatchToggleTool(tool)` | Enable/disable a tool |
|
|
387
|
+
|
|
388
|
+
**PromptFacade** — `store/prompt/prompt.facade.ts`
|
|
389
|
+
|
|
390
|
+
| Signal | Description |
|
|
391
|
+
|--------|-------------|
|
|
392
|
+
| `allPrompts` | All parsed prompts |
|
|
393
|
+
| `favoritePromptNames` | Names of favorited prompts |
|
|
394
|
+
| `selectedPrompt` | Currently selected prompt |
|
|
395
|
+
|
|
396
|
+
| Method | Description |
|
|
397
|
+
|--------|-------------|
|
|
398
|
+
| `dispatchParsePromptsConfig()` | Parse and normalize configured prompts |
|
|
399
|
+
| `dispatchSelectPrompt(prompt)` | Select a prompt (fills input) |
|
|
400
|
+
| `dispatchToggleFavoritePrompt(name)` | Toggle favorite state |
|
|
401
|
+
|
|
402
|
+
**WorkingContextFacade** — `store/working-context/working-context.facade.ts`
|
|
403
|
+
|
|
404
|
+
| Signal | Description |
|
|
405
|
+
|--------|-------------|
|
|
406
|
+
| `workingContext` | Current working context values |
|
|
407
|
+
| `isWorkingContextEnabled` | Whether working context is configured |
|
|
408
|
+
|
|
409
|
+
| Method | Description |
|
|
410
|
+
|--------|-------------|
|
|
411
|
+
| `dispatchGetWorkingContext()` | Fetch current context |
|
|
412
|
+
| `dispatchUpdateWorkingContext(context)` | Update with new values |
|
|
413
|
+
|
|
414
|
+
### Effects
|
|
415
|
+
|
|
416
|
+
| Effect Class | Key Operations |
|
|
417
|
+
|-------------|----------------|
|
|
418
|
+
| `AppLifecycleEffects` | Monitors IOIntelWebService init state → dispatches success/failure → triggers agent list + prompt parse |
|
|
419
|
+
| `AgentEffects` | Lists agents from server, selects agent (by config name or first available), fetches threads + tools on selection |
|
|
420
|
+
| `MessageEffects` | Handles `getResponse`/`reloadResponse` → streams AG-UI events into store actions (see [AG-UI Streaming Pipeline](#ag-ui-streaming-pipeline)) |
|
|
421
|
+
| `ThreadEffects` | Fetches threads for agent + user (reads `user.id` from `IO_ASSIST_DYNAMIC_CONFIG`), rename/delete, cleans up MCP app prefs on delete |
|
|
422
|
+
| `ToolEffects` | Fetches all tools from MCP servers, toggles tool enabled state |
|
|
423
|
+
| `PromptEffects` | Parses config `defaultPrompts` into UI-ready prompt objects via `PromptService` |
|
|
424
|
+
| `WorkingContextEffects` | Fetches and subscribes to working context changes via `WorkingContextService` |
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Services
|
|
429
|
+
|
|
430
|
+
### IOConnectService
|
|
431
|
+
|
|
432
|
+
File: `shared/services/io/io.service.ts`
|
|
433
|
+
|
|
434
|
+
Connects to io.Connect platform. Exposes:
|
|
435
|
+
- `isIoConnectReady` — signal from `IOConnectStore`
|
|
436
|
+
- `isDarkMode` — signal synced with io.Connect theme
|
|
437
|
+
- `startIODependentTasks()` — subscribes to theme changes, initializes ai-web factory, dispatches lifecycle init
|
|
438
|
+
- `requestIOModal(config)` — wraps io.Connect modals API
|
|
439
|
+
- `isModalsApiAvailable()` — checks if modals library is loaded
|
|
440
|
+
|
|
441
|
+
### IOIntelWebService
|
|
442
|
+
|
|
443
|
+
File: `shared/services/io-ai-web/io-ai-web.service.ts`
|
|
444
|
+
|
|
445
|
+
Initializes `@interopio/ai-web` API. Key behavior:
|
|
446
|
+
- `initialize()` — builds `IoAiWeb.WebConfig` from `IO_ASSIST_CONFIG`, calls `IoAiWebFactory`, attaches MCP apps service
|
|
447
|
+
- `buildMcpConfig(mcp)` — injects sampling/elicitation handlers from `SamplingService`/`ElicitationService` (custom or built-in)
|
|
448
|
+
- `constructConfig()` — spreads static `agentServer` fields, and sets `headers` from `IO_ASSIST_DYNAMIC_CONFIG.agentServer.headers` (the sole source of request headers)
|
|
449
|
+
- Exposes signals: `isInitialized`, `isInitializing`, `isError`
|
|
450
|
+
- Exposes API wrappers: `listAgents()`, `listThreads()`, `listTools()`, `getWorkingContext()`, etc.
|
|
451
|
+
|
|
452
|
+
### AgentService
|
|
453
|
+
|
|
454
|
+
File: `shared/services/agent/agent.service.ts`
|
|
455
|
+
|
|
456
|
+
Builds API call parameters. Key methods:
|
|
457
|
+
- `getResponse(params, isStream, agent)` — calls ai-web stream/generate
|
|
458
|
+
- `reloadResponse(params, isStream, agent)` — regenerates last response
|
|
459
|
+
- `getSamplingResponse(params, skipThread)` — non-streaming generation for sampling flow
|
|
460
|
+
- `buildAgentParams(params, skipThreadManagement)` — constructs memory params with `thread: threadId, resource: user.id`
|
|
461
|
+
|
|
462
|
+
**Critical behavior:**
|
|
463
|
+
- `user.id` is read from `IO_ASSIST_DYNAMIC_CONFIG().user.id` at call time — this scopes all threads to the current user
|
|
464
|
+
- When `skipThreadManagement=true` (sampling), the active thread is NOT inherited to avoid polluting conversation history
|
|
465
|
+
- Empty assistant messages are filtered out before sending (prevents Anthropic empty-content errors)
|
|
466
|
+
|
|
467
|
+
### AgentManagerService
|
|
468
|
+
|
|
469
|
+
File: `shared/services/agent/agent-manager/agent-manager.service.ts`
|
|
470
|
+
|
|
471
|
+
Lists available agents via `IOIntelWebService.listAgents()`.
|
|
472
|
+
|
|
473
|
+
### SamplingService
|
|
474
|
+
|
|
475
|
+
File: `shared/services/io-ai-web/sampling/sampling.service.ts`
|
|
476
|
+
|
|
477
|
+
Handles MCP sampling requests:
|
|
478
|
+
|
|
479
|
+
1. `selectSamplingHandler(mcp)` — returns custom handler if `mcp.clientsConfig.capabilities.sampling.handler` is provided, otherwise returns the built-in handler
|
|
480
|
+
2. Built-in flow:
|
|
481
|
+
- Rejects requests from background threads (user not on the requesting thread)
|
|
482
|
+
- Shows a confirmation UI:
|
|
483
|
+
- **io.Connect modal** (if `IOModals` library is available) — `noInputsConfirmationDialog` template
|
|
484
|
+
- **Built-in panel overlay** (fallback) — `OverlayService.showPanelOverlay()` with Continue/Cancel buttons
|
|
485
|
+
- On accept: calls `AgentService.getSamplingResponse()`, maps finish reason → MCP stop reason, returns `SamplingSuccessResponse`
|
|
486
|
+
- On decline: returns `SamplingErrorResponse` with rejection message
|
|
487
|
+
|
|
488
|
+
### ElicitationService
|
|
489
|
+
|
|
490
|
+
File: `shared/services/io-ai-web/elicitation/elicitation.service.ts`
|
|
491
|
+
|
|
492
|
+
Handles MCP elicitation requests:
|
|
493
|
+
|
|
494
|
+
1. `selectElicitationHandler(mcp)` — returns custom handler if provided, otherwise built-in
|
|
495
|
+
2. Built-in flow:
|
|
496
|
+
- Validates `_meta.toolName` starts with `io_connect`
|
|
497
|
+
- Rejects background thread requests
|
|
498
|
+
- Shows accept/decline/cancel UI (modal or overlay)
|
|
499
|
+
- Accept returns `{ action: 'accept', content: {} }` (empty content — actual form input is a TODO)
|
|
500
|
+
- Decline returns `{ action: 'decline' }`
|
|
501
|
+
- Cancel returns `{ action: 'cancel' }`
|
|
502
|
+
|
|
503
|
+
### McpAppsService
|
|
504
|
+
|
|
505
|
+
File: `shared/services/mcp-apps/mcp-apps.service.ts`
|
|
506
|
+
|
|
507
|
+
Wraps `ai-web` MCP Apps API for Angular consumption:
|
|
508
|
+
- `attach(api)` — subscribes to `onAppCreated`, `onRecreateRequested`, `onAppRecreated` events
|
|
509
|
+
- `activeInstances` — signal tracking all live `AppInstance` objects
|
|
510
|
+
- Handles duplicate instance dialog (3-option: replace oldest / replace all / new instance)
|
|
511
|
+
- Feeds app chat messages into the message store via `MessageFacade.dispatchGetResponse()`
|
|
512
|
+
- Thread switch triggers `closeAll()` + re-creation of pending apps for the new thread
|
|
513
|
+
|
|
514
|
+
### ThreadService
|
|
515
|
+
|
|
516
|
+
File: `shared/services/thread/thread.service.ts`
|
|
517
|
+
|
|
518
|
+
Thread operations:
|
|
519
|
+
- `fetchThreads(agentId, userId)` — lists threads from server
|
|
520
|
+
- `toUIThreads(threads)` — converts backend threads to UI model with relative-time labels
|
|
521
|
+
- `renameThread(thread, newTitle)` — renames via API
|
|
522
|
+
- `deleteThread(thread)` — deletes via API
|
|
523
|
+
- `deleteThreadState(threadId)` — cleans up persisted MCP app preferences for deleted threads
|
|
524
|
+
|
|
525
|
+
### ToolsService
|
|
526
|
+
|
|
527
|
+
File: `shared/services/tools/tools.service.ts`
|
|
528
|
+
|
|
529
|
+
- `listTools()` — fetches all tools from MCP servers
|
|
530
|
+
- `toggleTool(tool)` — enables/disables a tool
|
|
531
|
+
|
|
532
|
+
### PromptService
|
|
533
|
+
|
|
534
|
+
File: `shared/services/prompt/prompt.service.ts`
|
|
535
|
+
|
|
536
|
+
- Takes `defaultPrompts` from config and maps them to UI-ready objects
|
|
537
|
+
- Validates and normalizes icon resources (falls back to default icon)
|
|
538
|
+
- Icon sanitization: SVG strings have `fill`, `width`, `height` stripped; hardcoded colors → `currentColor`
|
|
539
|
+
|
|
540
|
+
### WorkingContextService
|
|
541
|
+
|
|
542
|
+
File: `shared/services/working-context/working-context.service.ts`
|
|
543
|
+
|
|
544
|
+
- `getWorkingContext()` — fetches current context values
|
|
545
|
+
- `subscribeToWorkingContext()` — subscribes to `onChanged` callback
|
|
546
|
+
- `isEnabled()` — checks if working context is configured
|
|
547
|
+
|
|
548
|
+
### OverlayService
|
|
549
|
+
|
|
550
|
+
File: `shared/services/overlay/overlay.service.ts`
|
|
551
|
+
|
|
552
|
+
CDK-based overlay system:
|
|
553
|
+
- `showPanelOverlay(config)` — creates centered overlay with backdrop, attaches `AppPanelComponent`
|
|
554
|
+
- Supports HTML content (string) or injected Angular component
|
|
555
|
+
- Maintains an overlay stack — backdrop click closes only the topmost overlay
|
|
556
|
+
- `closeCurrentOverlay()` — disposes the top overlay
|
|
557
|
+
- Also manages thread history panel visibility signal
|
|
558
|
+
|
|
559
|
+
### ConsumerAssetLoaderService
|
|
560
|
+
|
|
561
|
+
File: `shared/services/consumer-asset-loader/consumer-asset-loader.service.ts`
|
|
562
|
+
|
|
563
|
+
Injects external assets into the consumer app's DOM at runtime:
|
|
564
|
+
- **Google Inter font** — `<link>` to Google Fonts CDN
|
|
565
|
+
- **Prism.js CSS** — syntax highlighting styles
|
|
566
|
+
- **Prism.js JS** — syntax highlighting runtime
|
|
567
|
+
|
|
568
|
+
Called in `IoAssist.ngAfterViewInit()` after the DOM is ready.
|
|
569
|
+
|
|
570
|
+
### ResponsiveUIService
|
|
571
|
+
|
|
572
|
+
File: `shared/services/responsive-ui/responsive-ui.service.ts`
|
|
573
|
+
|
|
574
|
+
Tailwind-style breakpoint tracking:
|
|
575
|
+
- Uses `ResizeObserver` on the root component element
|
|
576
|
+
- Exposes signal-based breakpoint state matching Tailwind thresholds
|
|
577
|
+
- Components use it to adapt layout (e.g., compact header on small viewports)
|
|
578
|
+
|
|
579
|
+
### LoggerService
|
|
580
|
+
|
|
581
|
+
File: `shared/services/logger/logger.service.ts`
|
|
582
|
+
|
|
583
|
+
- Wraps io.Connect logger when available
|
|
584
|
+
- Falls back to `console` methods
|
|
585
|
+
- Namespaced: `logger.get('SamplingService').info(...)`
|
|
586
|
+
|
|
587
|
+
### ComponentEffectManagerService
|
|
588
|
+
|
|
589
|
+
File: `shared/services/component-effect-manager/component-effect-manager.service.ts`
|
|
590
|
+
|
|
591
|
+
- Named Angular `effect()` registration
|
|
592
|
+
- Tracks effects by name to prevent duplicates
|
|
593
|
+
- Auto-cleanup on service destroy
|
|
594
|
+
|
|
595
|
+
### SubscriptionCleanupService
|
|
596
|
+
|
|
597
|
+
File: `shared/services/subscription-cleanup/subscription-cleanup.service.ts`
|
|
598
|
+
|
|
599
|
+
- Named subscription registry (`add(name, subscription)`)
|
|
600
|
+
- `destroy()` unsubscribes all
|
|
601
|
+
- Used in components/services that manage RxJS subscriptions manually
|
|
602
|
+
|
|
603
|
+
### CopyToClipboardService
|
|
604
|
+
|
|
605
|
+
File: `shared/services/copy-to-clipboard/copy-to-clipboard.service.ts`
|
|
606
|
+
|
|
607
|
+
- Uses `navigator.clipboard.writeText()` with `<textarea>` fallback for older browsers
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Components
|
|
612
|
+
|
|
613
|
+
### Chat Layout
|
|
614
|
+
|
|
615
|
+
File: `components/chat/chat.component.ts`
|
|
616
|
+
|
|
617
|
+
Main container component. Renders:
|
|
618
|
+
- Thread history panel (toggle sidebar)
|
|
619
|
+
- Header
|
|
620
|
+
- Message area
|
|
621
|
+
- Favorite prompts (shown on home screen when no messages)
|
|
622
|
+
- Input area
|
|
623
|
+
- AI disclaimer footer
|
|
624
|
+
|
|
625
|
+
### Header
|
|
626
|
+
|
|
627
|
+
File: `components/header/header.component.ts`
|
|
628
|
+
|
|
629
|
+
- Thread history toggle button (with notification dot for background completions)
|
|
630
|
+
- Home button (starts new thread)
|
|
631
|
+
- Working context button (opens inspector panel)
|
|
632
|
+
|
|
633
|
+
### Input Area
|
|
634
|
+
|
|
635
|
+
File: `components/input-area/input-area.component.ts`
|
|
636
|
+
|
|
637
|
+
- Auto-resizing textarea
|
|
638
|
+
- `Enter` sends, `Shift+Enter` for newlines
|
|
639
|
+
- `Shift+Up/Down` cycles through user's message history
|
|
640
|
+
- Action bar with tools panel and prompt library buttons
|
|
641
|
+
- Send button with loading state during streaming
|
|
642
|
+
|
|
643
|
+
### Messages
|
|
644
|
+
|
|
645
|
+
| Component | Purpose |
|
|
646
|
+
|-----------|---------|
|
|
647
|
+
| `MessageAreaComponent` | Scrollable message list with auto-scroll |
|
|
648
|
+
| `UserMessageComponent` | User message bubble |
|
|
649
|
+
| `AssistantMessageComponent` | Markdown-rendered assistant response |
|
|
650
|
+
| `ToolTraceMessageComponent` | Expandable tool call trace (name + status) |
|
|
651
|
+
| `ToolMessageComponent` | Tool call args + result display |
|
|
652
|
+
| `MessageFooterComponent` | Copy + regenerate buttons |
|
|
653
|
+
| `McpAppResourceComponent` | Mounts MCP App iframe element |
|
|
654
|
+
|
|
655
|
+
### Threads
|
|
656
|
+
|
|
657
|
+
| Component | Purpose |
|
|
658
|
+
|-----------|---------|
|
|
659
|
+
| `ThreadHistoryComponent` | Sidebar panel with thread list |
|
|
660
|
+
| `ThreadHistoryListComponent` | Grouped list with relative-time dividers |
|
|
661
|
+
| `ThreadHistoryListItemComponent` | Individual thread with rename/delete actions |
|
|
662
|
+
|
|
663
|
+
### Tools
|
|
664
|
+
|
|
665
|
+
| Component | Purpose |
|
|
666
|
+
|-----------|---------|
|
|
667
|
+
| `ToolListComponent` | Searchable list of all available tools |
|
|
668
|
+
| `ToolListItemComponent` | Individual tool with enable/disable toggle |
|
|
669
|
+
| `ToolInfoComponent` | Tool metadata tooltip |
|
|
670
|
+
|
|
671
|
+
### Prompts
|
|
672
|
+
|
|
673
|
+
| Component | Purpose |
|
|
674
|
+
|-----------|---------|
|
|
675
|
+
| `PromptListComponent` | Categorized prompt browser with search |
|
|
676
|
+
| `PromptListItemComponent` | Individual prompt with favorite toggle |
|
|
677
|
+
| `FavoritePromptListComponent` | Home screen favorites grid |
|
|
678
|
+
|
|
679
|
+
### Working Context Panel
|
|
680
|
+
|
|
681
|
+
File: `components/working-context-panel/working-context-panel.component.ts`
|
|
682
|
+
|
|
683
|
+
- Info callout explaining what working context is
|
|
684
|
+
- JSON-formatted display of current context values (rendered as markdown)
|
|
685
|
+
|
|
686
|
+
### MCP App Resource
|
|
687
|
+
|
|
688
|
+
File: `components/messages/mcp-app-resource/mcp-app-resource.component.ts`
|
|
689
|
+
|
|
690
|
+
- Receives an `AppInstance` from `McpAppsService`
|
|
691
|
+
- Mounts `app.element` (the sandbox proxy iframe) into the DOM
|
|
692
|
+
- Only renders for inline display mode
|
|
693
|
+
|
|
694
|
+
### Shared UI Primitives
|
|
695
|
+
|
|
696
|
+
| Component | File |
|
|
697
|
+
|-----------|------|
|
|
698
|
+
| `AppIconComponent` | `shared/components/app-icon/` |
|
|
699
|
+
| `AppButtonComponent` | `shared/components/app-button/` |
|
|
700
|
+
| `AppToggleComponent` | `shared/components/app-toggle/` |
|
|
701
|
+
| `AppInputComponent` | `shared/components/app-input/` |
|
|
702
|
+
| `AppTooltipComponent` | `shared/components/app-tooltip/` |
|
|
703
|
+
| `AppPanelComponent` | `shared/components/app-panel/` |
|
|
704
|
+
| `AppSpinnerComponent` | `shared/components/app-spinner/` |
|
|
705
|
+
| `AppMdFormatterComponent` | `shared/components/app-md-formatter/` |
|
|
706
|
+
| `AppCopyToClipboardButtonComponent` | `shared/components/app-copy-to-clipboard-button/` |
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Directives
|
|
711
|
+
|
|
712
|
+
### Animation Effect Directive
|
|
713
|
+
|
|
714
|
+
File: `shared/directives/animation/`
|
|
715
|
+
|
|
716
|
+
Applies CSS animations to host elements with:
|
|
717
|
+
- Configurable animation type and duration
|
|
718
|
+
- Start/complete event emitters
|
|
719
|
+
- Used for message entrance animations
|
|
720
|
+
|
|
721
|
+
### Accent Gradient Border Directive
|
|
722
|
+
|
|
723
|
+
File: `shared/directives/accent-border/`
|
|
724
|
+
|
|
725
|
+
Applies a gradient border effect to elements. Used for visual emphasis on active/streaming states.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## AG-UI Streaming Pipeline
|
|
730
|
+
|
|
731
|
+
File: `store/message/message.effects.ts`
|
|
732
|
+
|
|
733
|
+
The core streaming loop. When `getResponse` or `reloadResponse` is dispatched:
|
|
734
|
+
|
|
735
|
+
1. **Start** — dispatches `startThreadStream(threadId, userMessage)` to track stream state
|
|
736
|
+
2. **Stream** — `AgentService.getResponse()` returns an AG-UI `StreamResponse` observable
|
|
737
|
+
3. **Event processing** — each AG-UI event is mapped to store actions:
|
|
738
|
+
|
|
739
|
+
| AG-UI Event | Store Action |
|
|
740
|
+
|-------------|-------------|
|
|
741
|
+
| `TEXT_MESSAGE_START` | `addAssistantMessage` (creates placeholder) |
|
|
742
|
+
| `TEXT_MESSAGE_CONTENT` | `addAssistantMessage` (updates content) or `updateStreamContent` (background thread) |
|
|
743
|
+
| `TOOL_CALL_START` | `addToolCallMessage` |
|
|
744
|
+
| `TOOL_CALL_ARGS` | Accumulated in local `Map<toolCallId, args>` |
|
|
745
|
+
| `TOOL_CALL_END` | Parses accumulated args JSON → `updateToolCallResult` |
|
|
746
|
+
| `TOOL_CALL_RESULT` | Parses result → `updateToolCallResult` |
|
|
747
|
+
|
|
748
|
+
4. **Complete** — dispatches `completeThreadStream(threadId, shouldNotify)`. Sets `shouldNotify=true` if the user switched to a different thread (background completion).
|
|
749
|
+
5. **Error** — dispatches `failThreadStream` + logs error
|
|
750
|
+
6. **Abort** — dispatches `abortThreadStream`
|
|
751
|
+
|
|
752
|
+
**Background thread handling:** If the user switches threads while a stream is active:
|
|
753
|
+
- Content deltas go to `updateStreamContent` (stored in response stream store, not visible UI)
|
|
754
|
+
- On completion, `shouldNotify=true` triggers a notification dot on the thread history button
|
|
755
|
+
- When the user switches back, the accumulated content is replayed
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Response Stream Store
|
|
760
|
+
|
|
761
|
+
File: `store/response-stream/`
|
|
762
|
+
|
|
763
|
+
Per-thread stream tracking:
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
type ThreadStreamState = {
|
|
767
|
+
threadId: string;
|
|
768
|
+
status: 'idle' | 'streaming' | 'completed' | 'error' | 'aborted';
|
|
769
|
+
accumulatedContent: string;
|
|
770
|
+
currentMessageId: string | null;
|
|
771
|
+
hasCompletionNotification: boolean;
|
|
772
|
+
errorMessage?: string;
|
|
773
|
+
userMessage: UIMessage | null;
|
|
774
|
+
toolMessages: UIToolMessage[];
|
|
775
|
+
};
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
| Action | Behavior |
|
|
779
|
+
|--------|----------|
|
|
780
|
+
| `startThreadStream` | Creates/overwrites stream entry with status `streaming` |
|
|
781
|
+
| `updateStreamContent` | Updates accumulated content (only if `streaming`) |
|
|
782
|
+
| `addStreamToolMessage` | Merges tool message by ID (update existing or append) |
|
|
783
|
+
| `completeThreadStream` | Sets terminal status + notification flag |
|
|
784
|
+
| `failThreadStream` | Sets `error` status + error message |
|
|
785
|
+
| `abortThreadStream` | Sets `aborted` status |
|
|
786
|
+
| `clearCompletionNotification` | Clears notification flag (e.g., when user views the thread) |
|
|
787
|
+
| `untrackThreadStreamState` | Removes stream entry (only if in terminal state) |
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## Sampling & Elicitation Flow
|
|
792
|
+
|
|
793
|
+
### Sampling
|
|
794
|
+
|
|
795
|
+
```
|
|
796
|
+
MCP Server → SamplingRequest → SamplingService.handleSamplingRequest()
|
|
797
|
+
├── Background thread? → SamplingErrorResponse (rejected)
|
|
798
|
+
├── io.Connect modal available? → noInputsConfirmationDialog
|
|
799
|
+
│ ├── "Continue" → AgentService.getSamplingResponse() → SamplingSuccessResponse
|
|
800
|
+
│ └── "Cancel" → SamplingErrorResponse
|
|
801
|
+
└── Fallback → OverlayService.showPanelOverlay()
|
|
802
|
+
├── "Continue" → AgentService.getSamplingResponse() → SamplingSuccessResponse
|
|
803
|
+
└── "Cancel" → SamplingErrorResponse
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
**Stop reason mapping:** Agent `finishReason` is mapped to MCP stop reasons:
|
|
807
|
+
- `'stop'` → `'endTurn'`
|
|
808
|
+
- `'length'` → `'maxTokens'`
|
|
809
|
+
- other → `'endTurn'` (default)
|
|
810
|
+
|
|
811
|
+
### Elicitation
|
|
812
|
+
|
|
813
|
+
```
|
|
814
|
+
MCP Server → ElicitationRequest → ElicitationService.handleElicitationRequest()
|
|
815
|
+
├── Invalid _meta.toolName? → ElicitationRejectionResponse
|
|
816
|
+
├── Background thread? → ElicitationCancelResponse
|
|
817
|
+
├── io.Connect modal available? → Dialog with Accept/Decline
|
|
818
|
+
│ ├── "Accept" → ElicitationConfirmationResponse { action: 'accept', content: {} }
|
|
819
|
+
│ ├── "Decline" → ElicitationRejectionResponse { action: 'decline' }
|
|
820
|
+
│ └── Close → ElicitationCancelResponse { action: 'cancel' }
|
|
821
|
+
└── Fallback → OverlayService panel
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
## MCP Apps Integration
|
|
827
|
+
|
|
828
|
+
### How it works
|
|
829
|
+
|
|
830
|
+
1. **Config** — consumer provides `mcp.mcpApps` (sandboxProxyUrl + displayMode) and `extensions['io.modelcontextprotocol/ui']` in capabilities
|
|
831
|
+
2. **Init** — `IOIntelWebService.initialize()` passes config to ai-web which starts the MCP Apps controller
|
|
832
|
+
3. **Attach** — `McpAppsService.attach(api)` subscribes to app lifecycle events
|
|
833
|
+
4. **Stream interception** — during AG-UI streaming, ai-web's stream interceptor watches for tools with `_meta.ui.resourceUri` and auto-creates app instances
|
|
834
|
+
5. **Rendering** — `McpAppResourceComponent` mounts the `AppInstance.element` (sandbox proxy iframe) for inline mode; workspace mode opens an io.Connect workspace window
|
|
835
|
+
6. **Communication** — apps use `postMessage` (inline) or io.Connect interop (workspace) to exchange data with the host
|
|
836
|
+
7. **Thread lifecycle** — on thread switch, `McpAppsService` closes all apps and recreates pending ones for the new thread
|
|
837
|
+
|
|
838
|
+
### Duplicate handling
|
|
839
|
+
|
|
840
|
+
When a tool is called while an instance already exists (workspace mode), `McpAppsService` shows a 3-option dialog:
|
|
841
|
+
- Uses io.Connect modal if available
|
|
842
|
+
- Falls back to overlay panel
|
|
843
|
+
- Options: Replace Oldest / Replace All / New Instance
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## Styling
|
|
848
|
+
|
|
849
|
+
- **Tailwind CSS** — main styling approach, built from `src/styles.css` via PostCSS
|
|
850
|
+
- **Exported stylesheet** — `@interopio/io-assist-ng/styles.css` must be imported by the consumer
|
|
851
|
+
- **Prism.js** — injected at runtime for code syntax highlighting
|
|
852
|
+
- **Inter font** — injected at runtime from Google Fonts CDN
|
|
853
|
+
- **Theme sync** — CSS variables adapt to io.Connect dark/light theme via `IOConnectService`
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
## Testing
|
|
858
|
+
|
|
859
|
+
- Test files are colocated with source (`*.spec.ts` alongside `*.ts`)
|
|
860
|
+
- Framework: Karma + Jasmine
|
|
861
|
+
- Run: `npm run test:disable` (currently disabled in CI)
|
|
862
|
+
- Coverage:
|
|
863
|
+
- Most component/service specs are smoke tests (`should create`)
|
|
864
|
+
- `icon.utils.spec.ts` has extensive behavior/security tests for SVG sanitization
|
|
865
|
+
- Store reducers/selectors/actions have basic coverage
|