@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.
Files changed (88) hide show
  1. package/custom-elements.json +4501 -1781
  2. package/dist/ai-artifact-card/ai-artifact-card.d.ts +75 -0
  3. package/dist/ai-artifact-card/ai-artifact-card.mjs +142 -0
  4. package/dist/ai-artifact-card/ai-artifact-card.scss.mjs +283 -0
  5. package/dist/ai-artifact-card/index.d.ts +1 -0
  6. package/dist/ai-artifact-card/index.mjs +5 -0
  7. package/dist/ai-artifact-card-group/ai-artifact-card-group.d.ts +57 -0
  8. package/dist/ai-artifact-card-group/ai-artifact-card-group.mjs +137 -0
  9. package/dist/ai-artifact-card-group/ai-artifact-card-group.scss.mjs +173 -0
  10. package/dist/ai-artifact-card-group/index.d.ts +1 -0
  11. package/dist/ai-artifact-card-group/index.mjs +5 -0
  12. package/dist/ai-assistant-response/ai-assistant-response.d.ts +2 -2
  13. package/dist/ai-assistant-response/ai-assistant-response.mjs +26 -2
  14. package/dist/ai-canvas/ai-canvas.d.ts +43 -0
  15. package/dist/ai-canvas/ai-canvas.mjs +95 -0
  16. package/dist/ai-canvas/ai-canvas.scss.mjs +4 -0
  17. package/dist/ai-canvas/index.d.ts +1 -0
  18. package/dist/ai-canvas/index.mjs +5 -0
  19. package/dist/ai-chatbot/ag-ui-adapter.d.ts +2 -2
  20. package/dist/ai-chatbot/ag-ui-adapter.mjs +1 -1
  21. package/dist/ai-chatbot/agent-adapter.d.ts +111 -4
  22. package/dist/ai-chatbot/agent-adapter.mjs +22 -0
  23. package/dist/ai-chatbot/agent-runner.d.ts +2 -2
  24. package/dist/ai-chatbot/ai-chatbot-base.d.ts +29 -2
  25. package/dist/ai-chatbot/ai-chatbot-base.mjs +147 -3
  26. package/dist/ai-chatbot/ai-chatbot-tool-call.d.ts +2 -2
  27. package/dist/ai-chatbot/ai-chatbot-tool-call.mjs +88 -10
  28. package/dist/ai-chatbot/ai-chatbot.d.ts +24 -1
  29. package/dist/ai-chatbot/ai-chatbot.mjs +102 -2
  30. package/dist/ai-chatbot/ai-chatbot.scss.mjs +1 -1
  31. package/dist/ai-chatbot/create-tool-renderer.d.ts +5 -5
  32. package/dist/ai-chatbot/index.d.ts +3 -2
  33. package/dist/ai-chatbot/index.mjs +3 -0
  34. package/dist/ai-chatbot/message-state-controller.d.ts +10 -1
  35. package/dist/ai-chatbot/message-state-controller.mjs +78 -20
  36. package/dist/ai-chatbot/tool-canvas.d.ts +60 -0
  37. package/dist/ai-chatbot/tool-canvas.mjs +128 -0
  38. package/dist/ai-chatbot/types.d.ts +325 -15
  39. package/dist/ai-chatbot/utils.d.ts +8 -3
  40. package/dist/ai-chatbot/utils.mjs +7 -1
  41. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.d.ts +40 -2
  42. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +224 -7
  43. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.scss.mjs +1 -1
  44. package/dist/ai-embedded-chat/ai-embedded-chat.mjs +1 -0
  45. package/dist/ai-message-thread/ai-message-thread.d.ts +3 -2
  46. package/dist/ai-message-thread/ai-message-thread.mjs +36 -3
  47. package/dist/ai-steps/ai-steps.d.ts +67 -0
  48. package/dist/ai-steps/ai-steps.mjs +163 -0
  49. package/dist/ai-steps/ai-steps.scss.mjs +4 -0
  50. package/dist/ai-steps/index.d.ts +1 -0
  51. package/dist/ai-steps/index.mjs +5 -0
  52. package/dist/ai-tool-call-indicator/ai-tool-call-indicator.d.ts +2 -2
  53. package/dist/ai-tool-call-indicator/ai-tool-call-indicator.mjs +1 -1
  54. package/dist/core/canvas-controller.d.ts +81 -0
  55. package/dist/core/canvas-controller.mjs +221 -0
  56. package/dist/core/chatbot-core-controller.d.ts +4 -2
  57. package/dist/core/chatbot-core-controller.mjs +121 -4
  58. package/dist/core/index.d.ts +2 -1
  59. package/dist/core/index.mjs +2 -0
  60. package/dist/core/resize-controller.d.ts +5 -0
  61. package/dist/core/resize-controller.mjs +7 -5
  62. package/dist/index.d.ts +4 -0
  63. package/dist/index.mjs +15 -0
  64. package/dist/tools/ai-data-table/ai-data-table-definition.mjs +1 -0
  65. package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +70 -0
  66. package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +589 -0
  67. package/dist/tools/ai-mcp-app/ai-mcp-app.scss.mjs +4 -0
  68. package/dist/tools/ai-mcp-app/index.d.ts +3 -0
  69. package/dist/tools/ai-mcp-app/index.mjs +6 -0
  70. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.d.ts +60 -0
  71. package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +147 -0
  72. package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +124 -0
  73. package/dist/tools/ai-mcp-app/mcp-app-bridge.d.ts +41 -0
  74. package/dist/tools/ai-mcp-app/mcp-app-bridge.mjs +293 -0
  75. package/dist/tools/ai-mcp-app/mcp-app-context.d.ts +46 -0
  76. package/dist/tools/ai-mcp-app/mcp-app-context.mjs +5 -0
  77. package/dist/tools/ai-steps/ai-steps-definition.d.ts +2 -0
  78. package/dist/tools/ai-steps/ai-steps-definition.mjs +51 -0
  79. package/dist/tools/ai-steps/ai-steps.d.ts +28 -0
  80. package/dist/tools/ai-steps/ai-steps.mjs +49 -0
  81. package/dist/tools/ai-steps/ai-steps.scss.mjs +4 -0
  82. package/dist/tools/ai-steps/index.d.ts +2 -0
  83. package/dist/tools/ai-steps/index.mjs +6 -0
  84. package/dist/tools/index.d.ts +1 -0
  85. package/dist/tools/index.mjs +5 -1
  86. package/dist/utils/inert-outside.d.ts +7 -0
  87. package/dist/utils/inert-outside.mjs +28 -0
  88. package/package.json +4 -1
