@tylertech/forge-ai 0.15.2 → 0.15.3-dev.1
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/custom-elements.json +4501 -1781
- package/dist/ai-artifact-card/ai-artifact-card.d.ts +75 -0
- package/dist/ai-artifact-card/ai-artifact-card.mjs +142 -0
- package/dist/ai-artifact-card/ai-artifact-card.scss.mjs +283 -0
- package/dist/ai-artifact-card/index.d.ts +1 -0
- package/dist/ai-artifact-card/index.mjs +5 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.d.ts +57 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.mjs +137 -0
- package/dist/ai-artifact-card-group/ai-artifact-card-group.scss.mjs +173 -0
- package/dist/ai-artifact-card-group/index.d.ts +1 -0
- package/dist/ai-artifact-card-group/index.mjs +5 -0
- package/dist/ai-assistant-response/ai-assistant-response.d.ts +2 -2
- package/dist/ai-assistant-response/ai-assistant-response.mjs +26 -2
- package/dist/ai-canvas/ai-canvas.d.ts +43 -0
- package/dist/ai-canvas/ai-canvas.mjs +95 -0
- package/dist/ai-canvas/ai-canvas.scss.mjs +4 -0
- package/dist/ai-canvas/index.d.ts +1 -0
- package/dist/ai-canvas/index.mjs +5 -0
- package/dist/ai-chatbot/ag-ui-adapter.d.ts +2 -2
- package/dist/ai-chatbot/ag-ui-adapter.mjs +1 -1
- package/dist/ai-chatbot/agent-adapter.d.ts +111 -4
- package/dist/ai-chatbot/agent-adapter.mjs +22 -0
- package/dist/ai-chatbot/agent-runner.d.ts +2 -2
- package/dist/ai-chatbot/ai-chatbot-base.d.ts +29 -2
- package/dist/ai-chatbot/ai-chatbot-base.mjs +147 -3
- package/dist/ai-chatbot/ai-chatbot-tool-call.d.ts +2 -2
- package/dist/ai-chatbot/ai-chatbot-tool-call.mjs +88 -10
- package/dist/ai-chatbot/ai-chatbot.d.ts +24 -1
- package/dist/ai-chatbot/ai-chatbot.mjs +102 -2
- package/dist/ai-chatbot/ai-chatbot.scss.mjs +1 -1
- package/dist/ai-chatbot/create-tool-renderer.d.ts +5 -5
- package/dist/ai-chatbot/index.d.ts +3 -2
- package/dist/ai-chatbot/index.mjs +3 -0
- package/dist/ai-chatbot/message-state-controller.d.ts +10 -1
- package/dist/ai-chatbot/message-state-controller.mjs +78 -20
- package/dist/ai-chatbot/tool-canvas.d.ts +60 -0
- package/dist/ai-chatbot/tool-canvas.mjs +128 -0
- package/dist/ai-chatbot/types.d.ts +325 -15
- package/dist/ai-chatbot/utils.d.ts +8 -3
- package/dist/ai-chatbot/utils.mjs +7 -1
- package/dist/ai-chatbot-launcher/ai-chatbot-launcher.d.ts +40 -2
- package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +224 -7
- package/dist/ai-chatbot-launcher/ai-chatbot-launcher.scss.mjs +1 -1
- package/dist/ai-embedded-chat/ai-embedded-chat.mjs +1 -0
- package/dist/ai-message-thread/ai-message-thread.d.ts +3 -2
- package/dist/ai-message-thread/ai-message-thread.mjs +36 -3
- package/dist/ai-steps/ai-steps.d.ts +67 -0
- package/dist/ai-steps/ai-steps.mjs +163 -0
- package/dist/ai-steps/ai-steps.scss.mjs +4 -0
- package/dist/ai-steps/index.d.ts +1 -0
- package/dist/ai-steps/index.mjs +5 -0
- package/dist/ai-tool-call-indicator/ai-tool-call-indicator.d.ts +2 -2
- package/dist/ai-tool-call-indicator/ai-tool-call-indicator.mjs +1 -1
- package/dist/core/canvas-controller.d.ts +81 -0
- package/dist/core/canvas-controller.mjs +221 -0
- package/dist/core/chatbot-core-controller.d.ts +4 -2
- package/dist/core/chatbot-core-controller.mjs +121 -4
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.mjs +2 -0
- package/dist/core/resize-controller.d.ts +5 -0
- package/dist/core/resize-controller.mjs +7 -5
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +15 -0
- package/dist/tools/ai-data-table/ai-data-table-definition.mjs +1 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +70 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +589 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.scss.mjs +4 -0
- package/dist/tools/ai-mcp-app/index.d.ts +3 -0
- package/dist/tools/ai-mcp-app/index.mjs +6 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.d.ts +60 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +147 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +124 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge.d.ts +41 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge.mjs +293 -0
- package/dist/tools/ai-mcp-app/mcp-app-context.d.ts +46 -0
- package/dist/tools/ai-mcp-app/mcp-app-context.mjs +5 -0
- package/dist/tools/ai-steps/ai-steps-definition.d.ts +2 -0
- package/dist/tools/ai-steps/ai-steps-definition.mjs +51 -0
- package/dist/tools/ai-steps/ai-steps.d.ts +28 -0
- package/dist/tools/ai-steps/ai-steps.mjs +49 -0
- package/dist/tools/ai-steps/ai-steps.scss.mjs +4 -0
- package/dist/tools/ai-steps/index.d.ts +2 -0
- package/dist/tools/ai-steps/index.mjs +6 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.mjs +5 -1
- package/dist/utils/inert-outside.d.ts +7 -0
- package/dist/utils/inert-outside.mjs +28 -0
- package/package.json +4 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatMessage, FileUploadCallbacks,
|
|
1
|
+
import { ChatMessage, FileUploadCallbacks, McpAppUiResource, McpToolResult, AnyToolDefinition } from './types.js';
|
|
2
2
|
import { EventEmitter, Subscription } from './event-emitter.js';
|
|
3
3
|
export interface MessageStartEvent {
|
|
4
4
|
messageId: string;
|
|
@@ -44,11 +44,70 @@ export interface ToolCallEndEvent {
|
|
|
44
44
|
}
|
|
45
45
|
export interface ToolResultEvent {
|
|
46
46
|
toolCallId: string;
|
|
47
|
+
/** Model-facing result. Never delivered to an MCP-app View. */
|
|
47
48
|
result: unknown;
|
|
48
49
|
message: ChatMessage;
|
|
50
|
+
/** Companion structured payload delivered to an MCP-app widget, stripped from LLM context. */
|
|
51
|
+
structuredContent?: unknown;
|
|
52
|
+
/** `true` when the backend dropped `structuredContent` (size caps). */
|
|
53
|
+
structuredContentOmitted?: boolean;
|
|
54
|
+
/** Raw MCP result for an MCP-app tool call; the View's `tool-result` is built from it. */
|
|
55
|
+
mcpResult?: McpToolResult;
|
|
56
|
+
/** `true` when the backend dropped `mcpResult` (size caps). */
|
|
57
|
+
mcpResultOmitted?: boolean;
|
|
49
58
|
isError?: boolean;
|
|
59
|
+
/** `true` when the tool call was cancelled rather than failed; takes precedence over `isError`. */
|
|
60
|
+
cancelled?: boolean;
|
|
50
61
|
rawEvent?: unknown;
|
|
51
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Emitted when the backend signals that a tool call is backed by an MCP-app UI
|
|
65
|
+
* resource. Carries only the widget binding — the HTML and the `csp`/`permissions`
|
|
66
|
+
* policy come from {@link AgentAdapter.resolveMcpAppResource}. The core controller
|
|
67
|
+
* resolves the resource and stamps a {@link McpAppUiResource} onto the tool call.
|
|
68
|
+
*
|
|
69
|
+
* May be emitted before or after the matching `tool-call` chunk, and also after
|
|
70
|
+
* `setMessages`/`setThreadState` to remount widgets restored from history.
|
|
71
|
+
*/
|
|
72
|
+
export interface McpUiResourceEvent {
|
|
73
|
+
toolCallId: string;
|
|
74
|
+
toolName?: string;
|
|
75
|
+
/** The concrete resolved `ui://` URI (interpolation done server-side). */
|
|
76
|
+
resourceUri: string;
|
|
77
|
+
serverId?: string;
|
|
78
|
+
mimeType?: string;
|
|
79
|
+
rawEvent?: unknown;
|
|
80
|
+
}
|
|
81
|
+
/** Parameters for {@link AgentAdapter.resolveMcpAppResource}. */
|
|
82
|
+
export interface McpAppResourceResolveParams {
|
|
83
|
+
/** The concrete resolved `ui://` URI from the `mcp-ui-resource` event. */
|
|
84
|
+
resourceUri: string;
|
|
85
|
+
/** The widget's tool call id; template reads are bound to it. */
|
|
86
|
+
toolCallId: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parameters an MCP-app widget passes when forwarding a `tools/call` to the server.
|
|
90
|
+
* The library never owns a `Client`; these delegate to the adapter.
|
|
91
|
+
*/
|
|
92
|
+
export interface McpToolCallParams {
|
|
93
|
+
name: string;
|
|
94
|
+
arguments?: Record<string, unknown>;
|
|
95
|
+
/** The calling widget's tool call id; the backend derives the MCP server from it. */
|
|
96
|
+
toolCallId: string;
|
|
97
|
+
}
|
|
98
|
+
/** Parameters for an MCP-app widget-initiated `resources/read`. */
|
|
99
|
+
export interface McpResourceReadParams {
|
|
100
|
+
uri: string;
|
|
101
|
+
/** The calling widget's tool call id. */
|
|
102
|
+
toolCallId: string;
|
|
103
|
+
}
|
|
104
|
+
/** Payload of a widget's `ui/update-model-context`, forwarded to the adapter. */
|
|
105
|
+
export interface McpModelContextUpdateParams {
|
|
106
|
+
/** The widget's tool call id. */
|
|
107
|
+
toolCallId: string;
|
|
108
|
+
content?: unknown[];
|
|
109
|
+
structuredContent?: unknown;
|
|
110
|
+
}
|
|
52
111
|
export interface AdapterState {
|
|
53
112
|
isConnected: boolean;
|
|
54
113
|
isConnecting: boolean;
|
|
@@ -108,7 +167,7 @@ export interface ActivityDeltaAgentEvent {
|
|
|
108
167
|
}
|
|
109
168
|
export declare abstract class AgentAdapter {
|
|
110
169
|
protected _state: AdapterState;
|
|
111
|
-
protected _tools:
|
|
170
|
+
protected _tools: AnyToolDefinition[];
|
|
112
171
|
protected _events: {
|
|
113
172
|
runStarted: EventEmitter<void>;
|
|
114
173
|
runFinished: EventEmitter<void>;
|
|
@@ -121,6 +180,7 @@ export declare abstract class AgentAdapter {
|
|
|
121
180
|
toolCallArgs: EventEmitter<ToolCallArgsEvent>;
|
|
122
181
|
toolCallEnd: EventEmitter<ToolCallEndEvent>;
|
|
123
182
|
toolResult: EventEmitter<ToolResultEvent>;
|
|
183
|
+
mcpUiResource: EventEmitter<McpUiResourceEvent>;
|
|
124
184
|
fileUpload: EventEmitter<FileUploadEvent>;
|
|
125
185
|
fileRemove: EventEmitter<FileRemoveEvent>;
|
|
126
186
|
error: EventEmitter<ErrorEvent>;
|
|
@@ -143,8 +203,46 @@ export declare abstract class AgentAdapter {
|
|
|
143
203
|
abstract get threadId(): string;
|
|
144
204
|
abstract set threadId(value: string);
|
|
145
205
|
clearMemory?(): Promise<void>;
|
|
146
|
-
|
|
147
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Resolve the MCP-app UI resource for an already-resolved `resourceUri`, bound to the
|
|
208
|
+
* widget's `toolCallId`. The adapter fetches the template for that concrete URI and
|
|
209
|
+
* fills `csp`/`permissions` from the template response's `_meta.ui` (`{}` when absent).
|
|
210
|
+
* It MUST NOT re-derive the URI (templated-URI interpolation is server-side only).
|
|
211
|
+
*/
|
|
212
|
+
resolveMcpAppResource?(params: McpAppResourceResolveParams): Promise<McpAppUiResource | undefined>;
|
|
213
|
+
/**
|
|
214
|
+
* Forward a widget-initiated `tools/call` to the MCP server. Resolve with the View's
|
|
215
|
+
* `CallToolResult` (tool-level errors resolve with `isError: true`); reject for
|
|
216
|
+
* transport/HTTP errors (e.g. a 403 for a tool without `"app"` visibility) — the
|
|
217
|
+
* rejection is returned to the View as a JSON-RPC error.
|
|
218
|
+
*/
|
|
219
|
+
callMcpTool?(params: McpToolCallParams): Promise<unknown>;
|
|
220
|
+
/**
|
|
221
|
+
* Forward a widget-initiated `resources/read` to the MCP server. When not implemented
|
|
222
|
+
* the View's request is rejected with a JSON-RPC error.
|
|
223
|
+
*/
|
|
224
|
+
readMcpResource?(params: McpResourceReadParams): Promise<unknown>;
|
|
225
|
+
/**
|
|
226
|
+
* Called when a widget sends `ui/update-model-context`. Each call replaces the previous
|
|
227
|
+
* context for that `toolCallId`; adapters should debounce and may hold the latest value
|
|
228
|
+
* until the run ends.
|
|
229
|
+
*/
|
|
230
|
+
updateMcpModelContext?(params: McpModelContextUpdateParams): Promise<void>;
|
|
231
|
+
/**
|
|
232
|
+
* Called after a widget is torn down (its turn left the visible thread, or the thread was
|
|
233
|
+
* cleared/replaced). Drop any state held for that `toolCallId`, e.g. a pending model context.
|
|
234
|
+
*/
|
|
235
|
+
disposeMcpAppView?(params: {
|
|
236
|
+
toolCallId: string;
|
|
237
|
+
}): void;
|
|
238
|
+
setTools(tools: AnyToolDefinition[]): void;
|
|
239
|
+
getTools(): AnyToolDefinition[];
|
|
240
|
+
/**
|
|
241
|
+
* Tools to forward to the backend agent, excluding MCP-app tools (`ToolDefinition.mcpApp`).
|
|
242
|
+
* MCP-app tools are a host-internal mechanism for streaming a UI resource into an existing
|
|
243
|
+
* tool call — the model never calls them directly, so they must never reach the agent.
|
|
244
|
+
*/
|
|
245
|
+
getModelFacingTools(): AnyToolDefinition[];
|
|
148
246
|
getState(): AdapterState;
|
|
149
247
|
get isConnected(): boolean;
|
|
150
248
|
get isConnecting(): boolean;
|
|
@@ -160,6 +258,7 @@ export declare abstract class AgentAdapter {
|
|
|
160
258
|
onToolCallArgs(callback: (event: ToolCallArgsEvent) => void): Subscription;
|
|
161
259
|
onToolCallEnd(callback: (event: ToolCallEndEvent) => void): Subscription;
|
|
162
260
|
onToolCallResult(callback: (event: ToolResultEvent) => void): Subscription;
|
|
261
|
+
onMcpUiResource(callback: (event: McpUiResourceEvent) => void): Subscription;
|
|
163
262
|
onFileUpload(callback: (event: FileUploadEvent) => void): Subscription;
|
|
164
263
|
onFileRemove(callback: (event: FileRemoveEvent) => void): Subscription;
|
|
165
264
|
onError(callback: (event: ErrorEvent) => void): Subscription;
|
|
@@ -184,6 +283,14 @@ export declare abstract class AgentAdapter {
|
|
|
184
283
|
protected _emitToolCallArgs(event: ToolCallArgsEvent, rawEvent?: unknown): void;
|
|
185
284
|
protected _emitToolCallEnd(event: ToolCallEndEvent, rawEvent?: unknown): void;
|
|
186
285
|
protected _emitToolResult(event: ToolResultEvent, rawEvent?: unknown): void;
|
|
286
|
+
/**
|
|
287
|
+
* Announce that a tool call renders as an MCP-app widget. Also the way to remount
|
|
288
|
+
* widgets restored from history: call it per widget after `setMessages`/`setThreadState`
|
|
289
|
+
* — the resource is resolved and stamped onto the restored (already finalized) tool
|
|
290
|
+
* call. If the tool call isn't in state yet, the resource is held and applied when it
|
|
291
|
+
* arrives.
|
|
292
|
+
*/
|
|
293
|
+
protected _emitMcpUiResource(event: McpUiResourceEvent, rawEvent?: unknown): void;
|
|
187
294
|
emitFileUpload(file: File, callbacks: FileUploadCallbacks): void;
|
|
188
295
|
emitFileRemove(fileId: string, callbacks: {
|
|
189
296
|
onSuccess: () => void;
|
|
@@ -15,6 +15,7 @@ class AgentAdapter {
|
|
|
15
15
|
toolCallArgs: new EventEmitter(),
|
|
16
16
|
toolCallEnd: new EventEmitter(),
|
|
17
17
|
toolResult: new EventEmitter(),
|
|
18
|
+
mcpUiResource: new EventEmitter(),
|
|
18
19
|
fileUpload: new EventEmitter(),
|
|
19
20
|
fileRemove: new EventEmitter(),
|
|
20
21
|
error: new EventEmitter(),
|
|
@@ -36,6 +37,14 @@ class AgentAdapter {
|
|
|
36
37
|
getTools() {
|
|
37
38
|
return [...this._tools];
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Tools to forward to the backend agent, excluding MCP-app tools (`ToolDefinition.mcpApp`).
|
|
42
|
+
* MCP-app tools are a host-internal mechanism for streaming a UI resource into an existing
|
|
43
|
+
* tool call — the model never calls them directly, so they must never reach the agent.
|
|
44
|
+
*/
|
|
45
|
+
getModelFacingTools() {
|
|
46
|
+
return this._tools.filter((tool) => tool.mcpApp === void 0);
|
|
47
|
+
}
|
|
39
48
|
getState() {
|
|
40
49
|
return { ...this._state };
|
|
41
50
|
}
|
|
@@ -81,6 +90,9 @@ class AgentAdapter {
|
|
|
81
90
|
onToolCallResult(callback) {
|
|
82
91
|
return this._events.toolResult.subscribe(callback);
|
|
83
92
|
}
|
|
93
|
+
onMcpUiResource(callback) {
|
|
94
|
+
return this._events.mcpUiResource.subscribe(callback);
|
|
95
|
+
}
|
|
84
96
|
onFileUpload(callback) {
|
|
85
97
|
return this._events.fileUpload.subscribe(callback);
|
|
86
98
|
}
|
|
@@ -153,6 +165,16 @@ class AgentAdapter {
|
|
|
153
165
|
_emitToolResult(event, rawEvent) {
|
|
154
166
|
this._events.toolResult.emit({ ...event, rawEvent });
|
|
155
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Announce that a tool call renders as an MCP-app widget. Also the way to remount
|
|
170
|
+
* widgets restored from history: call it per widget after `setMessages`/`setThreadState`
|
|
171
|
+
* — the resource is resolved and stamped onto the restored (already finalized) tool
|
|
172
|
+
* call. If the tool call isn't in state yet, the resource is held and applied when it
|
|
173
|
+
* arrives.
|
|
174
|
+
*/
|
|
175
|
+
_emitMcpUiResource(event, rawEvent) {
|
|
176
|
+
this._events.mcpUiResource.emit({ ...event, rawEvent });
|
|
177
|
+
}
|
|
156
178
|
emitFileUpload(file, callbacks) {
|
|
157
179
|
this._events.fileUpload.emit({ file, ...callbacks });
|
|
158
180
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { AgentAdapter } from './agent-adapter.js';
|
|
2
|
-
import { ChatMessage, ToolCall,
|
|
2
|
+
import { ChatMessage, ToolCall, AnyToolDefinition } from './types.js';
|
|
3
3
|
export interface AgentRunnerConfig {
|
|
4
4
|
/** The adapter instance that handles communication with the AI model */
|
|
5
5
|
adapter: AgentAdapter;
|
|
6
6
|
/** The user prompt to send to the agent */
|
|
7
7
|
prompt: string;
|
|
8
8
|
/** Optional tools the agent can call during execution */
|
|
9
|
-
tools?:
|
|
9
|
+
tools?: AnyToolDefinition[];
|
|
10
10
|
/** Execution mode: 'one-shot' stops after first response, 'multi-turn' allows tool usage loops. @default 'one-shot' */
|
|
11
11
|
mode?: 'one-shot' | 'multi-turn';
|
|
12
12
|
/** Maximum conversation turns in multi-turn mode to prevent infinite loops. @default 20 */
|
|
@@ -10,9 +10,10 @@ import { ForgeAiSuggestionsEventData, Suggestion } from '../ai-suggestions';
|
|
|
10
10
|
import { ForgeAiVoiceInputResultEvent } from '../ai-voice-input';
|
|
11
11
|
import { AgentAdapter } from './agent-adapter.js';
|
|
12
12
|
import { ChatbotCoreController } from '../core/chatbot-core-controller.js';
|
|
13
|
-
import { Agent, ChatMessage, ClientMessageInput, ContextItem, HeadingLevel, MessageItem, SlashCommand, ThreadState,
|
|
13
|
+
import { Agent, ChatMessage, ClientMessageInput, ContextItem, HeadingLevel, MessageItem, SlashCommand, ThreadState, ToolCall, AnyToolDefinition } from './types.js';
|
|
14
14
|
export type FeatureToggle = 'on' | 'off';
|
|
15
15
|
export declare abstract class AiChatbotBase extends LitElement {
|
|
16
|
+
#private;
|
|
16
17
|
protected abstract _messageThreadRef: Ref<AiMessageThreadComponent>;
|
|
17
18
|
protected abstract _promptRef: Ref<AiPromptComponent>;
|
|
18
19
|
adapter?: AgentAdapter;
|
|
@@ -34,14 +35,34 @@ export declare abstract class AiChatbotBase extends LitElement {
|
|
|
34
35
|
agents: Agent[];
|
|
35
36
|
selectedAgentId?: string;
|
|
36
37
|
contextItems: ContextItem[];
|
|
38
|
+
mcpAppSandboxUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Theme fed into an MCP-app widget's `hostContext.theme`. The chatbot's own appearance
|
|
41
|
+
* comes from Forge tokens and is unaffected. Set this whenever the app switches between
|
|
42
|
+
* light and dark: it is the signal that re-sends `theme` and the Forge-derived
|
|
43
|
+
* `styles.variables` to widgets, since token changes alone can't be detected. Absent →
|
|
44
|
+
* follows `prefers-color-scheme`.
|
|
45
|
+
*/
|
|
46
|
+
mcpAppTheme?: 'light' | 'dark';
|
|
47
|
+
private _mcpAppHost;
|
|
48
|
+
/**
|
|
49
|
+
* Sends `ui/resource-teardown` to mounted MCP-app widgets (all, or only `toolCallIds`) and
|
|
50
|
+
* releases them immediately, without waiting for acks. The adapter then drops any state it
|
|
51
|
+
* holds for them (e.g. a pending model context).
|
|
52
|
+
*/
|
|
53
|
+
protected _teardownMcpAppViews({ toolCallIds }?: {
|
|
54
|
+
toolCallIds?: string[];
|
|
55
|
+
}): void;
|
|
37
56
|
protected _coreController: ChatbotCoreController;
|
|
38
57
|
protected get _isStreaming(): boolean;
|
|
39
58
|
protected get _isUploading(): boolean;
|
|
40
|
-
protected get _tools(): Map<string,
|
|
59
|
+
protected get _tools(): Map<string, AnyToolDefinition>;
|
|
41
60
|
protected get _messageItems(): MessageItem[];
|
|
61
|
+
protected get _toolCalls(): ToolCall[];
|
|
42
62
|
protected get _hasMessages(): boolean;
|
|
43
63
|
protected get _allContextItems(): ContextItem[];
|
|
44
64
|
connectedCallback(): void;
|
|
65
|
+
disconnectedCallback(): void;
|
|
45
66
|
willUpdate(changedProperties: PropertyValues<this>): void;
|
|
46
67
|
protected _onConnected(): void;
|
|
47
68
|
protected get _slashCommands(): SlashCommand[];
|
|
@@ -136,6 +157,12 @@ export declare abstract class AiChatbotBase extends LitElement {
|
|
|
136
157
|
removeClientMessage(id: string): void;
|
|
137
158
|
sendMessage(content: string, files?: File[]): Promise<void>;
|
|
138
159
|
abort(): void;
|
|
160
|
+
/**
|
|
161
|
+
* Re-sends the Forge-derived `styles.variables` to every MCP-app widget. Call after
|
|
162
|
+
* changing Forge tokens without a light/dark switch (e.g. a brand change); for light/dark,
|
|
163
|
+
* set {@link mcpAppTheme} instead.
|
|
164
|
+
*/
|
|
165
|
+
refreshMcpAppStyles(): void;
|
|
139
166
|
scrollToBottom({ behavior }?: {
|
|
140
167
|
behavior?: ScrollBehavior;
|
|
141
168
|
}): Promise<void>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { provide } from "@lit/context";
|
|
1
2
|
import { LitElement } from "lit";
|
|
2
3
|
import { property } from "lit/decorators.js";
|
|
4
|
+
import { mcpAppHostContext } from "../tools/ai-mcp-app/mcp-app-context.mjs";
|
|
3
5
|
import { ChatbotCoreController } from "../core/chatbot-core-controller.mjs";
|
|
4
6
|
import { fileAttachmentsToContextItems, generateId, formatChatTranscript, downloadFile, createChatExportFilename } from "./utils.mjs";
|
|
5
7
|
var __defProp = Object.defineProperty;
|
|
@@ -11,6 +13,34 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
11
13
|
if (result) __defProp(target, key, result);
|
|
12
14
|
return result;
|
|
13
15
|
};
|
|
16
|
+
function mcpAppHostCapabilities(adapter) {
|
|
17
|
+
return {
|
|
18
|
+
openLinks: {},
|
|
19
|
+
logging: {},
|
|
20
|
+
message: { text: {} },
|
|
21
|
+
...typeof adapter?.callMcpTool === "function" ? { serverTools: {} } : {},
|
|
22
|
+
...typeof adapter?.updateMcpModelContext === "function" ? { updateModelContext: { text: {}, structuredContent: {} } } : {}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function mcpAppEnvironment() {
|
|
26
|
+
return {
|
|
27
|
+
availableDisplayModes: ["inline", "fullscreen", "pip"],
|
|
28
|
+
locale: navigator.language,
|
|
29
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
30
|
+
userAgent: navigator.userAgent,
|
|
31
|
+
platform: "web",
|
|
32
|
+
deviceCapabilities: {
|
|
33
|
+
touch: window.matchMedia("(pointer: coarse)").matches || navigator.maxTouchPoints > 0,
|
|
34
|
+
hover: window.matchMedia("(hover: hover)").matches
|
|
35
|
+
},
|
|
36
|
+
safeAreaInsets: { top: 0, right: 0, bottom: 0, left: 0 }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function mcpMessageText(content) {
|
|
40
|
+
return content.filter(
|
|
41
|
+
(block) => !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string"
|
|
42
|
+
).map((block) => block.text).join("\n\n");
|
|
43
|
+
}
|
|
14
44
|
class AiChatbotBase extends LitElement {
|
|
15
45
|
constructor() {
|
|
16
46
|
super(...arguments);
|
|
@@ -29,6 +59,74 @@ class AiChatbotBase extends LitElement {
|
|
|
29
59
|
this.clearOption = "on";
|
|
30
60
|
this.agents = [];
|
|
31
61
|
this.contextItems = [];
|
|
62
|
+
this.#themeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
63
|
+
this.#mcpAppViews = /* @__PURE__ */ new Map();
|
|
64
|
+
this._mcpAppHost = {
|
|
65
|
+
callTool: (params) => this._coreController?.adapter?.callMcpTool?.(params) ?? Promise.reject(new Error("tools/call is not supported by this host")),
|
|
66
|
+
readResource: (params) => this._coreController?.adapter?.readMcpResource?.(params) ?? Promise.reject(new Error("resources/read is not supported by this host")),
|
|
67
|
+
updateModelContext: (params) => this._coreController?.adapter?.updateMcpModelContext?.(params) ?? Promise.resolve(),
|
|
68
|
+
draftMessage: ({ content }) => this.#draftMcpAppMessage(content),
|
|
69
|
+
teardownView: ({ toolCallId }) => this._teardownMcpAppViews({ toolCallIds: [toolCallId] }),
|
|
70
|
+
registerView: (view) => {
|
|
71
|
+
this.#mcpAppViews.set(view.toolCallId, view);
|
|
72
|
+
return () => {
|
|
73
|
+
if (this.#mcpAppViews.get(view.toolCallId) === view) {
|
|
74
|
+
this.#mcpAppViews.delete(view.toolCallId);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
hostContext: { theme: this.#effectiveTheme, ...mcpAppEnvironment() },
|
|
79
|
+
sandboxUrl: void 0,
|
|
80
|
+
hostCapabilities: mcpAppHostCapabilities(void 0)
|
|
81
|
+
};
|
|
82
|
+
this.#handleThemeChange = () => {
|
|
83
|
+
this.#refreshMcpAppHostContext();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
#themeMediaQuery;
|
|
87
|
+
get #effectiveTheme() {
|
|
88
|
+
return this.mcpAppTheme ?? (this.#themeMediaQuery.matches ? "dark" : "light");
|
|
89
|
+
}
|
|
90
|
+
#mcpAppViews;
|
|
91
|
+
/**
|
|
92
|
+
* Places a widget's `ui/message` text in the prompt for the user to review and send. A
|
|
93
|
+
* widget never starts a model turn on its own. Refused when the message has no text.
|
|
94
|
+
*/
|
|
95
|
+
async #draftMcpAppMessage(content) {
|
|
96
|
+
const text = mcpMessageText(content);
|
|
97
|
+
const prompt = this._promptRef.value;
|
|
98
|
+
if (!text.trim() || !prompt) {
|
|
99
|
+
return { isError: true };
|
|
100
|
+
}
|
|
101
|
+
prompt.value = text;
|
|
102
|
+
prompt.focus();
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Sends `ui/resource-teardown` to mounted MCP-app widgets (all, or only `toolCallIds`) and
|
|
107
|
+
* releases them immediately, without waiting for acks. The adapter then drops any state it
|
|
108
|
+
* holds for them (e.g. a pending model context).
|
|
109
|
+
*/
|
|
110
|
+
_teardownMcpAppViews({ toolCallIds } = {}) {
|
|
111
|
+
const views = [...this.#mcpAppViews.values()].filter((view) => toolCallIds?.includes(view.toolCallId) ?? true);
|
|
112
|
+
for (const view of views) {
|
|
113
|
+
this.#mcpAppViews.delete(view.toolCallId);
|
|
114
|
+
view.teardown();
|
|
115
|
+
this._coreController?.adapter?.disposeMcpAppView?.({ toolCallId: view.toolCallId });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Tears down widgets rendered at or after `index`, then removes those items. */
|
|
119
|
+
#removeMessageItemsFrom(index) {
|
|
120
|
+
this._teardownMcpAppViews({ toolCallIds: this._coreController.getToolCallIdsFrom(index) });
|
|
121
|
+
this._coreController.removeMessageItemsFrom(index);
|
|
122
|
+
}
|
|
123
|
+
#handleThemeChange;
|
|
124
|
+
/** A new host context object makes every widget re-read its Forge styles and notify its View. */
|
|
125
|
+
#refreshMcpAppHostContext() {
|
|
126
|
+
this._mcpAppHost = {
|
|
127
|
+
...this._mcpAppHost,
|
|
128
|
+
hostContext: { ...this._mcpAppHost.hostContext, theme: this.#effectiveTheme }
|
|
129
|
+
};
|
|
32
130
|
}
|
|
33
131
|
get _isStreaming() {
|
|
34
132
|
return this._coreController?.isStreaming ?? false;
|
|
@@ -42,6 +140,17 @@ class AiChatbotBase extends LitElement {
|
|
|
42
140
|
get _messageItems() {
|
|
43
141
|
return this._coreController?.messageItems ?? [];
|
|
44
142
|
}
|
|
143
|
+
get _toolCalls() {
|
|
144
|
+
return this._messageItems.flatMap((item) => {
|
|
145
|
+
if (item.type === "toolCall") {
|
|
146
|
+
return [item.data];
|
|
147
|
+
}
|
|
148
|
+
if (item.type === "assistant") {
|
|
149
|
+
return item.data.children.flatMap((child) => child.type === "toolCall" ? [child.data] : []);
|
|
150
|
+
}
|
|
151
|
+
return [];
|
|
152
|
+
});
|
|
153
|
+
}
|
|
45
154
|
get _hasMessages() {
|
|
46
155
|
return this._coreController?.hasMessages ?? false;
|
|
47
156
|
}
|
|
@@ -66,12 +175,26 @@ class AiChatbotBase extends LitElement {
|
|
|
66
175
|
if (this.adapter) {
|
|
67
176
|
this._coreController.adapter = this.adapter;
|
|
68
177
|
}
|
|
178
|
+
this.#themeMediaQuery.addEventListener("change", this.#handleThemeChange);
|
|
69
179
|
this._onConnected();
|
|
70
180
|
}
|
|
181
|
+
disconnectedCallback() {
|
|
182
|
+
super.disconnectedCallback();
|
|
183
|
+
this.#themeMediaQuery.removeEventListener("change", this.#handleThemeChange);
|
|
184
|
+
}
|
|
71
185
|
willUpdate(changedProperties) {
|
|
72
186
|
if (changedProperties.has("adapter") && this._coreController) {
|
|
73
187
|
this._coreController.adapter = this.adapter;
|
|
74
188
|
}
|
|
189
|
+
if (changedProperties.has("adapter")) {
|
|
190
|
+
this._mcpAppHost = { ...this._mcpAppHost, hostCapabilities: mcpAppHostCapabilities(this.adapter) };
|
|
191
|
+
}
|
|
192
|
+
if (changedProperties.has("mcpAppSandboxUrl")) {
|
|
193
|
+
this._mcpAppHost = { ...this._mcpAppHost, sandboxUrl: this.mcpAppSandboxUrl };
|
|
194
|
+
}
|
|
195
|
+
if (changedProperties.has("mcpAppTheme")) {
|
|
196
|
+
this.#refreshMcpAppHostContext();
|
|
197
|
+
}
|
|
75
198
|
}
|
|
76
199
|
_onConnected() {
|
|
77
200
|
}
|
|
@@ -239,7 +362,7 @@ class AiChatbotBase extends LitElement {
|
|
|
239
362
|
}
|
|
240
363
|
const responseIndex = messageIndex + 1;
|
|
241
364
|
if (responseIndex < this._messageItems.length) {
|
|
242
|
-
this
|
|
365
|
+
this.#removeMessageItemsFrom(responseIndex);
|
|
243
366
|
}
|
|
244
367
|
adapter.sendMessage(this.getMessages());
|
|
245
368
|
}
|
|
@@ -256,7 +379,7 @@ class AiChatbotBase extends LitElement {
|
|
|
256
379
|
this._coreController.updateMessageContent(messageId, content);
|
|
257
380
|
const responseIndex = messageIndex + 1;
|
|
258
381
|
if (responseIndex < this._messageItems.length) {
|
|
259
|
-
this
|
|
382
|
+
this.#removeMessageItemsFrom(responseIndex);
|
|
260
383
|
}
|
|
261
384
|
adapter.sendMessage(this.getMessages());
|
|
262
385
|
}
|
|
@@ -272,7 +395,7 @@ class AiChatbotBase extends LitElement {
|
|
|
272
395
|
if (responseIndex === -1) {
|
|
273
396
|
return;
|
|
274
397
|
}
|
|
275
|
-
this
|
|
398
|
+
this.#removeMessageItemsFrom(responseIndex);
|
|
276
399
|
adapter.sendMessage(this.getMessages());
|
|
277
400
|
}
|
|
278
401
|
_handleFeedback(evt, type) {
|
|
@@ -348,6 +471,7 @@ class AiChatbotBase extends LitElement {
|
|
|
348
471
|
if (event.defaultPrevented) {
|
|
349
472
|
return false;
|
|
350
473
|
}
|
|
474
|
+
this._teardownMcpAppViews();
|
|
351
475
|
this._coreController.clearMessages();
|
|
352
476
|
return true;
|
|
353
477
|
}
|
|
@@ -365,6 +489,7 @@ class AiChatbotBase extends LitElement {
|
|
|
365
489
|
* resetting thread IDs or closing panels.
|
|
366
490
|
*/
|
|
367
491
|
startNewChat() {
|
|
492
|
+
this._teardownMcpAppViews();
|
|
368
493
|
this._coreController.clearMessages();
|
|
369
494
|
}
|
|
370
495
|
getMessages() {
|
|
@@ -431,6 +556,14 @@ class AiChatbotBase extends LitElement {
|
|
|
431
556
|
abort() {
|
|
432
557
|
this._handleStop();
|
|
433
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Re-sends the Forge-derived `styles.variables` to every MCP-app widget. Call after
|
|
561
|
+
* changing Forge tokens without a light/dark switch (e.g. a brand change); for light/dark,
|
|
562
|
+
* set {@link mcpAppTheme} instead.
|
|
563
|
+
*/
|
|
564
|
+
refreshMcpAppStyles() {
|
|
565
|
+
this.#refreshMcpAppHostContext();
|
|
566
|
+
}
|
|
434
567
|
async scrollToBottom({ behavior } = {}) {
|
|
435
568
|
await this._messageThreadRef.value?.updateComplete;
|
|
436
569
|
await new Promise(requestAnimationFrame);
|
|
@@ -448,6 +581,7 @@ class AiChatbotBase extends LitElement {
|
|
|
448
581
|
return this.agents.find((a) => a.id === this.selectedAgentId);
|
|
449
582
|
}
|
|
450
583
|
async setThreadState(threadState) {
|
|
584
|
+
this._teardownMcpAppViews();
|
|
451
585
|
this.setMessages(threadState.messages);
|
|
452
586
|
this.selectedAgentId = threadState.selectedAgentId;
|
|
453
587
|
if (threadState.threadId && this._coreController.adapter) {
|
|
@@ -535,6 +669,16 @@ __decorateClass([
|
|
|
535
669
|
__decorateClass([
|
|
536
670
|
property({ attribute: false })
|
|
537
671
|
], AiChatbotBase.prototype, "contextItems");
|
|
672
|
+
__decorateClass([
|
|
673
|
+
property({ attribute: "mcp-app-sandbox-url" })
|
|
674
|
+
], AiChatbotBase.prototype, "mcpAppSandboxUrl");
|
|
675
|
+
__decorateClass([
|
|
676
|
+
property({ attribute: "mcp-app-theme" })
|
|
677
|
+
], AiChatbotBase.prototype, "mcpAppTheme");
|
|
678
|
+
__decorateClass([
|
|
679
|
+
provide({ context: mcpAppHostContext }),
|
|
680
|
+
property({ attribute: false })
|
|
681
|
+
], AiChatbotBase.prototype, "_mcpAppHost");
|
|
538
682
|
export {
|
|
539
683
|
AiChatbotBase
|
|
540
684
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LitElement, TemplateResult, nothing, PropertyValues, CSSResult } from 'lit';
|
|
2
|
-
import { ToolCall,
|
|
2
|
+
import { ToolCall, AnyToolDefinition } from './types.js';
|
|
3
3
|
declare global {
|
|
4
4
|
interface HTMLElementTagNameMap {
|
|
5
5
|
'forge-ai-chatbot-tool-call': AiChatbotToolCallComponent;
|
|
@@ -13,7 +13,7 @@ export declare class AiChatbotToolCallComponent extends LitElement {
|
|
|
13
13
|
#private;
|
|
14
14
|
static styles: CSSResult;
|
|
15
15
|
toolCall: ToolCall;
|
|
16
|
-
toolDefinition?:
|
|
16
|
+
toolDefinition?: AnyToolDefinition;
|
|
17
17
|
updated(changedProperties: PropertyValues<this>): void;
|
|
18
18
|
render(): TemplateResult | typeof nothing;
|
|
19
19
|
}
|