@tylertech/forge-ai 0.15.1 → 0.15.3-dev.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/custom-elements.json +8152 -6309
- 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.mjs +25 -2
- package/dist/ai-chatbot/ag-ui-adapter.mjs +1 -1
- package/dist/ai-chatbot/agent-adapter.d.ts +108 -1
- package/dist/ai-chatbot/agent-adapter.mjs +22 -0
- package/dist/ai-chatbot/ai-chatbot-base.d.ts +26 -0
- package/dist/ai-chatbot/ai-chatbot-base.mjs +136 -3
- package/dist/ai-chatbot/index.d.ts +2 -2
- 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/types.d.ts +199 -1
- package/dist/ai-chatbot/utils.mjs +3 -1
- package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +14 -4
- package/dist/ai-message-thread/ai-message-thread.d.ts +1 -0
- package/dist/ai-message-thread/ai-message-thread.mjs +35 -3
- package/dist/ai-sidebar/ai-sidebar.d.ts +17 -6
- package/dist/ai-sidebar/ai-sidebar.mjs +78 -36
- package/dist/ai-sidebar/ai-sidebar.scss.mjs +1 -1
- package/dist/ai-sidebar/index.d.ts +1 -0
- package/dist/ai-sidebar-chat/ai-sidebar-chat.d.ts +14 -1
- package/dist/ai-sidebar-chat/ai-sidebar-chat.mjs +18 -14
- 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.mjs +1 -1
- package/dist/core/chatbot-core-controller.d.ts +2 -0
- package/dist/core/chatbot-core-controller.mjs +99 -4
- package/dist/core/resize-controller.d.ts +1 -0
- package/dist/core/resize-controller.mjs +11 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +9 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.d.ts +69 -0
- package/dist/tools/ai-mcp-app/ai-mcp-app.mjs +574 -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 +61 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-controller.mjs +156 -0
- package/dist/tools/ai-mcp-app/mcp-app-bridge-types.d.ts +123 -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 +50 -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/index.d.ts +1 -0
- package/dist/utils/index.mjs +5 -1
- package/dist/utils/inert-outside.d.ts +7 -0
- package/dist/utils/inert-outside.mjs +28 -0
- package/dist/utils/storage-utils.d.ts +26 -0
- package/dist/utils/storage-utils.mjs +32 -0
- package/package.json +4 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { LitElement, PropertyValues, nothing, TemplateResult, CSSResult } from 'lit';
|
|
2
|
+
declare global {
|
|
3
|
+
interface HTMLElementTagNameMap {
|
|
4
|
+
'forge-ai-steps': AiStepsComponent;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export declare const AiStepsComponentTagName: keyof HTMLElementTagNameMap;
|
|
8
|
+
/** Lifecycle state of an individual step. */
|
|
9
|
+
export type AiStepStatus = 'pending' | 'running' | 'complete';
|
|
10
|
+
/**
|
|
11
|
+
* A single step in the timeline. Every field is supplied by the agent — the component derives no
|
|
12
|
+
* labels of its own and makes no assumption about how the agent names its work.
|
|
13
|
+
*/
|
|
14
|
+
export interface AiStep {
|
|
15
|
+
/**
|
|
16
|
+
* Agent-provided label for the step, e.g. `'Filtered by \`crime_category\`'`. Rendered as inline
|
|
17
|
+
* markdown, so the agent decides which part of its own label to emphasize. Literal `*`, `_` and
|
|
18
|
+
* `` ` `` characters need escaping.
|
|
19
|
+
*/
|
|
20
|
+
label: string;
|
|
21
|
+
/**
|
|
22
|
+
* Optional agent-provided elaboration, rendered as a detail card beneath the label. Also rendered
|
|
23
|
+
* as inline markdown.
|
|
24
|
+
*/
|
|
25
|
+
detail?: string;
|
|
26
|
+
/** Lifecycle state of this step. Defaults to `'complete'` when omitted. */
|
|
27
|
+
status?: AiStepStatus;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @tag forge-ai-steps
|
|
31
|
+
*
|
|
32
|
+
* @summary Expandable timeline summarizing the steps an agent took while producing a response.
|
|
33
|
+
*
|
|
34
|
+
* @description
|
|
35
|
+
* Collapsed, it shows a step count and a summary of the first couple of step labels. When expanded,
|
|
36
|
+
* it lists each step as a row in a vertical timeline along with its status. Steps carrying a
|
|
37
|
+
* `detail` render as detail cards; steps without one render as a simple labeled row.
|
|
38
|
+
*
|
|
39
|
+
* Labels and details are rendered as sanitized inline markdown, so the agent controls its own
|
|
40
|
+
* emphasis — bold, italic, strikethrough, `code` and links — rather than the component or the
|
|
41
|
+
* consumer prescribing which part of a step matters.
|
|
42
|
+
*
|
|
43
|
+
* This is a presentational primitive: it renders whatever `steps` it is given and knows nothing
|
|
44
|
+
* about tool calls or tool definitions. `forge-ai-tool-steps` is the tool renderer that maps an
|
|
45
|
+
* agent's `displaySteps` tool call onto these properties.
|
|
46
|
+
*
|
|
47
|
+
* While `running` is `true` the timeline stays expanded and the summary row is hidden. When
|
|
48
|
+
* `running` flips back to `false` the timeline auto-collapses, leaving the summary row as the way
|
|
49
|
+
* back in. An element that mounts with `running` already `false` renders expanded, so static usage
|
|
50
|
+
* (a finished run, or a Storybook story) is readable without interaction.
|
|
51
|
+
*
|
|
52
|
+
* @property {AiStep[]} steps - The steps to render as timeline rows
|
|
53
|
+
* @property {boolean} running - Whether the run producing these steps is still in progress
|
|
54
|
+
*/
|
|
55
|
+
export declare class AiStepsComponent extends LitElement {
|
|
56
|
+
#private;
|
|
57
|
+
static styles: CSSResult;
|
|
58
|
+
steps: AiStep[];
|
|
59
|
+
/**
|
|
60
|
+
* Whether the run producing these steps is still in progress. While `true` the timeline is held
|
|
61
|
+
* open and the summary row is hidden; the transition back to `false` collapses the timeline.
|
|
62
|
+
*/
|
|
63
|
+
running: boolean;
|
|
64
|
+
private _expanded;
|
|
65
|
+
willUpdate(changedProperties: PropertyValues<this>): void;
|
|
66
|
+
render(): TemplateResult | typeof nothing;
|
|
67
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { unsafeCSS, LitElement, html, nothing } from "lit";
|
|
2
|
+
import { property, state, customElement } from "lit/decorators.js";
|
|
3
|
+
import { join } from "lit/directives/join.js";
|
|
4
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
5
|
+
import { when } from "lit/directives/when.js";
|
|
6
|
+
import { renderInlineMarkdown } from "../utils/markdown.mjs";
|
|
7
|
+
import styles from "./ai-steps.scss.mjs";
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __typeError = (msg) => {
|
|
11
|
+
throw TypeError(msg);
|
|
12
|
+
};
|
|
13
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
14
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
15
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
16
|
+
if (decorator = decorators[i])
|
|
17
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
18
|
+
if (kind && result) __defProp(target, key, result);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
22
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
23
|
+
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);
|
|
24
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
25
|
+
var _chevronIcon, _rowMarker, _AiStepsComponent_instances, renderInline_fn, summaryLabel_get, toggle_fn, renderNoDetailStep_fn, renderStep_fn, isExpanded_get, steps_get, stepsSummary_get, stepsCountLabel_get;
|
|
26
|
+
const AiStepsComponentTagName = "forge-ai-steps";
|
|
27
|
+
const MAX_DETAIL_LENGTH = 2e3;
|
|
28
|
+
let AiStepsComponent = class extends LitElement {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
__privateAdd(this, _AiStepsComponent_instances);
|
|
32
|
+
this.steps = [];
|
|
33
|
+
this.running = false;
|
|
34
|
+
this._expanded = true;
|
|
35
|
+
__privateAdd(this, _chevronIcon, html`
|
|
36
|
+
<svg
|
|
37
|
+
class="chevron"
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
height="20"
|
|
41
|
+
width="20"
|
|
42
|
+
fill="currentColor"
|
|
43
|
+
aria-hidden="true">
|
|
44
|
+
<path d="M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z" />
|
|
45
|
+
</svg>
|
|
46
|
+
`);
|
|
47
|
+
__privateAdd(this, _rowMarker, html`<span class="row-marker"></span>`);
|
|
48
|
+
}
|
|
49
|
+
willUpdate(changedProperties) {
|
|
50
|
+
if (changedProperties.has("running") && !this.running && changedProperties.get("running") === true) {
|
|
51
|
+
this._expanded = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
render() {
|
|
55
|
+
if (this.steps.length === 0) {
|
|
56
|
+
return nothing;
|
|
57
|
+
}
|
|
58
|
+
return html`
|
|
59
|
+
<div class="steps">
|
|
60
|
+
<div class="steps-count">${__privateGet(this, _AiStepsComponent_instances, stepsCountLabel_get)}</div>
|
|
61
|
+
${__privateGet(this, _AiStepsComponent_instances, stepsSummary_get)} ${__privateGet(this, _AiStepsComponent_instances, steps_get)}
|
|
62
|
+
</div>
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
_chevronIcon = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
_rowMarker = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
_AiStepsComponent_instances = /* @__PURE__ */ new WeakSet();
|
|
69
|
+
renderInline_fn = function(value) {
|
|
70
|
+
return html`${unsafeHTML(renderInlineMarkdown(value))}`;
|
|
71
|
+
};
|
|
72
|
+
summaryLabel_get = function() {
|
|
73
|
+
const stepLabels = this.steps.map((step) => html`<span>${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>`);
|
|
74
|
+
const visibleLabels = stepLabels.slice(0, 2);
|
|
75
|
+
const remaining = stepLabels.length - visibleLabels.length;
|
|
76
|
+
return remaining > 0 ? html`${join(visibleLabels, html`, `)} +${remaining} more` : html`${join(visibleLabels, html`, `)}`;
|
|
77
|
+
};
|
|
78
|
+
toggle_fn = function() {
|
|
79
|
+
this._expanded = !this._expanded;
|
|
80
|
+
};
|
|
81
|
+
renderNoDetailStep_fn = function(step) {
|
|
82
|
+
return html`
|
|
83
|
+
<div class="timeline-row" data-status=${step.status ?? "complete"}>
|
|
84
|
+
<div class="row-header">
|
|
85
|
+
${__privateGet(this, _rowMarker)}
|
|
86
|
+
<span class="row-label">
|
|
87
|
+
<span class="row-name">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
`;
|
|
92
|
+
};
|
|
93
|
+
renderStep_fn = function(step) {
|
|
94
|
+
const detail = step.detail;
|
|
95
|
+
if (!detail) {
|
|
96
|
+
return __privateMethod(this, _AiStepsComponent_instances, renderNoDetailStep_fn).call(this, step);
|
|
97
|
+
}
|
|
98
|
+
const truncated = detail.length > MAX_DETAIL_LENGTH ? `${detail.slice(0, MAX_DETAIL_LENGTH)}… (truncated)` : detail;
|
|
99
|
+
return html`
|
|
100
|
+
<div class="timeline-row" data-status=${step.status ?? "complete"}>
|
|
101
|
+
<div class="row-header">
|
|
102
|
+
${__privateGet(this, _rowMarker)}
|
|
103
|
+
<div class="step-card">
|
|
104
|
+
<span class="step-card-title">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, step.label)}</span>
|
|
105
|
+
<div class="step-card-body">
|
|
106
|
+
<div class="step-card-result">${__privateMethod(this, _AiStepsComponent_instances, renderInline_fn).call(this, truncated)}</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
`;
|
|
112
|
+
};
|
|
113
|
+
isExpanded_get = function() {
|
|
114
|
+
return this.running || this._expanded;
|
|
115
|
+
};
|
|
116
|
+
steps_get = function() {
|
|
117
|
+
return html`
|
|
118
|
+
<div
|
|
119
|
+
id="timeline-content"
|
|
120
|
+
class="timeline ${__privateGet(this, _AiStepsComponent_instances, isExpanded_get) ? "expanded" : ""}"
|
|
121
|
+
aria-hidden=${__privateGet(this, _AiStepsComponent_instances, isExpanded_get) ? "false" : "true"}>
|
|
122
|
+
<div class="timeline-content">${this.steps.map((step) => __privateMethod(this, _AiStepsComponent_instances, renderStep_fn).call(this, step))}</div>
|
|
123
|
+
</div>
|
|
124
|
+
`;
|
|
125
|
+
};
|
|
126
|
+
stepsSummary_get = function() {
|
|
127
|
+
return when(
|
|
128
|
+
!this.running,
|
|
129
|
+
() => html`
|
|
130
|
+
<button
|
|
131
|
+
class="summary"
|
|
132
|
+
type="button"
|
|
133
|
+
aria-expanded=${this._expanded}
|
|
134
|
+
aria-controls="timeline-content"
|
|
135
|
+
@click=${__privateMethod(this, _AiStepsComponent_instances, toggle_fn)}>
|
|
136
|
+
${__privateGet(this, _chevronIcon)}
|
|
137
|
+
<span class="status-text">${__privateGet(this, _AiStepsComponent_instances, summaryLabel_get)}</span>
|
|
138
|
+
<span class="focus-indicator"></span>
|
|
139
|
+
</button>
|
|
140
|
+
`
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
stepsCountLabel_get = function() {
|
|
144
|
+
const count = this.steps.length;
|
|
145
|
+
return `${count} step${count === 1 ? "" : "s"}`;
|
|
146
|
+
};
|
|
147
|
+
AiStepsComponent.styles = unsafeCSS(styles);
|
|
148
|
+
__decorateClass([
|
|
149
|
+
property({ attribute: false })
|
|
150
|
+
], AiStepsComponent.prototype, "steps", 2);
|
|
151
|
+
__decorateClass([
|
|
152
|
+
property({ type: Boolean, reflect: true })
|
|
153
|
+
], AiStepsComponent.prototype, "running", 2);
|
|
154
|
+
__decorateClass([
|
|
155
|
+
state()
|
|
156
|
+
], AiStepsComponent.prototype, "_expanded", 2);
|
|
157
|
+
AiStepsComponent = __decorateClass([
|
|
158
|
+
customElement(AiStepsComponentTagName)
|
|
159
|
+
], AiStepsComponent);
|
|
160
|
+
export {
|
|
161
|
+
AiStepsComponent,
|
|
162
|
+
AiStepsComponentTagName
|
|
163
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const styles = '/* prettier-ignore */\n@keyframes fadein {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes forge-focus-indicator-outward-grow {\n from {\n outline-width: 0;\n }\n to {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-outward-shrink {\n from {\n outline-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-grow {\n from {\n border-width: 0;\n }\n to {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n@keyframes forge-focus-indicator-inward-shrink {\n from {\n border-width: var(--_focus-indicator-active-width);\n }\n}\n:host {\n display: flex;\n flex-direction: column;\n gap: var(--forge-spacing-small, 12px);\n}\n\n.summary {\n display: flex;\n align-items: center;\n gap: var(--forge-spacing-xsmall, 8px);\n padding-block: var(--forge-spacing-xxsmall, 4px);\n padding-inline: var(--forge-spacing-xsmall, 8px);\n border: none;\n background-color: var(--forge-theme-surface-container-minimum, #f5f5f5);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n cursor: pointer;\n text-align: start;\n position: relative;\n outline: none;\n --_state-layer-display: var(--forge-state-layer-display, flex);\n --_state-layer-color: var(--forge-state-layer-color, var(--forge-theme-on-surface, #000000));\n --_state-layer-hover-color: var(--forge-state-layer-hover-color, var(--_state-layer-color));\n --_state-layer-hover-opacity: var(--forge-state-layer-hover-opacity, 0.08);\n --_state-layer-pressed-color: var(--forge-state-layer-pressed-color, var(--_state-layer-color));\n --_state-layer-pressed-opacity: var(--forge-state-layer-pressed-opacity, 0.12);\n --_state-layer-hover-duration: var(--forge-state-layer-hover-duration, 15ms);\n --_state-layer-pressed-duration: var(--forge-state-layer-pressed-duration, 105ms);\n --_state-layer-animation-duration: var(--forge-state-layer-animation-duration, 375ms);\n}\n.summary::before {\n opacity: 0;\n position: absolute;\n backface-visibility: hidden;\n transform: translateZ(0);\n background-color: var(--_state-layer-hover-color);\n inset: 0;\n transition: opacity var(--_state-layer-hover-duration) linear, background-color var(--_state-layer-hover-duration) linear;\n --_state-layer-hover-duration: var(--forge-state-layer-hover-duration, 100ms);\n content: "";\n opacity: 0;\n border-radius: inherit;\n}\n.summary:hover::before {\n background-color: var(--_state-layer-hover-color);\n opacity: var(--_state-layer-hover-opacity);\n}\n.summary:active::before {\n opacity: var(--_state-layer-pressed-opacity);\n transition-duration: var(--_state-layer-pressed-duration);\n --_state-layer-pressed-opacity: var(--forge-state-layer-pressed-opacity, 0.18);\n}\n\n.status-text {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n}\n\n.chevron {\n flex-shrink: 0;\n transform: rotate(-90deg);\n transition: transform var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n\n.summary[aria-expanded=true] .chevron {\n transform: rotate(0deg);\n}\n\n.focus-indicator {\n position: absolute;\n display: none;\n}\n\n:host .summary:focus .focus-indicator {\n --_focus-indicator-display: var(--forge-focus-indicator-display, flex);\n --_focus-indicator-width: var(--forge-focus-indicator-width, var(--forge-border-medium, 2px));\n --_focus-indicator-active-width: var(--forge-focus-indicator-active-width, 6px);\n --_focus-indicator-color: var(--forge-focus-indicator-color, var(--forge-theme-primary, #3f51b5));\n --_focus-indicator-shape: var(--forge-focus-indicator-shape, calc(var(--forge-shape-extra-small, 1px) * var(--forge-shape-factor, 1)));\n --_focus-indicator-duration: var(--forge-focus-indicator-duration, var(--forge-animation-duration-long4, 600ms));\n --_focus-indicator-easing: var(--forge-focus-indicator-easing, var(--forge-animation-easing-emphasized, cubic-bezier(0.2, 0, 0, 1)));\n --_focus-indicator-shape-start-start: var(--forge-focus-indicator-shape-start-start, var(--_focus-indicator-shape));\n --_focus-indicator-shape-start-end: var(--forge-focus-indicator-shape-start-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-end: var(--forge-focus-indicator-shape-end-end, var(--_focus-indicator-shape));\n --_focus-indicator-shape-end-start: var(--forge-focus-indicator-shape-end-start, var(--_focus-indicator-shape));\n --_focus-indicator-outward-offset: var(--forge-focus-indicator-outward-offset, var(--forge-spacing-xxsmall, 4px));\n --_focus-indicator-inward-offset: var(--forge-focus-indicator-inward-offset, 0px);\n --_focus-indicator-offset-block: var(--forge-focus-indicator-offset-block, 0);\n --_focus-indicator-offset-inline: var(--forge-focus-indicator-offset-inline, 0);\n animation-delay: 0s, calc(var(--_focus-indicator-duration) * 0.25);\n animation-duration: calc(var(--_focus-indicator-duration) * 0.25), calc(var(--_focus-indicator-duration) * 0.75);\n animation-timing-function: var(--_focus-indicator-easing);\n box-sizing: border-box;\n color: var(--_focus-indicator-color);\n display: none;\n pointer-events: none;\n position: absolute;\n margin-block: var(--_focus-indicator-offset-block);\n margin-inline: var(--_focus-indicator-offset-inline);\n animation-name: forge-focus-indicator-inward-grow, forge-focus-indicator-inward-shrink;\n border-end-end-radius: calc(var(--_focus-indicator-shape-end-end) - var(--_focus-indicator-inward-offset));\n border-end-start-radius: calc(var(--_focus-indicator-shape-end-start) - var(--_focus-indicator-inward-offset));\n border-start-end-radius: calc(var(--_focus-indicator-shape-start-end) - var(--_focus-indicator-inward-offset));\n border-start-start-radius: calc(var(--_focus-indicator-shape-start-start) - var(--_focus-indicator-inward-offset));\n border: var(--_focus-indicator-width) solid currentColor;\n inset: var(--_focus-indicator-inward-offset);\n}\n:host .summary:focus .focus-indicator::before {\n content: "";\n}\n:host .summary:focus .focus-indicator {\n display: block;\n --forge-focus-indicator-shape: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n}\n\n.steps {\n display: flex;\n flex-direction: column;\n gap: var(--forge-spacing-xsmall, 8px);\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n font-size: var(--forge-ai-markdown-font-size, 0.875rem);\n line-height: var(--forge-ai-markdown-line-height, 1.5rem);\n color: var(--forge-theme-on-surface, #000000);\n overflow-wrap: break-word;\n}\n.steps h1,\n.steps h2,\n.steps h3,\n.steps h4,\n.steps h5,\n.steps h6 {\n margin: 1.75rem 0 var(--forge-spacing-small, 12px);\n}\n.steps strong {\n font-weight: 500;\n}\n.steps a {\n color: var(--forge-theme-primary, #3f51b5);\n}\n.steps hr {\n border: none;\n border-top: 1px solid var(--forge-theme-outline, #e0e0e0);\n}\n.steps code {\n color: var(--forge-theme-on-surface-container-low, #000000);\n background-color: var(--forge-theme-surface-container-low, #ebebeb);\n border-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n padding: var(--forge-spacing-xxxsmall, 2px) var(--forge-spacing-xxsmall, 4px);\n font-size: 0.75em;\n}\n.steps pre {\n border: 1px solid var(--forge-theme-outline, #e0e0e0);\n border-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n margin: 0;\n max-width: 100%;\n overflow-x: auto;\n}\n.steps pre:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: 2px;\n}\n.steps pre code {\n display: block;\n padding: var(--forge-spacing-xsmall, 8px) var(--forge-spacing-small, 12px);\n color: var(--forge-theme-on-surface-container-minimum, #000000);\n background: var(--forge-theme-surface-container-minimum, #f5f5f5);\n white-space: pre;\n}\n.steps [data-scroll-region=table] {\n max-width: 100%;\n overflow-x: auto;\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n margin-block: var(--forge-spacing-medium, 16px);\n}\n.steps [data-scroll-region=table]:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: 2px;\n}\n.steps [data-scroll-region=table] table {\n margin-block: 0;\n}\n.steps table {\n width: 100%;\n border-spacing: 0;\n background-color: var(--forge-theme-surface, #ffffff);\n border-radius: calc(var(--forge-shape-large, 8px) * var(--forge-shape-factor, 1));\n overflow: hidden;\n margin-block: var(--forge-spacing-medium, 16px);\n}\n.steps table thead {\n color: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n font-weight: bold;\n text-align: left;\n font-size: calc(var(--forge-typography-font-size, 1rem) * 0.75);\n box-sizing: border-box;\n}\n.steps table tr {\n height: 36px;\n height: 32px;\n}\n.steps table tr:nth-child(even) {\n background-color: var(--forge-theme-surface-dim, #fafafa);\n}\n.steps table tr:last-child td {\n border-bottom: none;\n}\n.steps table th,\n.steps table td {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-label1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-label1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-font-size-scale, 0.75)));\n font-weight: var(--forge-typography-label1-font-weight, 400);\n line-height: var(--forge-typography-label1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-line-height-scale, 1.25)));\n letter-spacing: var(--forge-typography-label1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-label1-text-transform, inherit);\n text-decoration: var(--forge-typography-label1-text-decoration, inherit);\n padding-block: var(--forge-spacing-xsmall, 8px);\n padding-inline-start: var(--forge-spacing-xsmall, 8px);\n padding-inline-end: var(--forge-spacing-medium, 16px);\n border-bottom-width: 1px;\n border-bottom-style: solid;\n border-bottom-color: var(--forge-theme-outline, #e0e0e0);\n}\n.steps table th {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-label2-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-label2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-font-size-scale, 0.8125)));\n font-weight: var(--forge-typography-label2-font-weight, 400);\n line-height: var(--forge-typography-label2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-label-line-height-scale, 1.25)));\n letter-spacing: var(--forge-typography-label2-letter-spacing, 0.0096153846em);\n text-transform: var(--forge-typography-label2-text-transform, inherit);\n text-decoration: var(--forge-typography-label2-text-decoration, inherit);\n font-weight: 600;\n}\n.steps > p:has(+ ol),\n.steps > p:has(+ ul) {\n margin-block-end: var(--forge-spacing-small, 12px);\n}\n.steps > ul {\n list-style-type: disc;\n padding-inline-start: 1.6875rem;\n}\n.steps > ol {\n padding-inline-start: 1.75rem;\n}\n.steps > ol,\n.steps > ul {\n margin: var(--forge-spacing-xsmall, 8px) 0;\n margin-block-end: var(--forge-spacing-medium, 16px);\n}\n.steps > :first-child {\n margin-block-start: 0;\n}\n.steps > p {\n margin-block-end: var(--forge-spacing-medium, 16px);\n overflow-wrap: anywhere;\n}\n.steps > p:first-child {\n margin-block-start: 0;\n}\n.steps > p:last-child {\n margin-block-end: 0;\n}\n.steps-count {\n color: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.timeline {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows var(--forge-animation-duration-medium2, 300ms) var(--forge-animation-easing-decelerate, cubic-bezier(0, 0, 0, 1));\n}\n.timeline.expanded {\n grid-template-rows: 1fr;\n}\n\n.timeline-content {\n overflow: hidden;\n min-height: 0;\n padding-inline-start: var(--forge-spacing-small, 12px);\n}\n\n.timeline-row {\n position: relative;\n padding-inline-start: var(--forge-spacing-large, 24px);\n padding-block-end: var(--forge-spacing-medium, 16px);\n}\n.timeline-row::before {\n content: "";\n position: absolute;\n inset-block-start: calc((var(--forge-spacing-medium-large, 20px) - var(--forge-spacing-xsmall, 8px)) / 2);\n inset-block-end: calc(-1 * var(--forge-spacing-xsmall, 8px));\n inset-inline-start: calc((var(--forge-spacing-medium-large, 20px) - 1px) / 2);\n width: 1px;\n background-color: var(--forge-theme-outline, #e0e0e0);\n}\n.timeline-row:last-child::before {\n display: none;\n}\n\n.row-header {\n display: flex;\n align-items: flex-start;\n gap: var(--forge-spacing-small, 12px);\n width: 100%;\n min-height: var(--forge-spacing-medium-large, 20px);\n margin-inline-start: calc(-1 * var(--forge-spacing-large, 24px));\n}\n\n.row-marker {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: var(--forge-spacing-medium-large, 20px);\n height: var(--forge-spacing-medium-large, 20px);\n border-radius: calc(var(--forge-shape-round, 50%) * var(--forge-shape-factor, 1));\n}\n.row-marker::before {\n content: "";\n flex-shrink: 0;\n width: var(--forge-spacing-xsmall, 8px);\n height: var(--forge-spacing-xsmall, 8px);\n border-radius: calc(var(--forge-shape-round, 50%) * var(--forge-shape-factor, 1));\n background-color: currentColor;\n position: relative;\n z-index: 1;\n}\n\n.row-label {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n gap: var(--forge-spacing-xxxsmall, 2px);\n}\n\n.row-name {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n display: flex;\n align-items: center;\n gap: var(--forge-spacing-xsmall, 8px);\n line-height: normal;\n font-weight: 500;\n color: var(--forge-theme-text-high, rgba(0, 0, 0, 0.87));\n}\n\n.step-card {\n width: 100%;\n}\n.step-card-title {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n font-weight: 700;\n}\n.step-card-result {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n}\n\n.row-name strong,\n.step-card-title strong {\n font-weight: 700;\n}';
|
|
2
|
+
export {
|
|
3
|
+
styles as default
|
|
4
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ai-steps';
|
|
@@ -218,7 +218,7 @@ highlightJson_fn = function(value) {
|
|
|
218
218
|
return html`${unsafeHTML(highlighted)}`;
|
|
219
219
|
};
|
|
220
220
|
statusMarker_fn = function(toolCall) {
|
|
221
|
-
if (toolCall.status === "error") {
|
|
221
|
+
if (toolCall.status === "error" || toolCall.status === "cancelled") {
|
|
222
222
|
return html`<span class="row-marker row-marker--error">${__privateGet(this, _errorIcon)}</span>`;
|
|
223
223
|
}
|
|
224
224
|
if (isToolCallSettled(toolCall, this.tools?.get(toolCall.name))) {
|
|
@@ -38,6 +38,8 @@ export declare class ChatbotCoreController implements ReactiveController {
|
|
|
38
38
|
getMessages(): ChatMessage[];
|
|
39
39
|
setMessages(messages: ChatMessage[]): void;
|
|
40
40
|
updateMessageContent(id: string, content: string): void;
|
|
41
|
+
/** Tool call ids rendered at or after `index` in {@link messageItems}. */
|
|
42
|
+
getToolCallIdsFrom(index: number): string[];
|
|
41
43
|
removeMessageItemsFrom(index: number): void;
|
|
42
44
|
setResponseFeedback(responseId: string, feedback: {
|
|
43
45
|
type: 'positive' | 'negative';
|
|
@@ -2,6 +2,7 @@ import { SubscriptionManager } from "../ai-chatbot/event-emitter.mjs";
|
|
|
2
2
|
import { FileUploadManager } from "../ai-chatbot/file-upload-manager.mjs";
|
|
3
3
|
import { MessageStateController } from "../ai-chatbot/message-state-controller.mjs";
|
|
4
4
|
import { generateId } from "../ai-chatbot/utils.mjs";
|
|
5
|
+
const UNSETTLED_TOOL_CALL_STATUSES = ["pending", "parsing", "executing"];
|
|
5
6
|
class ChatbotCoreController {
|
|
6
7
|
#host;
|
|
7
8
|
#callbacks;
|
|
@@ -10,6 +11,14 @@ class ChatbotCoreController {
|
|
|
10
11
|
#adapterSubscriptions;
|
|
11
12
|
#adapter;
|
|
12
13
|
#executingToolHandlers = 0;
|
|
14
|
+
/** Tool calls announced by an `mcp-ui-resource` event; they only settle via a tool result. */
|
|
15
|
+
#mcpAppToolCallIds = /* @__PURE__ */ new Set();
|
|
16
|
+
/** Agent tool calls marked complete on their `tool-call` chunk, before any result arrived. */
|
|
17
|
+
#presumedCompleteToolCallIds = /* @__PURE__ */ new Set();
|
|
18
|
+
/** Resolved UI resources whose tool call is not in state yet. */
|
|
19
|
+
#pendingUiResources = /* @__PURE__ */ new Map();
|
|
20
|
+
/** Bumped whenever the thread is cleared or replaced, so in-flight UI resource resolves from before are dropped. */
|
|
21
|
+
#threadGeneration = 0;
|
|
13
22
|
constructor(host, config) {
|
|
14
23
|
this.#host = host;
|
|
15
24
|
this.#callbacks = config.callbacks;
|
|
@@ -83,6 +92,7 @@ class ChatbotCoreController {
|
|
|
83
92
|
this.#adapter.onToolCallEnd(this.#handleToolCallEnd.bind(this)),
|
|
84
93
|
this.#adapter.onToolCall(this.#handleToolCall.bind(this)),
|
|
85
94
|
this.#adapter.onToolCallResult(this.#handleToolCallResult.bind(this)),
|
|
95
|
+
this.#adapter.onMcpUiResource(this.#handleMcpUiResource.bind(this)),
|
|
86
96
|
this.#adapter.onStepStarted(this.#handleStepStarted.bind(this)),
|
|
87
97
|
this.#adapter.onStepFinished(this.#handleStepFinished.bind(this)),
|
|
88
98
|
this.#adapter.onRunFinished(this.#handleRunFinished.bind(this)),
|
|
@@ -115,10 +125,29 @@ class ChatbotCoreController {
|
|
|
115
125
|
#handleRunFinished() {
|
|
116
126
|
this.#tryCompleteResponse();
|
|
117
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* A run ended (finished, aborted, or errored) without delivering a result for an
|
|
130
|
+
* MCP-app tool call: mark it cancelled so the widget receives `tool-cancelled`.
|
|
131
|
+
*/
|
|
132
|
+
#cancelUnsettledMcpAppToolCalls() {
|
|
133
|
+
for (const id of this.#mcpAppToolCallIds) {
|
|
134
|
+
const toolCall = this.#messageStateController.getToolCall(id);
|
|
135
|
+
if (toolCall && UNSETTLED_TOOL_CALL_STATUSES.includes(toolCall.status)) {
|
|
136
|
+
this.#messageStateController.updateToolCallInResponse(id, { status: "cancelled" });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.#mcpAppToolCallIds.clear();
|
|
140
|
+
this.#presumedCompleteToolCallIds.clear();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Finalizes once the run is truly over. An executing client-tool handler or a running
|
|
144
|
+
* adapter means a continuation follows, so widgets stay open for it to settle (§2).
|
|
145
|
+
*/
|
|
118
146
|
#tryCompleteResponse() {
|
|
119
147
|
if (this.#executingToolHandlers > 0 || this.#adapter?.isRunning) {
|
|
120
148
|
return;
|
|
121
149
|
}
|
|
150
|
+
this.#cancelUnsettledMcpAppToolCalls();
|
|
122
151
|
this.#messageStateController.tryFinalizeResponse();
|
|
123
152
|
const messages = this.getMessages();
|
|
124
153
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -134,7 +163,8 @@ class ChatbotCoreController {
|
|
|
134
163
|
args: {},
|
|
135
164
|
argsBuffer: "",
|
|
136
165
|
status: "parsing",
|
|
137
|
-
type: this.tools.has(event.name) ? "client" : "agent"
|
|
166
|
+
type: this.tools.has(event.name) ? "client" : "agent",
|
|
167
|
+
...this.#takePendingUiResource(event.id)
|
|
138
168
|
};
|
|
139
169
|
this.#messageStateController.addToolCallToResponse(toolCall);
|
|
140
170
|
const toolDef = this.tools.get(event.name);
|
|
@@ -195,7 +225,8 @@ class ChatbotCoreController {
|
|
|
195
225
|
name: event.name,
|
|
196
226
|
args: event.args,
|
|
197
227
|
status: "executing",
|
|
198
|
-
type: this.tools.has(event.name) ? "client" : "agent"
|
|
228
|
+
type: this.tools.has(event.name) ? "client" : "agent",
|
|
229
|
+
...this.#takePendingUiResource(event.id)
|
|
199
230
|
};
|
|
200
231
|
this.#messageStateController.addToolCallToResponse(toolCall);
|
|
201
232
|
}
|
|
@@ -211,7 +242,8 @@ class ChatbotCoreController {
|
|
|
211
242
|
} else {
|
|
212
243
|
this.#sendToolResult(event.id, this.#createToolResponse(event.name));
|
|
213
244
|
}
|
|
214
|
-
} else {
|
|
245
|
+
} else if (!this.#mcpAppToolCallIds.has(event.id)) {
|
|
246
|
+
this.#presumedCompleteToolCallIds.add(event.id);
|
|
215
247
|
this.#messageStateController.updateToolCallInResponse(event.id, { status: "complete" });
|
|
216
248
|
}
|
|
217
249
|
}
|
|
@@ -249,6 +281,7 @@ class ChatbotCoreController {
|
|
|
249
281
|
}
|
|
250
282
|
}
|
|
251
283
|
#handleError(event) {
|
|
284
|
+
this.#cancelUnsettledMcpAppToolCalls();
|
|
252
285
|
const errorMessage = {
|
|
253
286
|
id: generateId(),
|
|
254
287
|
role: "assistant",
|
|
@@ -261,6 +294,7 @@ class ChatbotCoreController {
|
|
|
261
294
|
this.#callbacks.onDispatchEvent("forge-ai-chatbot-error", { error: event.message });
|
|
262
295
|
}
|
|
263
296
|
#handleRunAborted() {
|
|
297
|
+
this.#cancelUnsettledMcpAppToolCalls();
|
|
264
298
|
this.#messageStateController.tryFinalizeResponse();
|
|
265
299
|
const id = this.#messageStateController.addClientMessage({ content: "Run cancelled" });
|
|
266
300
|
const abortMessage = this.#messageStateController.getMessage(id);
|
|
@@ -272,8 +306,53 @@ class ChatbotCoreController {
|
|
|
272
306
|
this.#callbacks.onRequestUpdate();
|
|
273
307
|
}
|
|
274
308
|
#handleToolCallResult(event) {
|
|
309
|
+
this.#presumedCompleteToolCallIds.delete(event.toolCallId);
|
|
275
310
|
this.#messageStateController.completeToolCallInResponse(event.toolCallId, event.result, event);
|
|
276
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Resolves and stamps the UI resource. Works whether the event arrives before or after
|
|
314
|
+
* the tool call's chunks, mid-run, after the run ended, or after `setMessages` for
|
|
315
|
+
* tool calls restored from history. A resolve that outlives the thread it started in
|
|
316
|
+
* (cleared/replaced, or the adapter swapped) is dropped.
|
|
317
|
+
*/
|
|
318
|
+
async #handleMcpUiResource(event) {
|
|
319
|
+
const adapter = this.#adapter;
|
|
320
|
+
if (!adapter?.resolveMcpAppResource) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const { toolCallId, resourceUri } = event;
|
|
324
|
+
const generation = this.#threadGeneration;
|
|
325
|
+
this.#mcpAppToolCallIds.add(toolCallId);
|
|
326
|
+
if (this.#presumedCompleteToolCallIds.delete(toolCallId)) {
|
|
327
|
+
this.#messageStateController.updateToolCallInResponse(toolCallId, {
|
|
328
|
+
status: "executing",
|
|
329
|
+
endTimestamp: void 0
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
let uiResource;
|
|
333
|
+
try {
|
|
334
|
+
uiResource = await adapter.resolveMcpAppResource({ resourceUri, toolCallId });
|
|
335
|
+
} catch (error) {
|
|
336
|
+
console.warn(`[forge-ai-mcp-app] Failed to resolve UI resource for tool call ${toolCallId}:`, error);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!uiResource || adapter !== this.#adapter || generation !== this.#threadGeneration) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (this.#messageStateController.getToolCall(toolCallId)) {
|
|
343
|
+
this.#messageStateController.updateToolCallInResponse(toolCallId, { uiResource });
|
|
344
|
+
} else {
|
|
345
|
+
this.#pendingUiResources.set(toolCallId, uiResource);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
#takePendingUiResource(toolCallId) {
|
|
349
|
+
const uiResource = this.#pendingUiResources.get(toolCallId);
|
|
350
|
+
if (!uiResource) {
|
|
351
|
+
return {};
|
|
352
|
+
}
|
|
353
|
+
this.#pendingUiResources.delete(toolCallId);
|
|
354
|
+
return { uiResource };
|
|
355
|
+
}
|
|
277
356
|
async #sendToolResult(toolCallId, result) {
|
|
278
357
|
const toolCall = this.#messageStateController.getToolCall(toolCallId);
|
|
279
358
|
if (!toolCall || !this.#adapter) {
|
|
@@ -366,6 +445,10 @@ class ChatbotCoreController {
|
|
|
366
445
|
return this.#messageStateController.getToolCall(id);
|
|
367
446
|
}
|
|
368
447
|
clearMessages() {
|
|
448
|
+
this.#threadGeneration++;
|
|
449
|
+
this.#mcpAppToolCallIds.clear();
|
|
450
|
+
this.#presumedCompleteToolCallIds.clear();
|
|
451
|
+
this.#pendingUiResources.clear();
|
|
369
452
|
this.#messageStateController.clearMessages();
|
|
370
453
|
this.#fileUploadManager.clear();
|
|
371
454
|
}
|
|
@@ -373,13 +456,25 @@ class ChatbotCoreController {
|
|
|
373
456
|
return this.#messageStateController.getMessages();
|
|
374
457
|
}
|
|
375
458
|
setMessages(messages) {
|
|
459
|
+
this.#threadGeneration++;
|
|
460
|
+
this.#mcpAppToolCallIds.clear();
|
|
461
|
+
this.#presumedCompleteToolCallIds.clear();
|
|
462
|
+
this.#pendingUiResources.clear();
|
|
376
463
|
this.#messageStateController.setMessages(messages);
|
|
377
464
|
}
|
|
378
465
|
updateMessageContent(id, content) {
|
|
379
466
|
this.#messageStateController.updateMessageContent(id, content);
|
|
380
467
|
}
|
|
468
|
+
/** Tool call ids rendered at or after `index` in {@link messageItems}. */
|
|
469
|
+
getToolCallIdsFrom(index) {
|
|
470
|
+
return this.#messageStateController.getToolCallIdsFrom(index);
|
|
471
|
+
}
|
|
381
472
|
removeMessageItemsFrom(index) {
|
|
382
|
-
this.#messageStateController.removeMessageItemsFrom(index)
|
|
473
|
+
for (const id of this.#messageStateController.removeMessageItemsFrom(index)) {
|
|
474
|
+
this.#mcpAppToolCallIds.delete(id);
|
|
475
|
+
this.#presumedCompleteToolCallIds.delete(id);
|
|
476
|
+
this.#pendingUiResources.delete(id);
|
|
477
|
+
}
|
|
383
478
|
}
|
|
384
479
|
setResponseFeedback(responseId, feedback) {
|
|
385
480
|
this.#messageStateController.setResponseFeedback(responseId, feedback);
|
|
@@ -10,6 +10,7 @@ export interface ResizeControllerOptions {
|
|
|
10
10
|
onResize?: (width: number) => void;
|
|
11
11
|
onResizeStart?: () => void;
|
|
12
12
|
onResizeEnd?: () => void;
|
|
13
|
+
onCommit?: (width: number) => void;
|
|
13
14
|
}
|
|
14
15
|
export declare class ResizeController implements ReactiveController {
|
|
15
16
|
#private;
|
|
@@ -36,9 +36,10 @@ class ResizeController {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
event.preventDefault();
|
|
39
|
-
const
|
|
40
|
-
this.#currentWidth = this.#constrainWidth(
|
|
39
|
+
const previousWidth = this.#currentWidth;
|
|
40
|
+
this.#currentWidth = this.#constrainWidth(previousWidth + delta);
|
|
41
41
|
this.#applyWidth();
|
|
42
|
+
this.#commitIfChanged(previousWidth);
|
|
42
43
|
};
|
|
43
44
|
this.#handlePointerMove = (event) => {
|
|
44
45
|
if (!this.#isResizing) {
|
|
@@ -62,6 +63,7 @@ class ResizeController {
|
|
|
62
63
|
this.#isResizing = false;
|
|
63
64
|
this.#host.requestUpdate();
|
|
64
65
|
this.#options.onResizeEnd();
|
|
66
|
+
this.#commitIfChanged(this.#dragStartWidth);
|
|
65
67
|
this.#removeResizeListeners();
|
|
66
68
|
};
|
|
67
69
|
this.#host = host;
|
|
@@ -76,6 +78,8 @@ class ResizeController {
|
|
|
76
78
|
},
|
|
77
79
|
onResizeEnd: () => {
|
|
78
80
|
},
|
|
81
|
+
onCommit: () => {
|
|
82
|
+
},
|
|
79
83
|
...options
|
|
80
84
|
};
|
|
81
85
|
this.#currentWidth = this.#options.defaultWidth;
|
|
@@ -108,6 +112,11 @@ class ResizeController {
|
|
|
108
112
|
}
|
|
109
113
|
#handlePointerMove;
|
|
110
114
|
#handlePointerUp;
|
|
115
|
+
#commitIfChanged(previousWidth) {
|
|
116
|
+
if (this.#currentWidth !== previousWidth) {
|
|
117
|
+
this.#options.onCommit(this.#currentWidth);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
111
120
|
#constrainWidth(width) {
|
|
112
121
|
const availableWidth = window.innerWidth - RESIZE_SCREEN_BUFFER;
|
|
113
122
|
const effectiveMaxWidth = Math.min(this.#options.maxWidth, availableWidth);
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export * from './ai-agent-info';
|
|
|
5
5
|
export * from './ai-agent-selector';
|
|
6
6
|
export * from './ai-tool-call-indicator';
|
|
7
7
|
export * from './ai-artifact';
|
|
8
|
+
export * from './ai-artifact-card';
|
|
9
|
+
export * from './ai-artifact-card-group';
|
|
8
10
|
export * from './ai-attachment';
|
|
9
11
|
export * from './ai-button';
|
|
10
12
|
export * from './ai-chain-of-thought';
|
|
@@ -40,6 +42,7 @@ export * from './ai-response-message';
|
|
|
40
42
|
export * from './ai-sidebar';
|
|
41
43
|
export * from './ai-sidebar-chat';
|
|
42
44
|
export * from './ai-slash-command-menu';
|
|
45
|
+
export * from './ai-steps';
|
|
43
46
|
export * from './ai-suggestions';
|
|
44
47
|
export * from './ai-thinking-indicator';
|
|
45
48
|
export * from './ai-threads';
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,8 @@ import { AiAgentInfoComponent, AiAgentInfoComponentTagName } from "./ai-agent-in
|
|
|
5
5
|
import { AiAgentSelectorComponent, AiAgentSelectorComponentTagName } from "./ai-agent-selector/ai-agent-selector.mjs";
|
|
6
6
|
import { AiToolCallIndicatorComponent, AiToolCallIndicatorComponentTagName } from "./ai-tool-call-indicator/ai-tool-call-indicator.mjs";
|
|
7
7
|
import { AiArtifactComponent, AiArtifactComponentTagName } from "./ai-artifact/ai-artifact.mjs";
|
|
8
|
+
import { AiArtifactCardComponent, AiArtifactCardComponentTagName } from "./ai-artifact-card/ai-artifact-card.mjs";
|
|
9
|
+
import { AiArtifactCardGroupComponent, AiArtifactCardGroupComponentTagName } from "./ai-artifact-card-group/ai-artifact-card-group.mjs";
|
|
8
10
|
import { AiAttachmentComponent, AiAttachmentComponentTagName } from "./ai-attachment/ai-attachment.mjs";
|
|
9
11
|
import { AiButtonComponent, AiButtonComponentTagName } from "./ai-button/ai-button.mjs";
|
|
10
12
|
import { AiChainOfThoughtComponent, AiChainOfThoughtComponentTagName } from "./ai-chain-of-thought/ai-chain-of-thought.mjs";
|
|
@@ -61,6 +63,7 @@ import { AiResponseMessageComponent, AiResponseMessageComponentTagName } from ".
|
|
|
61
63
|
import { AiSidebarComponent, AiSidebarComponentTagName } from "./ai-sidebar/ai-sidebar.mjs";
|
|
62
64
|
import { AiSidebarChatComponent, AiSidebarChatComponentTagName } from "./ai-sidebar-chat/ai-sidebar-chat.mjs";
|
|
63
65
|
import { AiSlashCommandMenuComponent, AiSlashCommandMenuComponentTagName } from "./ai-slash-command-menu/ai-slash-command-menu.mjs";
|
|
66
|
+
import { AiStepsComponent, AiStepsComponentTagName } from "./ai-steps/ai-steps.mjs";
|
|
64
67
|
import { AiSuggestionsComponent, AiSuggestionsComponentTagName } from "./ai-suggestions/ai-suggestions.mjs";
|
|
65
68
|
import { AiThinkingIndicatorComponent, AiThinkingIndicatorComponentTagName } from "./ai-thinking-indicator/ai-thinking-indicator.mjs";
|
|
66
69
|
import { AiThreadsComponent, AiThreadsComponentTagName } from "./ai-threads/ai-threads.mjs";
|
|
@@ -77,6 +80,10 @@ export {
|
|
|
77
80
|
AiAgentInfoComponentTagName,
|
|
78
81
|
AiAgentSelectorComponent,
|
|
79
82
|
AiAgentSelectorComponentTagName,
|
|
83
|
+
AiArtifactCardComponent,
|
|
84
|
+
AiArtifactCardComponentTagName,
|
|
85
|
+
AiArtifactCardGroupComponent,
|
|
86
|
+
AiArtifactCardGroupComponentTagName,
|
|
80
87
|
AiArtifactComponent,
|
|
81
88
|
AiArtifactComponentTagName,
|
|
82
89
|
AiAssistantResponseComponent,
|
|
@@ -152,6 +159,8 @@ export {
|
|
|
152
159
|
AiSidebarComponentTagName,
|
|
153
160
|
AiSlashCommandMenuComponent,
|
|
154
161
|
AiSlashCommandMenuComponentTagName,
|
|
162
|
+
AiStepsComponent,
|
|
163
|
+
AiStepsComponentTagName,
|
|
155
164
|
AiSuggestionsComponent,
|
|
156
165
|
AiSuggestionsComponentTagName,
|
|
157
166
|
AiThinkingIndicatorComponent,
|