@@ -0,0 +1,293 @@
1
+ const PROTOCOL_VERSION = "2026-01-26";
2
+ const METHOD_NOT_FOUND = -32601;
3
+ const INVALID_PARAMS = -32602;
4
+ const SERVER_ERROR = -32e3;
5
+ const DISPLAY_MODES = ["inline", "fullscreen", "pip"];
6
+ class JsonRpcError extends Error {
7
+ constructor(config) {
8
+ super(config.message);
9
+ this.code = config.code;
10
+ }
11
+ }
12
+ function invalidParams(message) {
13
+ return new JsonRpcError({ code: INVALID_PARAMS, message });
14
+ }
15
+ function methodNotFound(method) {
16
+ return new JsonRpcError({ code: METHOD_NOT_FOUND, message: `Method not found: ${method}` });
17
+ }
18
+ function asParams(params) {
19
+ if (params === void 0 || params === null) {
20
+ return {};
21
+ }
22
+ if (typeof params !== "object" || Array.isArray(params)) {
23
+ throw invalidParams("params must be an object");
24
+ }
25
+ return params;
26
+ }
27
+ function asJsonRpc(data) {
28
+ if (!data || typeof data !== "object") {
29
+ return null;
30
+ }
31
+ const payload = data;
32
+ return payload.jsonrpc === "2.0" ? payload : null;
33
+ }
34
+ function asDisplayModes(value) {
35
+ return Array.isArray(value) ? DISPLAY_MODES.filter((mode) => value.includes(mode)) : [];
36
+ }
37
+ class McpAppBridge {
38
+ #hostInfo;
39
+ #capabilities = {};
40
+ #phase = "idle";
41
+ #sandboxWindow = null;
42
+ #sandboxOrigin = "";
43
+ #hostContext = { theme: "light" };
44
+ /** The host context the View last received (in the `ui/initialize` result or a change notification). */
45
+ #deliveredHostContext = null;
46
+ #appDisplayModes = [];
47
+ #nextId = 1;
48
+ #pending = /* @__PURE__ */ new Map();
49
+ #resolveProxyReady;
50
+ #initialized = null;
51
+ #resolveInitialized;
52
+ constructor(hostInfo) {
53
+ this.#hostInfo = hostInfo;
54
+ }
55
+ connect(config) {
56
+ if (this.#phase !== "idle") {
57
+ throw new Error("Bridge has already been connected; create a new bridge per widget.");
58
+ }
59
+ this.#phase = "connecting";
60
+ this.#capabilities = config.hostCapabilities;
61
+ this.#hostContext = config.hostContext;
62
+ this.#sandboxWindow = config.sandboxWindow;
63
+ this.#sandboxOrigin = config.sandboxOrigin;
64
+ window.addEventListener("message", this.#handleMessage);
65
+ const proxyReady = new Promise((resolve) => {
66
+ this.#resolveProxyReady = resolve;
67
+ });
68
+ this.#initialized = new Promise((resolve) => {
69
+ this.#resolveInitialized = resolve;
70
+ }).then(() => {
71
+ this.#phase = "ready";
72
+ this.#flushHostContext();
73
+ });
74
+ return proxyReady.then(
75
+ () => this.#sendNotification("ui/notifications/sandbox-resource-ready", {
76
+ html: config.html,
77
+ permissions: config.permissions,
78
+ sandbox: config.sandbox
79
+ })
80
+ ).then(() => this.#initialized);
81
+ }
82
+ sendToolInputPartial(args) {
83
+ this.#sendNotification("ui/notifications/tool-input-partial", { arguments: args });
84
+ }
85
+ sendToolInput(args) {
86
+ this.#sendNotification("ui/notifications/tool-input", { arguments: args });
87
+ }
88
+ sendToolResult(result) {
89
+ this.#sendNotification("ui/notifications/tool-result", result);
90
+ }
91
+ sendToolCancelled(reason) {
92
+ this.#sendNotification("ui/notifications/tool-cancelled", { reason: reason ?? "cancelled" });
93
+ }
94
+ sendHostContextChange(hostContext) {
95
+ this.#hostContext = hostContext;
96
+ if (this.#phase === "ready") {
97
+ this.#flushHostContext();
98
+ }
99
+ }
100
+ /**
101
+ * Send `ui/resource-teardown` and dispose immediately; the host doesn't wait for the ack,
102
+ * so the iframe is removed right away. A View that never initialized isn't messaged.
103
+ */
104
+ sendTeardown() {
105
+ if (this.#phase === "ready") {
106
+ this.#sendRequest("ui/resource-teardown", {}).catch(() => void 0);
107
+ }
108
+ this.#dispose();
109
+ }
110
+ #flushHostContext() {
111
+ if (this.#hostContext === this.#deliveredHostContext) {
112
+ return;
113
+ }
114
+ this.#deliveredHostContext = this.#hostContext;
115
+ this.#sendNotification("ui/notifications/host-context-changed", this.#hostContext);
116
+ }
117
+ /** Release the `message` listener + reject any in-flight requests. Idempotent. */
118
+ #dispose() {
119
+ this.#phase = "disposed";
120
+ window.removeEventListener("message", this.#handleMessage);
121
+ for (const pending of this.#pending.values()) {
122
+ pending.reject(new Error("Bridge disposed"));
123
+ }
124
+ this.#pending.clear();
125
+ this.#sandboxWindow = null;
126
+ }
127
+ #handleMessage = (event) => {
128
+ if (event.source !== this.#sandboxWindow || event.origin !== this.#sandboxOrigin) {
129
+ return;
130
+ }
131
+ const payload = asJsonRpc(event.data);
132
+ if (!payload || typeof payload.method !== "string") {
133
+ if (payload && payload.id !== void 0) {
134
+ this.#handleResponse(payload);
135
+ } else if (payload) {
136
+ console.warn("[forge-ai-mcp-app] Ignoring malformed JSON-RPC message", event.data);
137
+ }
138
+ return;
139
+ }
140
+ if (payload.id !== void 0) {
141
+ void this.#handleRequest(payload.method, payload.params, payload.id);
142
+ } else {
143
+ this.#handleNotification(payload.method, payload.params);
144
+ }
145
+ };
146
+ #handleResponse(payload) {
147
+ const id = payload.id;
148
+ const pending = this.#pending.get(id);
149
+ if (!pending) {
150
+ return;
151
+ }
152
+ this.#pending.delete(id);
153
+ if (payload.error) {
154
+ pending.reject(new Error(payload.error.message));
155
+ } else {
156
+ pending.resolve(payload.result);
157
+ }
158
+ }
159
+ async #handleRequest(method, params, id) {
160
+ try {
161
+ const result = await this.#dispatchRequest(method, params);
162
+ this.#post({ jsonrpc: "2.0", id, result: result ?? {} });
163
+ } catch (error) {
164
+ this.#post({
165
+ jsonrpc: "2.0",
166
+ id,
167
+ error: {
168
+ code: error instanceof JsonRpcError ? error.code : SERVER_ERROR,
169
+ message: error instanceof Error ? error.message : "Request failed"
170
+ }
171
+ });
172
+ }
173
+ }
174
+ /** Unknown or unadvertised methods (`tools/list`, `sampling/createMessage`, `ui/download-file`, …) reject with -32601. */
175
+ async #dispatchRequest(method, params) {
176
+ const p = asParams(params);
177
+ switch (method) {
178
+ case "ui/initialize": {
179
+ const appCapabilities = p.appCapabilities ?? {};
180
+ this.#appDisplayModes = asDisplayModes(appCapabilities.availableDisplayModes);
181
+ this.#deliveredHostContext = this.#hostContext;
182
+ return {
183
+ protocolVersion: PROTOCOL_VERSION,
184
+ hostInfo: this.#hostInfo,
185
+ hostCapabilities: this.#capabilities,
186
+ hostContext: this.#hostContext
187
+ };
188
+ }
189
+ case "ui/open-link":
190
+ if (typeof p.url !== "string") {
191
+ throw invalidParams("ui/open-link requires a string url");
192
+ }
193
+ return await this.onopenlink?.({ url: p.url }) ?? { isError: true };
194
+ case "ui/message":
195
+ if (!Array.isArray(p.content)) {
196
+ throw invalidParams("ui/message requires a content array");
197
+ }
198
+ return await this.onmessage?.({ content: p.content, role: "user" }) ?? { isError: true };
199
+ case "ui/update-model-context":
200
+ await this.onupdatemodelcontext?.({
201
+ content: Array.isArray(p.content) ? p.content : void 0,
202
+ structuredContent: p.structuredContent
203
+ });
204
+ return {};
205
+ case "ui/request-display-mode": {
206
+ const current = this.#hostContext.displayMode ?? "inline";
207
+ const [mode] = asDisplayModes([p.mode]);
208
+ if (!mode || !this.onrequestdisplaymode) {
209
+ return { mode: current };
210
+ }
211
+ return this.onrequestdisplaymode({ mode, appDisplayModes: this.#appDisplayModes });
212
+ }
213
+ case "ui/resource-teardown":
214
+ case "ping":
215
+ return {};
216
+ case "tools/call":
217
+ if (!this.oncalltool || !this.#capabilities.serverTools) {
218
+ throw methodNotFound(method);
219
+ }
220
+ this.#assertReady(method);
221
+ if (typeof p.name !== "string" || !p.name) {
222
+ throw invalidParams("tools/call requires a tool name");
223
+ }
224
+ return this.oncalltool({ name: p.name, arguments: asParams(p.arguments) });
225
+ case "resources/read":
226
+ if (!this.onreadresource) {
227
+ throw methodNotFound(method);
228
+ }
229
+ this.#assertReady(method);
230
+ if (typeof p.uri !== "string" || !p.uri) {
231
+ throw invalidParams("resources/read requires a uri");
232
+ }
233
+ return this.onreadresource({ uri: p.uri });
234
+ default:
235
+ throw methodNotFound(method);
236
+ }
237
+ }
238
+ /** Server-bound requests reach the adapter only after the View completes the handshake. */
239
+ #assertReady(method) {
240
+ if (this.#phase !== "ready") {
241
+ throw new JsonRpcError({
242
+ code: SERVER_ERROR,
243
+ message: `${method} is not allowed before ui/notifications/initialized`
244
+ });
245
+ }
246
+ }
247
+ #handleNotification(method, params) {
248
+ const p = params && typeof params === "object" ? params : {};
249
+ switch (method) {
250
+ case "ui/notifications/sandbox-proxy-ready":
251
+ this.#resolveProxyReady?.();
252
+ return;
253
+ case "ui/notifications/initialized":
254
+ this.#resolveInitialized?.();
255
+ return;
256
+ case "ui/notifications/size-changed":
257
+ this.onsizechange?.({
258
+ width: typeof p.width === "number" ? p.width : void 0,
259
+ height: typeof p.height === "number" ? p.height : 0
260
+ });
261
+ return;
262
+ case "notifications/message":
263
+ this.onloggingmessage?.({
264
+ level: typeof p.level === "string" ? p.level : "info",
265
+ logger: typeof p.logger === "string" ? p.logger : void 0,
266
+ data: p.data
267
+ });
268
+ return;
269
+ case "ui/notifications/request-teardown":
270
+ this.onrequestteardown?.();
271
+ return;
272
+ default:
273
+ console.warn(`[forge-ai-mcp-app] Unhandled notification method: ${method}`);
274
+ return;
275
+ }
276
+ }
277
+ #sendNotification(method, params) {
278
+ this.#post({ jsonrpc: "2.0", method, params });
279
+ }
280
+ #sendRequest(method, params) {
281
+ const id = this.#nextId++;
282
+ return new Promise((resolve, reject) => {
283
+ this.#pending.set(id, { resolve, reject });
284
+ this.#post({ jsonrpc: "2.0", id, method, params });
285
+ });
286
+ }
287
+ #post(data) {
288
+ this.#sandboxWindow?.postMessage(data, this.#sandboxOrigin);
289
+ }
290
+ }
291
+ export {
292
+ McpAppBridge
293
+ };
@@ -0,0 +1,46 @@
1
+ import { McpAppHostCapabilities, McpAppHostContext, McpModelContextUpdateParams, McpResourceReadParams, McpToolCallParams } from '../../ai-chatbot';
2
+ import { McpAppRequestResult } from './mcp-app-bridge-types.js';
3
+ /** A mounted widget, registered so the host can tear it down before removing it. */
4
+ export interface McpAppViewRegistration {
5
+ toolCallId: string;
6
+ teardown(): void;
7
+ }
8
+ /**
9
+ * Host surface an `<forge-ai-mcp-app>` consumes to service widget→host calls and to read
10
+ * the current host context. Provided by the chatbot base component (Step 4) and updated
11
+ * in place; consumers use `@consume({ subscribe: true })` and re-render when it changes.
12
+ *
13
+ * The library never owns an MCP `Client` — `callTool`/`readResource`/`updateModelContext`
14
+ * delegate to the adapter's `callMcpTool`/`readMcpResource`/`updateMcpModelContext`.
15
+ * Every call carries the widget's `toolCallId`.
16
+ */
17
+ export interface McpAppHost {
18
+ callTool(params: McpToolCallParams): Promise<unknown>;
19
+ readResource(params: McpResourceReadParams): Promise<unknown>;
20
+ updateModelContext(params: McpModelContextUpdateParams): Promise<void>;
21
+ /**
22
+ * Place a widget's `ui/message` text in the prompt for the user to send. Resolves
23
+ * `{ isError: true }` when there is no text or no prompt.
24
+ */
25
+ draftMessage(params: {
26
+ toolCallId: string;
27
+ content: unknown[];
28
+ }): Promise<McpAppRequestResult>;
29
+ /** Tear down one widget through the host's normal per-widget teardown path. */
30
+ teardownView(params: {
31
+ toolCallId: string;
32
+ }): void;
33
+ /** Register a mounted widget; returns the unregister callback. */
34
+ registerView(view: McpAppViewRegistration): () => void;
35
+ hostContext: McpAppHostContext;
36
+ /** Cross-origin sandbox proxy URL. Absent → the element renders an error artifact. */
37
+ sandboxUrl?: string;
38
+ /**
39
+ * Advertised to the widget in `McpUiInitializeResult`, derived from what the host and
40
+ * adapter implement. The element adds `sandbox` (the enforced csp/permissions) per widget.
41
+ */
42
+ hostCapabilities: McpAppHostCapabilities;
43
+ }
44
+ export declare const mcpAppHostContext: {
45
+ __context__: McpAppHost;
46
+ };
@@ -0,0 +1,5 @@
1
+ import { createContext } from "@lit/context";
2
+ const mcpAppHostContext = createContext(/* @__PURE__ */ Symbol("forge-mcp-app-host"));
3
+ export {
4
+ mcpAppHostContext
5
+ };
@@ -0,0 +1,2 @@
1
+ import { ToolDefinition } from '../../ai-chatbot';
2
+ export declare const displayStepsTool: ToolDefinition;
@@ -0,0 +1,51 @@
1
+ import "../../ai-chatbot/ai-chatbot-base.mjs";
2
+ import "../../ai-chatbot/ai-chatbot.mjs";
3
+ import "../../ai-chatbot/ai-chatbot-tool-call.mjs";
4
+ import "@ag-ui/client";
5
+ import "uuid";
6
+ import "../../utils/markdown.mjs";
7
+ import { createToolRenderer } from "../../ai-chatbot/create-tool-renderer.mjs";
8
+ import "../../ai-chatbot/tool-canvas.mjs";
9
+ const displayStepsTool = {
10
+ name: "displaySteps",
11
+ displayName: "Display Steps",
12
+ description: "Surface the sequence of steps you took while working on a request as a compact, expandable timeline. Call this when your answer involved multiple discrete actions — searching, filtering, joining, calculating — and showing that work helps the user trust the result. Provide the steps in the order they happened.",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ steps: {
17
+ type: "array",
18
+ description: "The steps taken, in the order they happened.",
19
+ items: {
20
+ type: "object",
21
+ properties: {
22
+ label: {
23
+ type: "string",
24
+ description: 'Short past-tense description of the step, e.g. "Searched the orders table" or "Filtered by `crime_category`". This is the row title, so keep it to a few words. Inline markdown is supported — use `backticks` for identifiers such as columns, tables or query fragments, **bold** for a figure or outcome worth emphasizing, and *italics* or ~~strikethrough~~ where they help. Emphasize sparingly; a row where everything is bold reads the same as one where nothing is.'
25
+ },
26
+ detail: {
27
+ type: "string",
28
+ description: 'Optional elaboration shown beneath the label, e.g. the filter applied or the number of records matched. Supports the same inline markdown as "label". Omit for steps that need no explanation.'
29
+ },
30
+ status: {
31
+ type: "string",
32
+ enum: ["pending", "running", "complete"],
33
+ description: 'Outcome of the step. Defaults to "complete" when omitted.'
34
+ }
35
+ },
36
+ required: ["label"]
37
+ }
38
+ }
39
+ },
40
+ required: ["steps"]
41
+ },
42
+ // Steps are a progress affordance, so the timeline needs to appear while arguments are still
43
+ // streaming rather than only once the call settles.
44
+ renderOnStart: true,
45
+ renderer: createToolRenderer({
46
+ elementTag: "forge-ai-tool-steps"
47
+ })
48
+ };
49
+ export {
50
+ displayStepsTool
51
+ };
@@ -0,0 +1,28 @@
1
+ import { LitElement, TemplateResult, CSSResult } from 'lit';
2
+ import { IToolRenderer, ToolCall } from '../../ai-chatbot';
3
+ import { AiStep } from '../../ai-steps';
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ 'forge-ai-tool-steps': StepsToolElement;
7
+ }
8
+ }
9
+ interface StepsData {
10
+ steps: AiStep[];
11
+ }
12
+ /**
13
+ * Steps component tool renderer. Maps a `displaySteps` tool call onto the presentational
14
+ * `forge-ai-steps` primitive, so the primitive stays unaware of tool calls.
15
+ *
16
+ * @tag forge-ai-tool-steps
17
+ * @internal
18
+ */
19
+ export declare class StepsToolElement extends LitElement implements IToolRenderer<StepsData> {
20
+ #private;
21
+ static styles: CSSResult;
22
+ /**
23
+ * Tool call data containing the agent-provided steps.
24
+ */
25
+ toolCall: ToolCall<StepsData>;
26
+ render(): TemplateResult;
27
+ }
28
+ export {};
@@ -0,0 +1,49 @@
1
+ import { unsafeCSS, LitElement, html } from "lit";
2
+ import { property, customElement } from "lit/decorators.js";
3
+ import "../../ai-steps/ai-steps.mjs";
4
+ import styles from "./ai-steps.scss.mjs";
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
10
+ var __decorateClass = (decorators, target, key, kind) => {
11
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
12
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
13
+ if (decorator = decorators[i])
14
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
15
+ if (kind && result) __defProp(target, key, result);
16
+ return result;
17
+ };
18
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
19
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
20
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
21
+ var _StepsToolElement_instances, steps_get, running_get;
22
+ let StepsToolElement = class extends LitElement {
23
+ constructor() {
24
+ super(...arguments);
25
+ __privateAdd(this, _StepsToolElement_instances);
26
+ }
27
+ render() {
28
+ return html`<forge-ai-steps .steps=${__privateGet(this, _StepsToolElement_instances, steps_get)} ?running=${__privateGet(this, _StepsToolElement_instances, running_get)}></forge-ai-steps>`;
29
+ }
30
+ };
31
+ _StepsToolElement_instances = /* @__PURE__ */ new WeakSet();
32
+ steps_get = function() {
33
+ const steps = this.toolCall?.args?.steps;
34
+ return Array.isArray(steps) ? steps.filter((step) => !!step?.label) : [];
35
+ };
36
+ running_get = function() {
37
+ const status = this.toolCall?.status;
38
+ return status !== "complete" && status !== "error" && status !== "cancelled";
39
+ };
40
+ StepsToolElement.styles = unsafeCSS(styles);
41
+ __decorateClass([
42
+ property({ attribute: false })
43
+ ], StepsToolElement.prototype, "toolCall", 2);
44
+ StepsToolElement = __decorateClass([
45
+ customElement("forge-ai-tool-steps")
46
+ ], StepsToolElement);
47
+ export {
48
+ StepsToolElement
49
+ };
@@ -0,0 +1,4 @@
1
+ const styles = ":host {\n display: block;\n}";
2
+ export {
3
+ styles as default
4
+ };
@@ -0,0 +1,2 @@
1
+ export * from './ai-steps-definition.js';
2
+ export * from './ai-steps.js';
@@ -0,0 +1,6 @@
1
+ import { displayStepsTool } from "./ai-steps-definition.mjs";
2
+ import { StepsToolElement } from "./ai-steps.mjs";
3
+ export {
4
+ StepsToolElement,
5
+ displayStepsTool
6
+ };
@@ -1,2 +1,3 @@
1
1
  export * from './ai-data-table';
2
2
  export * from './ai-paginator';
3
+ export * from './ai-steps';
@@ -1,8 +1,12 @@
1
1
  import { displayDataTableTool } from "./ai-data-table/ai-data-table-definition.mjs";
2
2
  import { DataTableToolElement } from "./ai-data-table/ai-data-table.mjs";
3
3
  import { AiPaginator } from "./ai-paginator/ai-paginator.mjs";
4
+ import { displayStepsTool } from "./ai-steps/ai-steps-definition.mjs";
5
+ import { StepsToolElement } from "./ai-steps/ai-steps.mjs";
4
6
  export {
5
7
  AiPaginator,
6
8
  DataTableToolElement,
7
- displayDataTableTool
9
+ StepsToolElement,
10
+ displayDataTableTool,
11
+ displayStepsTool
8
12
  };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Marks everything outside `element` inert, as a modal would, without moving it in the DOM.
3
+ * `inert` on an ancestor would disable `element` too (even in the top layer), so this marks
4
+ * the siblings at each level up through shadow roots instead. Returns a restore function that
5
+ * clears only the elements this call made inert.
6
+ */
7
+ export declare function inertOutside(element: Element): () => void;
@@ -0,0 +1,28 @@
1
+ function flatLevel(element) {
2
+ if (element.assignedSlot) {
3
+ return { siblings: element.assignedSlot.assignedElements(), parent: element.assignedSlot };
4
+ }
5
+ if (element.parentElement) {
6
+ return { siblings: [...element.parentElement.children], parent: element.parentElement };
7
+ }
8
+ const root = element.getRootNode();
9
+ return root instanceof ShadowRoot ? { siblings: [...root.children], parent: root.host } : { siblings: [], parent: null };
10
+ }
11
+ function inertOutside(element) {
12
+ const marked = [];
13
+ let current = element;
14
+ while (current) {
15
+ const { siblings, parent } = flatLevel(current);
16
+ for (const sibling of siblings) {
17
+ if (sibling !== current && sibling instanceof HTMLElement && !sibling.inert) {
18
+ sibling.inert = true;
19
+ marked.push(sibling);
20
+ }
21
+ }
22
+ current = parent;
23
+ }
24
+ return () => marked.forEach((sibling) => sibling.inert = false);
25
+ }
26
+ export {
27
+ inertOutside
28
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tylertech/forge-ai",
3
- "version": "0.15.2",
3
+ "version": "0.15.3-dev.1",
4
4
  "description": "A library of Tyler Forge™ AI chat interface web components.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Tyler Technologies, Inc.",
@@ -52,6 +52,8 @@
52
52
  "devDependencies": {
53
53
  "@custom-elements-manifest/analyzer": "0.11.0",
54
54
  "@esm-bundle/chai": "4.3.4-fix.0",
55
+ "@modelcontextprotocol/ext-apps": "^1.7.5",
56
+ "@modelcontextprotocol/sdk": "^1.32.2",
55
57
  "@open-wc/testing": "4.0.0",
56
58
  "@storybook/addon-a11y": "9.1.20",
57
59
  "@storybook/addon-docs": "9.1.20",
@@ -111,6 +113,7 @@
111
113
  "preview": "vite preview",
112
114
  "prestorybook": "pnpm run cem",
113
115
  "storybook": "storybook dev -p 6016",
116
+ "sandbox": "node sandbox/server.mjs",
114
117
  "prebuild:storybook": "pnpm run cem",
115
118
  "build:storybook": "storybook build",
116
119
  "lint": "pnpm run lint:lit-analyzer && pnpm run lint:eslint && pnpm run lint:stylelint",