import { NgComponentOutlet, AsyncPipe } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, inject, input, computed, output, signal, viewChildren, ChangeDetectionStrategy, Component, Injector, Input } from '@angular/core';
import { GainsightService, DatePipe, ModalService, AlertService, C8Y_PLUGIN_CONTEXT_PATH, AppStateService, Status, LoadingComponent, EmptyStateComponent, C8yTranslateDirective, GuideDocsComponent, GuideHrefDirective, MarkdownToHtmlPipe, C8yTranslatePipe, NumberPipe, ContextRouteService } from '@c8y/ngx-components';
import { TranslateService } from '@ngx-translate/core';
import { AIService, defaultPruneMessagesForAgent } from '@c8y/ngx-components/ai';
import { gettext } from '@c8y/ngx-components/gettext';
import { AiChatAssistantMessageComponent, AiChatComponent, AiChatSuggestionComponent, AiChatMessageComponent, AiChatMessageActionComponent } from '@c8y/ngx-components/ai/ai-chat';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import * as i1 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { WidgetConfigService, WidgetConfigFeedbackComponent } from '@c8y/ngx-components/context-dashboard';
import { of, isObservable } from 'rxjs';

/** A stateless service used by agent-chat for managing chat history. Not public. */
class ChatHistoryService {
    /**
     * Returns a message pruner function suitable for passing to `save()`, configured by the given options.
     *
     * The returned function:
     * - Limits the total number of messages kept (if `config.maxMessages` is set)
     * - Strips transient streaming parts (`tool-input-streaming`, `tool-executing`) from all assistant messages
     * - Strips `reasoning` and `tool-result` parts from assistant messages older than the 10 most recent
     * - Strips `tool-result` parts whose `toolName` is in `config.transientToolNames`
     *
     * @param config Optional configuration
     */
    static createDefaultSerializationMessagePruner(config) {
        return (messages) => {
            let msgs = messages;
            // Apply maxMessages limit - keep most recent messages
            if (config?.maxMessages && msgs.length > config.maxMessages) {
                msgs = msgs.slice(-config.maxMessages);
            }
            const transientToolNames = new Set(config?.transientToolNames ?? []);
            // NB: in future we could use tool metadata from the MCP servers to identify transient tools automatically
            // Only keep tool results and reasoning for the most recent N messages
            const recentMessageStartIndex = msgs.length - Math.min(10, msgs.length);
            return msgs.map((msg, msgIndex) => {
                if (msg.role !== 'assistant') {
                    return msg;
                }
                const isRecentMessage = msgIndex >= recentMessageStartIndex;
                const filteredContent = msg.content.filter((part) => {
                    // Always strip transient streaming states
                    if (part.type === 'tool-input-streaming' || part.type === 'tool-executing') {
                        return false;
                    }
                    if (!isRecentMessage) {
                        // For older messages, only keep text and step-start
                        return part.type === 'text' || part.type === 'step-start';
                    }
                    // For recent messages, additionally filter transient tool results
                    if (part.type === 'tool-result') {
                        return !transientToolNames.has(part.toolName);
                    }
                    return true;
                });
                return { ...msg, content: filteredContent };
            });
        };
    }
    /**
     * Save messages and suggestions to an opaque, versioned, JSON-serializable snapshot for persistence.
     *
     * By default, uses `createDefaultSerializationMessagePruner()` to strip transient content and limit
     * the size of the serialized history. Pass a custom `pruneMessages` function to override this behaviour,
     * for example to configure `maxMessages` or `transientToolNames`, or pass `msgs => msgs` to disable pruning.
     *
     * @param messages The current conversation messages to save
     * @param suggestions The current suggestions to include in the snapshot
     * @param pruneMessages Optional function to prune/transform messages before serialization. If not specified, createDefaultSerializationMessagePruner is used.
     */
    save(messages, suggestions, pruneMessages) {
        const prune = pruneMessages ?? ChatHistoryService.createDefaultSerializationMessagePruner();
        const msgs = prune(messages);
        const history = {
            messages: msgs.map(msg => {
                if (msg.role === 'assistant') {
                    return {
                        role: msg.role,
                        // Note: AIMessagePart[] cannot be statically checked as JSON-safe because
                        // ToolCallPart.output is typed as `unknown`. Serializability is enforced at
                        // the boundary by the outer `as ChatHistory` (= JsonValue) cast.
                        content: msg.content,
                        ...(msg.timestamp ? { timestamp: msg.timestamp } : {})
                    };
                }
                return {
                    role: msg.role,
                    content: msg.content,
                    ...('timestamp' in msg && msg.timestamp ? { timestamp: msg.timestamp } : {})
                };
            }),
            suggestions,
            chatHistoryVersion: 2
        };
        return history;
    }
    /**
     * Restore a previously saved chat history snapshot.
     *
     * Validates the snapshot format and returns the messages and suggestions.
     * Throws an error if the snapshot is invalid or the version is not supported.
     * For a short time, version 1 snapshots are automatically migrated to the current format.
     *
     * @param history A snapshot previously returned by `save()`
     */
    restore(history) {
        // Validate history structure and messages
        if (typeof history !== 'object' ||
            history === null ||
            !('messages' in history) ||
            !Array.isArray(history['messages'])) {
            throw new Error('Invalid chat history format');
        }
        const version = history['chatHistoryVersion'];
        // TODO: remove v1 migration after a month or so once stored histories have been migrated
        if (version === 1) {
            return this.restoreV1(history);
        }
        if (version !== 2) {
            throw new Error(`Cannot load chat history - expected version 2 but got ${version}`);
        }
        const messagesArray = history['messages'];
        if (messagesArray.some(msg => {
            if (!msg || typeof msg !== 'object' || !msg.role)
                return true;
            if (msg.role === 'assistant')
                return !Array.isArray(msg.content);
            return typeof msg.content !== 'string';
        })) {
            throw new Error('Invalid message format in chat history');
        }
        const suggestions = Array.isArray(history['suggestions'])
            ? history['suggestions']
            : [];
        return {
            messages: messagesArray,
            suggestions
        };
    }
    restoreV1(history) {
        const messagesArray = history['messages'];
        if (messagesArray.some((msg) => !msg || typeof msg !== 'object' || !msg.role || typeof msg.content !== 'string')) {
            throw new Error('Invalid message format in chat history');
        }
        const messages = messagesArray.map((msg) => {
            if (msg.role !== 'assistant') {
                return { role: msg.role, content: msg.content };
            }
            const parts = [];
            const steps = msg.steps ?? [];
            if (steps.length === 0) {
                // No steps saved — fall back to the v1 string content
                if (msg.content) {
                    parts.push({ type: 'text', text: msg.content });
                }
            }
            else {
                steps.forEach((step, i) => {
                    // step-start is a boundary between steps, never before the first
                    if (i > 0) {
                        parts.push({ type: 'step-start' });
                    }
                    if (step.reasoning) {
                        parts.push({ type: 'reasoning', text: step.reasoning });
                    }
                    if (step.text) {
                        parts.push({ type: 'text', text: step.text });
                    }
                    for (const toolResult of step.toolResults ?? []) {
                        parts.push(toolResult);
                    }
                });
            }
            return {
                role: 'assistant',
                content: parts,
                ...(msg.timestamp ? { timestamp: msg.timestamp } : {})
            };
        });
        const suggestions = Array.isArray(history['suggestions'])
            ? history['suggestions']
            : [];
        return { messages, suggestions };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ChatHistoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ChatHistoryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ChatHistoryService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

/** A service used to send product experience data to GainSight PX. Not public. */
class UserAnalyticsService {
    constructor() {
        this.gainsightService = inject(GainsightService);
        this.maxAnalyticsMessageLength = 4000;
        /** If needed we could set this to false in production to only include hashes of the messages,
         * if there's a decision that customer data shouldn't be uploaded to GainSight. */
        this.includeCustomerSensitiveDataInAnalytics = true;
    }
    /** Create a dictionary of context from the agent-chat component that is useful to include in all GainSight messages */
    async getAnalyticsMetadataContext(agentName, agentDefinition, userAnalyticsContext, model, systemPrompt) {
        let systemPromptHash = undefined;
        if (systemPrompt) {
            systemPromptHash = await this._computeHash(systemPrompt.map(s => s.text).join('\n\n'));
        }
        else if (agentDefinition && agentDefinition.definition) {
            // Fall back to getting it from the agent definition (if provided)
            systemPromptHash = await this._computeHash(agentDefinition.definition.agent.system);
        }
        return {
            // User-defined context first
            ...(userAnalyticsContext || {}),
            model: model,
            agent: agentName,
            // Since we don't currently have any other version indicator available, a hash of the system prompt is useful to correlate feedback with the exact prompt used
            systemPromptHash: systemPromptHash
        };
    }
    /** Compute SHA-256 hash of a string for correlation purposes (assumes a secure context where crypto.subtle.digest is available) */
    async _computeHash(message) {
        const encoder = new TextEncoder();
        const data = encoder.encode(message);
        const hashBuffer = await crypto.subtle.digest('SHA-256', data);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
    /** Summarize message metrics for GainSight analytics (without including any proprietary data) */
    async _summarizeMessagesForGainSight(userMessage, assistantMessage, detailed = false) {
        // NB: Do NOT send any message content to GainSight servers because that's proprietary/personal customer-owned data that we must protect
        // except when includeCustomerSensitiveDataInAnalytics is set;
        // include hashes of messages so we can always correlate with the history stored on our own servers if available
        const allTextParts = assistantMessage.content
            .filter((p) => p.type === 'text')
            .map(p => p.text);
        const allText = allTextParts.join('\n---\n');
        // If the full text is very long, prioritize the final step's text to keep analytics payloads under the limit
        const lastStepStartIdx = assistantMessage.content.reduce((last, p, i) => (p.type === 'step-start' ? i : last), 0);
        const finalStepText = assistantMessage.content
            .slice(lastStepStartIdx + 1)
            .filter((p) => p.type === 'text')
            .map(p => p.text)
            .join('\n---\n');
        const allAssistantText = allText.length > this.maxAnalyticsMessageLength ? finalStepText : allText;
        // Collect all unique tool names from tool-result parts
        const toolResultParts = assistantMessage.content.filter((p) => p.type === 'tool-result');
        const toolNames = toolResultParts
            .map(tr => tr.toolName)
            .filter((name, index, self) => self.indexOf(name) === index);
        return {
            assistantMessageTimestamp: assistantMessage.timestamp,
            // Track how long we're spending waiting for the AI
            assistantDurationSecs: assistantMessage.timestamp && userMessage.timestamp
                ? (new Date(assistantMessage.timestamp).getTime() -
                    new Date(userMessage.timestamp).getTime()) /
                    1000
                : undefined,
            assistantMessageHash: await this._computeHash(allAssistantText),
            assistantMessage: this.includeCustomerSensitiveDataInAnalytics ? allAssistantText : undefined,
            // although harder to read, it's useful to include the full message including tool calls and steps the feedback is due to bugs etc
            assistantMessageJSON: this.includeCustomerSensitiveDataInAnalytics && detailed
                ? JSON.stringify(assistantMessage)
                : undefined,
            userMessageHash: userMessage ? await this._computeHash(userMessage.content) : '',
            userMessage: this.includeCustomerSensitiveDataInAnalytics ? userMessage.content : undefined,
            // nb: toolCalls here refers to completed calls (i.e. toolResults), we shouldn't have any calls still in progress at this point
            assistantMessageSteps: assistantMessage.content.filter(p => p.type === 'step-start').length,
            assistantMessageToolCallsCount: toolResultParts.length,
            assistantMessageToolNames: toolNames.length === 0 ? undefined : toolNames.join(','),
            assistantMessageLines: allAssistantText.split('\n').length,
            userMessageLength: userMessage.content.length,
            userMessageLines: userMessage.content.split('\n').length
        };
    }
    async sendAssistantMessageComplete(assistantMessage, allMessages, analyticsMetadataContext) {
        // Can't use lastIndexOf(assistantMessage) here (as we for for feedback) because allMessages may not yet have been updated with this assistantMessage item
        const userMessage = [...allMessages].reverse().find(m => m.role === 'user');
        if (!userMessage) {
            console.warn('No user message found for assistant message, skipping analytics event');
            return;
        }
        this.gainsightService.triggerEvent('ai.agent.message', {
            messageCount: allMessages.length,
            ...(await this._summarizeMessagesForGainSight(userMessage, assistantMessage)),
            ...analyticsMetadataContext
        });
    }
    /** Sent when there is a streaming error from the agent */
    async sendAssistantMessageError(error, allMessages, analyticsMetadataContext) {
        this.gainsightService.triggerEvent('ai.agent.error', {
            errorMessage: error,
            messageCount: allMessages.length,
            ...analyticsMetadataContext
        });
    }
    /** Sent when the agent is unavailable, for example because the microservice is not present or has insufficient permissions.
     * This is useful to find out what kinds of problems users are mostly having to get access.
     */
    async sendAgentUnavailable(errorType) {
        this.gainsightService.triggerEvent('ai.agent.unavailable', {
            errorType: errorType
        });
    }
    /** Rates an AI message as positive/negative, sending the feedback to GainSight */
    async sendFeedbackOnAssistantMessage(assistantMessage, allMessages, positive, analyticsMetadataContext) {
        const userMessage = allMessages[allMessages.lastIndexOf(assistantMessage) - 1];
        const detailedFeedback = !positive;
        this.gainsightService.triggerEvent('ai.agent.feedback', {
            positive,
            messageCount: allMessages.length,
            ...(await this._summarizeMessagesForGainSight(userMessage, assistantMessage, detailedFeedback)),
            ...analyticsMetadataContext
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: UserAnalyticsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: UserAnalyticsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: UserAnalyticsService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }] });

class AgentChatComponent {
    constructor() {
        this.translateService = inject(TranslateService);
        this.datePipe = inject(DatePipe);
        this.modalService = inject(ModalService);
        /** Debug logging function for AI messages. Can be enabled at runtime with localStorage.setItem('c8y-debug-log.agent-chat', 'true'); */
        this.debugLog = (category => {
            const isDebugEnabled = localStorage.getItem('c8y-debug-log.' + category) === 'true';
            return (message, ...args) => isDebugEnabled && console.debug(`[${category} DEBUG] ${message}`, ...args);
        })('agent-chat');
        /**
         * Identifies which AI agent from the AI Agent Manager service will be used by this component.
         *
         * Where possible, provide a `ClientAgentDefinition` rather than just a name.
         *
         * It is recommended to define a `toolCallConfig` in the same place you define `ClientAgentDefinition`,
         * and to use the keys from `assistantMessageDisplayConfig.toolCallConfig`
         * in the `ClientAgentDefinition` tools list.
         */
        this.agent = input.required(...(ngDevMode ? [{ debugName: "agent" }] : []));
        /**
         * Clickable suggestions buttons to display just above the chat, for likely next user actions.
         * These may be hardcoded or be set by the AI agent (e.g. in a tool call).
         * This input should be `undefined` to indicate that there are no new suggestions yet, and that any restored from the chat history should be used instead.
         * You can use the `isWelcoming` output to show different suggestions while the initial welcome message is displayed.
         */
        this.suggestions = input([], ...(ngDevMode ? [{ debugName: "suggestions" }] : []));
        /** ChatConfig to customize the ai-chat component's configuration if desired. */
        this.chatConfig = input(...(ngDevMode ? [undefined, { debugName: "chatConfig" }] : []));
        /** Template for customizing the welcome view using `ng-template`. */
        this.welcomeTemplate = input(...(ngDevMode ? [undefined, { debugName: "welcomeTemplate" }] : []));
        /** Indicates whether the component has zero messages and is showing the welcome page. */
        this.isWelcoming = computed(() => this.messages().length === 0, ...(ngDevMode ? [{ debugName: "isWelcoming" }] : []));
        /** Configures whether this component should automatically create the provided `agent: ClientAgentDefinition`
         * if an agent of that name doesn't already exist. This is an alternative to the usual mechanism of configuring
         * agents using a `.agent.c8y.ts` file.
         */
        this.autoCreateAgents = input(false, ...(ngDevMode ? [{ debugName: "autoCreateAgents" }] : []));
        /** Variables to pass to the AI agent for placeholders specified in the agent's system prompt. */
        this.variables = input({}, ...(ngDevMode ? [{ debugName: "variables" }] : []));
        /**
         * Allows overriding the component used to render messages from the AI assistant, if the default
         * AiChatAssistantMessageComponent does not do what you need.
         *
         * The component must accept a single input `assistantMessageContext: AssistantMessageContext`.
         * This object contains all data needed to render the assistant message and is forward-compatible with future additions.
         *
         * Example usage:
         *   <c8y-agent-chat [assistantMessageComponent]="MyCustomAssistantMessageComponent"></c8y-agent-chat>
         */
        this.assistantMessageComponent = input(AiChatAssistantMessageComponent, ...(ngDevMode ? [{ debugName: "assistantMessageComponent" }] : []));
        /**
         * Configuration passed to `AiChatAssistantMessageComponent` for controlling how messages from the assistant
         * are rendered, including tool calls.
         *
         * If you are performing any tool calls it is recommended to configure the `toolCallConfig`
         * to provide localized display names for the tools used in your `AgentDefinitionConfig`.
         */
        this.assistantMessageDisplayConfig = input({}, ...(ngDevMode ? [{ debugName: "assistantMessageDisplayConfig" }] : []));
        /**
         * Optional function to preprocess messages from the assistant before they are displayed, for example if you need to remove special
         * tagged sections from the content to move into simulated tool calls.
         *
         * This function may be called many times for each message, as more steps, tool calls and content are streamed from the agent.
         *
         * If you modify any part of the message other than the `changedPart`, you should replace it with a new shallow copy of the object so
         * that change detection works correctly. This is not necessary for changedPart.
         *
         * @param message The message from the AI assistant.
         * @param changedPart The part of the message that has changed, which can be modified in-place if desired.
         */
        this.preprocessAgentMessage = input(undefined, ...(ngDevMode ? [{ debugName: "preprocessAgentMessage" }] : []));
        /** Optional function to override how the message history is prepared and compacted ready for sending with
         * agent requests. This overrides the default behaviour from `defaultPruneMessagesForAgent`.
         *
         * For example this can be used to remove unnecessary information to minimize tokens, reduce the number of messages,
         * or to make what is sent to the agent different from what is rendered in the UI.
         */
        this.pruneMessagesForAgent = input(undefined, ...(ngDevMode ? [{ debugName: "pruneMessagesForAgent" }] : []));
        /** When true, skips the agent health check on initialization.
         * Use this when the component is used with the test endpoint (snapshot mode) where the agent
         * does not need to exist on the backend. Default: false. */
        this.skipHealthCheck = input(false, ...(ngDevMode ? [{ debugName: "skipHealthCheck" }] : []));
        /** Input that provides a previously-saved chat history snapshot to restore. */
        this.initialChatHistory = input(...(ngDevMode ? [undefined, { debugName: "initialChatHistory" }] : []));
        /** Suggestions that were restored from the initial chat history; used if `suggestions` input is not yet set. */
        this._restoredSuggestions = [];
        /**
         * An optional function that returns a message sent in AI agent requests just before each new user message to provide context or "grounding" to
         * anchor the AI's response - such as the current state of the document the AI is helping with.
         *
         * Typically this string would begin with a special prefix (e.g. "[STATE_SNAPSHOT]") that is described by the system prompt.
         *
         * This message does not form part of the chat history, to avoid confusing the AI agent with older grounding state.
         * It is injected into the message history just before the latest user message as per standard recommendations,
         * since AI gives more attention to the recent content.
         */
        this.groundingContextProvider = input(...(ngDevMode ? [undefined, { debugName: "groundingContextProvider" }] : []));
        /**
         * Provides application-defined fields to enrich the user feedback tracking data for this component.
         *
         * Do not include Intellectual Property or Personal Identifiable Information in this object, as it may
         * be included in feedback data sent to third-party services.
         */
        this.userAnalyticsContext = input(...(ngDevMode ? [undefined, { debugName: "userAnalyticsContext" }] : []));
        this.onMessageFinish = output();
        /**
         * Notified when a tool result is received from the agent.
         *
         * This is called before it is added to the message displayed in the component.
         *
         * Tools with a client/UI implementation may provide `output` for the tool
         * (e.g. data to render in the UI) by calling `addToolOutput`.
         */
        this.onToolResult = output();
        /** Notified when the user presses the positive or negative feedback buttons for an assistant message.*/
        this.onFeedback = output();
        /**
         * Emitted after any meaningful mutation of the messages list (send, finish, reprompt, reload, delete, reset).
         * Allows the parent to react to conversation changes, e.g. to persist the conversation.
         */
        this.onMessagesChange = output();
        this._isLoading = signal(true, ...(ngDevMode ? [{ debugName: "_isLoading" }] : [])); // initially true while we do our initial ping of the agent health - prevents message flashing up before ready
        this.aiAgentManagerApplicationName = gettext('AI Agent Manager');
        /** Stores any error with the agent or backend microservice. This error message is already translated. */
        this._agentHealthError = signal(this.translateService.instant(gettext('Connecting to AI agent…')), ...(ngDevMode ? [{ debugName: "_agentHealthError" }] : []));
        /** Stores the most recent agent health check response, or `undefined` while the initial check is in progress. */
        this._agentHealth = signal(undefined, ...(ngDevMode ? [{ debugName: "_agentHealth" }] : []));
        /** Stores any detailed error messages from the backend. */
        this.agentHealthDetailedMessages = signal(undefined, ...(ngDevMode ? [{ debugName: "agentHealthDetailedMessages" }] : []));
        /**
         * Stores any error from LLM call.
         *
         * If possible this string is translated already or registered with `gettext`.
         * In practice, this may not be translated if it comes from the backend.
         */
        this.agentRequestError = signal(undefined, ...(ngDevMode ? [{ debugName: "agentRequestError" }] : []));
        // A stream of AI messages representing the conversation.
        // NB: this is NOT part of the API of this component and may change in future
        // (e.g. we may improve AIMessage class)
        this.messages = signal([], ...(ngDevMode ? [{ debugName: "messages" }] : []));
        /** Computed cumulative token usage across all messages in the conversation. */
        this.cumulativeUsage = computed(() => {
            const msgs = this.messages();
            return msgs.reduce((acc, msg) => {
                if ('usage' in msg && msg.usage) {
                    acc.inputTokens += msg.usage.inputTokens || 0;
                    acc.outputTokens += msg.usage.outputTokens || 0;
                    acc.totalTokens += msg.usage.totalTokens || 0;
                }
                return acc;
            }, { inputTokens: 0, outputTokens: 0, totalTokens: 0 });
        }, ...(ngDevMode ? [{ debugName: "cumulativeUsage" }] : []));
        /**
         * Computed signal holding the last assistant message, which is the one which may be rapidly changing as we
         * stream it back. This allows us to update the UI reactively without affecting the rest of the message history.
         */
        this.lastAssistantMessageContext = computed(() => {
            const msgs = this.messages();
            const lastMsg = msgs[msgs.length - 1];
            const config = this.assistantMessageDisplayConfig();
            const isLoadingAiResponse = this.isLoadingAiResponse();
            if (lastMsg?.role !== 'assistant')
                return null;
            return {
                message: lastMsg,
                config: config,
                isMessageLoading: isLoadingAiResponse,
                messageDisplayIndex: 0
            };
        }, ...(ngDevMode ? [{ debugName: "lastAssistantMessageContext" }] : []));
        /** If the create agent button should be shown to the user. */
        this.canCreate = false;
        this.prompt = '';
        this.aiService = inject(AIService);
        this.userAnalyticsService = inject(UserAnalyticsService);
        this.alertService = inject(AlertService);
        this.chatHistoryService = inject(ChatHistoryService);
        this.pluginContextPath = inject(C8Y_PLUGIN_CONTEXT_PATH, { optional: true });
        this.appState = inject(AppStateService);
        this.assistantMessageComponents = viewChildren(AiChatAssistantMessageComponent, ...(ngDevMode ? [{ debugName: "assistantMessageComponents" }] : []));
        /**
         * Stores client-supplied tool outputs to override tool results for a given toolCallId in the current assistant message.
         * Cleared at the start of each new message.
         */
        this._clientToolOutputs = {};
    }
    get agentName() {
        const value = this.agent();
        return typeof value === 'string' ? value : value.definition.name;
    }
    get agentDefinition() {
        const value = this.agent();
        if (!value || typeof value === 'string')
            return undefined;
        return value;
    }
    /** Is an AI agent request currently in progress. This can be used to disable UI elements while a reply (which might change the state) is being loaded. */
    get isLoadingAiResponse() {
        return this._isLoading.asReadonly();
    }
    /** Returns the most recent agent health check status such as `ready`, or an error. */
    get agentHealthStatus() {
        return this._agentHealth()?.status || 'loading';
    }
    get currentContextPath() {
        return this.pluginContextPath || this.appState.currentApplication?.value?.contextPath || '';
    }
    async ngOnInit() {
        await this.initializeChat();
    }
    async ngOnChanges(changes) {
        if ((changes['agent'] && this.agent()) || changes['initialChatHistory']) {
            this.resetMessages();
            await this.initializeChat();
        }
    }
    /**
     * Save the current chat history and suggestions to an opaque, versioned, JSON-serializable snapshot of the chat history for persistence.
     *
     * To save space, only recent tool results and reasoning are included.
     * Additional options for compressing the history can be provided using the config parameters.
     *
     * @param pruner Optional function to prune/transform messages before serialization.
     * If not specified, `ChatHistoryService.createDefaultSerializationMessagePruner()` is used.
     */
    saveChatHistory(pruner) {
        return this.chatHistoryService.save(this.messages(), this.suggestions(), pruner);
    }
    /** Sends a message to the AI */
    async sendMessage(message) {
        if (this.isLoadingAiResponse()) {
            // Probably not worth a user-facing error message, but since we assume everywhere that we don't have parallel chats going on at once,
            // don't allow this (e.g. if called from a chat button or other part of the application's UI)
            console.log('Ignoring chat message because previous AI response is still loading');
            return;
        }
        // Collapse the thinking section of the previous assistant message before adding a new one
        const components = this.assistantMessageComponents();
        if (components.length > 0) {
            components[components.length - 1].setThinkingExpanded(false);
        }
        this.messages.set([...this.messages(), message]);
        this.onMessagesChange.emit(this.messages());
        this._isLoading.set(true);
        this.agentRequestError.set(undefined); // Clear any previous stream errors
        this.abortController = new AbortController();
        this._clientToolOutputs = {};
        // Prepare messages for AI service, optionally including grounding message
        const pruneMessagesForAgent = this.pruneMessagesForAgent() || defaultPruneMessagesForAgent;
        const messagesForAI = pruneMessagesForAgent(this.messages());
        const groundingProvider = this.groundingContextProvider();
        if (groundingProvider) {
            // Insert grounding message before the last user message
            const groundingMsg = {
                role: 'user',
                content: groundingProvider()
            };
            messagesForAI.splice(messagesForAI.length - 1, 0, groundingMsg);
        }
        const currentAssistantMessage = {
            role: 'assistant',
            content: []
        };
        this.messages.set([...this.messages(), currentAssistantMessage]);
        // Branch based on agent type: object agents use a single non-streaming request,
        // text agents use SSE streaming
        if (this.agentDefinition?.definition.type === 'object') {
            await this._sendObjectMessage(currentAssistantMessage, messagesForAI);
        }
        else {
            await this._sendStreamMessage(messagesForAI);
        }
    }
    ngOnDestroy() {
        this.cancel();
    }
    async reprompt(userMessage) {
        const index = this.messages().indexOf(userMessage);
        if (index > -1) {
            const formattedTimestamp = userMessage.timestamp
                ? this.datePipe.transform(userMessage.timestamp, 'adaptiveDate')
                : gettext('(unknown time)');
            try {
                await this.modalService.confirm(gettext('Edit this prompt and delete the following messages'), this.translateService.instant(gettext('Are you sure you want to rewrite the conversation by editing your message from {{timestamp}}, and deleting the following {{messageCount}} assistant and user messages?'), {
                    timestamp: formattedTimestamp,
                    messageCount: this.messages().length - index - 1
                }), Status.DANGER, { cancel: gettext('Cancel'), ok: gettext('Delete and replace') });
                this.messages.set(this.messages().slice(0, index));
                this.onMessagesChange.emit(this.messages());
                this.prompt = userMessage.content;
            }
            catch (e) {
                if (!e)
                    return; // User dismissed the dialog, do nothing
                throw e; // Unexpected error, rethrow to propagate
            }
        }
    }
    /** Rates an AI message as positive/negative, sending the feedback to GainSight */
    async rate(assistantMessage, positive) {
        this.userAnalyticsService.sendFeedbackOnAssistantMessage(assistantMessage, this.messages(), positive, await this.getAnalyticsMetadataContext());
        this.onFeedback.emit({
            userMessage: this.messages()[this.messages().lastIndexOf(assistantMessage) - 1],
            assistantMessage,
            feedbackPositive: positive
        });
        this.alertService.success(gettext('Thank you for the feedback!'));
    }
    reload(assistantMessage) {
        const index = this.messages().indexOf(assistantMessage);
        const userMessage = this.messages()[index - 1];
        if (index > -1) {
            this.messages.set(this.messages().slice(0, index - 1));
            this.onMessagesChange.emit(this.messages());
            this.sendMessage({
                role: 'user',
                content: userMessage.content,
                timestamp: new Date().toISOString()
            });
        }
    }
    cancel() {
        const abortMessage = this.translateService.instant(gettext('AI response cancelled.'));
        if (this.abortController) {
            this.abortController.abort(abortMessage);
        }
        if (this.assistantSubscription) {
            this.assistantSubscription.unsubscribe();
        }
        this._isLoading.set(false);
        const lastMessage = this.messages()[this.messages().length - 1];
        if (lastMessage && lastMessage.role === 'assistant' && !lastMessage.content) {
            this.messages.set(this.messages().slice(0, -1));
            this.onMessagesChange.emit(this.messages());
            // if user aborts sending message immediately, before abort controller is instantiated,
            // error message won't be set and we need to do it manually here
            if (!this.agentRequestError()) {
                this.agentRequestError.set(abortMessage);
            }
        }
    }
    /** Clears all messages and cancels any in-flight stream. */
    resetMessages() {
        this.cancel();
        this.messages.set([]);
        this.onMessagesChange.emit(this.messages());
    }
    /** Removes the last user message and all assistant messages that follow it from the conversation. */
    deleteLastExchange() {
        const msgs = this.messages();
        if (msgs.length === 0)
            return;
        // Find the index of the last user message
        let lastUserIndex = -1;
        for (let i = msgs.length - 1; i >= 0; i--) {
            if (msgs[i].role === 'user') {
                lastUserIndex = i;
                break;
            }
        }
        // If no user message found, do nothing
        if (lastUserIndex === -1)
            return;
        // Remove from the last user message onwards
        this.messages.set(msgs.slice(0, lastUserIndex));
        this.onMessagesChange.emit(this.messages());
    }
    /**
     * Overrides the output of a specific tool result (in the current assistant message), including optionally indicating that an error occurred.
     *
     * This can be used to provide output for tool calls that are implemented on the client,
     * and is typically called from `onToolResult`.
     *
     * @param output: A string or JSON-serializable object.
     */
    addToolOutput(toolCallId, output, error) {
        // No validation of the id is performed here; validation is done when applying the override in processAgentMessage
        this.debugLog(`Adding client-side tool ${error ? 'error' : 'output'} for ${toolCallId}: `, output);
        this._clientToolOutputs[toolCallId] = { output: output, error: error ?? false };
    }
    async createAgent() {
        this._isLoading.set(true);
        try {
            if (!this.agentDefinition) {
                throw new Error('No agent definition provided');
            }
            await this.aiService.createOrUpdateAgent(this.agentDefinition.definition);
            this._agentHealthError.set(undefined);
        }
        catch (ex) {
            // TODO: Distinguish permissions errors to provide a more specific error message.
            console.error('Error configuring AI agent:', ex);
            this.alertService.danger(gettext('Failed to configure the agent.'));
            this._agentHealthError.set(this.translateService.instant(gettext('Failed to configure the agent.')));
        }
        this._isLoading.set(false);
    }
    // Private methods go here:
    async initializeChat() {
        this._restoreChatHistory(this.initialChatHistory());
        if (this.skipHealthCheck()) {
            this._agentHealthError.set(undefined);
            this._isLoading.set(false);
        }
        else {
            await this.checkAgentHealth();
        }
    }
    /** Restore chat history from a previously-saved snapshot. Does nothing if the component already has some messages. */
    _restoreChatHistory(history) {
        if (!history)
            return;
        // Ignore updates to history once we have messages
        if (this.messages().length > 0) {
            return;
        }
        const restored = this.chatHistoryService.restore(history);
        this.messages.set(restored.messages);
        this._restoredSuggestions = restored.suggestions;
    }
    async checkAgentHealth() {
        // Set the loading indicator to prevent interactions while we establish whether the agent is useable
        this._isLoading.set(true);
        try {
            const agentHealth = await this.aiService.getAgentHealth(this.agentName, this.currentContextPath);
            this._agentHealth.set(agentHealth);
            this.agentHealthDetailedMessages.set(agentHealth.messages?.join('\n'));
            this._agentHealthError.set(this.handleAgentError(agentHealth));
            if (this.agentDefinition && this.agentDefinition.snapshot) {
                this.createAgent();
            }
        }
        catch (ex) {
            console.warn('Error getting agent manager health:', ex);
            this.userAnalyticsService.sendAgentUnavailable('agent-health-check-failed');
            this._agentHealthError.set(this.translateService.instant(gettext('This tenant does not have the {{agentManager}} microservice. Please contact your administrator.'), { agentManager: this.translateService.instant(this.aiAgentManagerApplicationName) }));
        }
        this._isLoading.set(false);
    }
    async getAnalyticsMetadataContext() {
        return await this.userAnalyticsService.getAnalyticsMetadataContext(this.agentName, this.agentDefinition, this.userAnalyticsContext(), this.model, this.systemPrompt);
    }
    handleAgentError(agentHealth) {
        this.canCreate = false;
        const agentManager = this.translateService.instant(this.aiAgentManagerApplicationName);
        if (agentHealth.status != 'ready')
            this.userAnalyticsService.sendAgentUnavailable(agentHealth.status || 'unknown-error');
        // All of these errors are displayed under a headline banner stating "AI agent is not available"
        // From end-user perspective it makes sense to recommend opening AI Agent Manager to configure their provider/agent
        // (or, implicitly, to ask their admin to do that if they don't have access themselves).
        // If they're the application author they could create a `.agent.c8y.ts` file instead, but we don't want to
        // expose such low-level details in the UI.
        switch (agentHealth.status) {
            case 'ready':
                return undefined;
            case 'missing-provider':
                return this.translateService.instant(gettext('Configure a provider in the {{agentManager}} to get started.'), { agentManager });
            case 'missing-permissions':
                return this.translateService.instant(gettext('Add AI Agent permissions to the current user or role to get started.'));
            case 'missing-microservice':
                return this.translateService.instant(gettext('This tenant does not have the {{agentManager}} microservice. Please contact your administrator.'), { agentManager });
        }
        // Otherwise it's a missing agent definition
        if (agentHealth.canCreate && this.agentDefinition) {
            this.canCreate = true;
            if (this.autoCreateAgents() && this.agentDefinition) {
                this.createAgent();
                return this.translateService.instant(gettext('Creating the "{{agentName}}" AI agent now…'), { agentName: this.agentName });
            }
            // In this case we show a "Create agent" button
            return this.translateService.instant(gettext('Create the "{{agentName}}" AI agent to get started.'), { agentName: this.agentName });
        }
        return this.translateService.instant(gettext('Configure the "{{agentName}}" agent using the {{agentManager}} to get started.'), { agentName: this.agentName, agentManager });
    }
    /**
     * Called as responses are incrementally streamed from the agent.
     * Reads the current assistant message from the end of the messages array, processes the update,
     * and replaces it with a new immutable message object to trigger change detection.
     * @param updatedAssistantMsg contains the latest data received from the agent
     * @param changedPart if provided, indicates which part of the message was changed in this update
     */
    async processAgentMessage(updatedAssistantMsg, changedPart) {
        const currentAssistantMsg = this.messages()[this.messages().length - 1];
        // Must ensure changedPart is a new instance, otherwise change detection can be missed
        if (changedPart && changedPart?.type !== 'response-metadata') {
            const newInstance = { ...changedPart };
            const idx = updatedAssistantMsg.content.findIndex(p => p === changedPart);
            if (idx !== -1) {
                updatedAssistantMsg.content[idx] = newInstance;
            }
            else {
                // Should never happen; if it does we need to know
                console.error('Internal error - changed part not found in updated assistant message content', changedPart, updatedAssistantMsg);
            }
            changedPart = newInstance;
        }
        // Apply preprocessing if provided. Is permitted to modify updatedAssistantMsg.content array
        const preprocess = this.preprocessAgentMessage();
        if (preprocess) {
            updatedAssistantMsg = preprocess(updatedAssistantMsg, changedPart);
        }
        // Invoke tool callback - which may call addToolOutput
        if (changedPart?.type === 'tool-result') {
            this.debugLog(`Received tool result: ${changedPart.toolName}`, changedPart);
            this.onToolResult.emit(changedPart);
        }
        // Create new message object with updated content (immutable update)
        // don't need a deep copy for content itself, and must avoid that so pre-processor can add steps
        const newMsg = {
            ...currentAssistantMsg,
            content: updatedAssistantMsg.content
        };
        // Apply any pending tool output overrides - and check that none of them refers to a non-existent item
        if (Object.keys(this._clientToolOutputs).length > 0) {
            for (const [toolCallId, override] of Object.entries(this._clientToolOutputs)) {
                const toolResult = newMsg.content.find((p) => p.type === 'tool-result' && p.toolCallId === toolCallId);
                if (toolResult) {
                    toolResult.output = override.output;
                    toolResult.error = override.error;
                }
                else {
                    throw new Error(`addToolOutput was called with a toolCallId '${toolCallId}' that does not exist in the current assistant message`);
                }
            }
        }
        if (updatedAssistantMsg.finishReason) {
            this.debugLog(`Received assistant message with ${updatedAssistantMsg.content.filter(p => p.type === 'step-start').length} steps: `, updatedAssistantMsg);
            try {
                this.handleMessageFinish(newMsg, updatedAssistantMsg);
            }
            finally {
                this._isLoading.set(false);
            }
        }
        // Replace streaming message with new immutable instance to trigger change detection
        this.messages.update(msgs => [...msgs.slice(0, -1), newMsg]);
    }
    handleMessageFinish(currentAssistantMsg, updatedAssistantMsg) {
        currentAssistantMsg.timestamp = new Date().toISOString();
        for (const part of currentAssistantMsg.content) {
            if (part.type === 'tool-input-streaming' || part.type === 'tool-executing') {
                part.error = true;
            }
        }
        if (updatedAssistantMsg.finishReason === 'stop') {
            currentAssistantMsg.usage = updatedAssistantMsg.usage;
            this.onMessagesChange.emit(this.messages());
            this.onMessageFinish.emit(currentAssistantMsg);
            void this.getAnalyticsMetadataContext()
                .then(analyticsContext => {
                this.userAnalyticsService.sendAssistantMessageComplete(updatedAssistantMsg, this.messages(), analyticsContext);
            })
                .catch(error => {
                console.error('Error sending analytics:', error);
            });
            if (currentAssistantMsg.content.length === 0) {
                // Should be impossible, but adding this as a debugging aid, as it was seen once by a user
                console.warn('Received finish reason "stop" but assistant message content is empty. This may indicate an issue with the AI service or agent configuration.', currentAssistantMsg);
            }
        }
        else if (updatedAssistantMsg.finishReason === 'length') {
            currentAssistantMsg.usage = updatedAssistantMsg.usage;
            this.onMessagesChange.emit(this.messages());
            this.onMessageFinish.emit(currentAssistantMsg);
            const maxOutputTokens = this.agentDefinition?.definition.agent.maxOutputTokens;
            const message = maxOutputTokens
                ? this.translateService.instant(gettext('The response was truncated because the maximum output tokens limit ({{ maxOutputTokens }}) configured by this application for a single assistant response was reached.'), { maxOutputTokens })
                : this.translateService.instant(gettext('The response was truncated because the maximum output tokens limit configured by this application for a single assistant response was reached.'));
            this.agentRequestError.set(message);
        }
        else if (updatedAssistantMsg.finishReason === 'error') {
            if (currentAssistantMsg.content.length === 0) {
                // Should be impossible, but adding this as a debugging aid, as it was seen once by a user
                console.warn('Received finish reason "error" but assistant message content is empty. This may indicate an issue with the AI service or agent configuration.', currentAssistantMsg);
            }
        }
    }
    /** Handles sending a message to a text-type agent via SSE streaming. */
    async _sendStreamMessage(messagesForAI) {
        const stream = await this.aiService.stream$(this.agentDefinition || this.agentName, messagesForAI, this.variables(), this.abortController, this.currentContextPath);
        if (this.assistantSubscription) {
            this.assistantSubscription.unsubscribe();
        }
        this.assistantSubscription = stream.subscribe({
            next: async (response) => {
                if (response?.changedPart?.type === 'response-metadata') {
                    if (response.changedPart.model) {
                        this.model = response.changedPart.model;
                    }
                    if (response.changedPart.systemPrompt) {
                        this.systemPrompt = response.changedPart.systemPrompt;
                    }
                }
                else {
                    try {
                        await this.processAgentMessage(response.message, response.changedPart);
                    }
                    catch (error) {
                        console.error('Error processing agent message:', error, response.changedPart, response.message);
                        this.cancel();
                        const errorMessage = error instanceof Error
                            ? error.message
                            : gettext('An error occurred while processing the response from the AI agent.');
                        this.agentRequestError.set(errorMessage);
                    }
                }
            },
            complete: () => {
                if (this._isLoading()) {
                    // Should never happen, but worth knowing if it does (indicates bug in processAgentMessage)
                    console.error('AI response stream completed but message loading indicator was not reset');
                }
            },
            error: async (error) => {
                // Currently errors from the AIService are often a big JSON with no human-friendly message anywhere
                // (e.g. {error:{error:{statusCode:400, name:"AI-APICallError", ...}},
                // so we can only show the detail in the console and have to give a non-informative error to the user;
                // but if the backend could add a user-friendly "message" we could render that
                console.error('Error in AI stream:', error);
                const errorMessage = typeof error === 'string'
                    ? error
                    : error?.message || gettext('An error occurred while communicating with the AI agent.');
                this.agentRequestError.set(errorMessage);
                // Remove the empty assistant message that was added
                const messages = this.messages();
                if (messages[messages.length - 1]?.role === 'assistant') {
                    this.messages.set(messages.slice(0, -1));
                    this.onMessagesChange.emit(this.messages());
                }
                this._isLoading.set(false);
                this.userAnalyticsService.sendAssistantMessageError(errorMessage, this.messages(), await this.getAnalyticsMetadataContext());
            }
        });
    }
    /** Handles sending a message to an object-type agent via a single non-streaming request.
     * The response JSON is rendered as a fenced code block in the assistant message.
     */
    async _sendObjectMessage(currentAssistantMessage, messagesForAI) {
        try {
            const response = await this.aiService.callObjectAgent(this.agentDefinition, messagesForAI, this.variables(), this.abortController);
            const jsonContent = JSON.stringify(response.object ?? response, null, 2);
            currentAssistantMessage.content = [{ type: 'object', jsonContent }];
            currentAssistantMessage.timestamp = new Date().toISOString();
            currentAssistantMessage.finishReason = 'stop';
            if (response.totalUsage) {
                currentAssistantMessage.usage = {
                    inputTokens: response.totalUsage.inputTokens,
                    outputTokens: response.totalUsage.outputTokens,
                    totalTokens: response.totalUsage.totalTokens
                };
            }
            this._isLoading.set(false);
            this.messages.set([...this.messages()]);
            this.onMessagesChange.emit(this.messages());
            this.onMessageFinish.emit(currentAssistantMessage);
            void this.getAnalyticsMetadataContext()
                .then(analyticsContext => {
                this.userAnalyticsService.sendAssistantMessageComplete(currentAssistantMessage, this.messages(), analyticsContext);
            })
                .catch(error => {
                console.error('Error sending analytics:', error);
            });
        }
        catch (error) {
            console.error('Error in object agent request:', error);
            const errorMessage = typeof error === 'string'
                ? error
                : error?.message || gettext('An error occurred while communicating with the AI agent.');
            this.agentRequestError.set(errorMessage);
            // Remove the empty assistant message that was added
            const messages = this.messages();
            if (messages[messages.length - 1] === currentAssistantMessage) {
                this.messages.set(messages.slice(0, -1));
            }
            this.onMessagesChange.emit(this.messages());
            this._isLoading.set(false);
        }
        finally {
            this._isLoading.set(false);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AgentChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AgentChatComponent, isStandalone: true, selector: "c8y-agent-chat", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: true, transformFunction: null }, suggestions: { classPropertyName: "suggestions", publicName: "suggestions", isSignal: true, isRequired: false, transformFunction: null }, chatConfig: { classPropertyName: "chatConfig", publicName: "chatConfig", isSignal: true, isRequired: false, transformFunction: null }, welcomeTemplate: { classPropertyName: "welcomeTemplate", publicName: "welcomeTemplate", isSignal: true, isRequired: false, transformFunction: null }, autoCreateAgents: { classPropertyName: "autoCreateAgents", publicName: "autoCreateAgents", isSignal: true, isRequired: false, transformFunction: null }, variables: { classPropertyName: "variables", publicName: "variables", isSignal: true, isRequired: false, transformFunction: null }, assistantMessageComponent: { classPropertyName: "assistantMessageComponent", publicName: "assistantMessageComponent", isSignal: true, isRequired: false, transformFunction: null }, assistantMessageDisplayConfig: { classPropertyName: "assistantMessageDisplayConfig", publicName: "assistantMessageDisplayConfig", isSignal: true, isRequired: false, transformFunction: null }, preprocessAgentMessage: { classPropertyName: "preprocessAgentMessage", publicName: "preprocessAgentMessage", isSignal: true, isRequired: false, transformFunction: null }, pruneMessagesForAgent: { classPropertyName: "pruneMessagesForAgent", publicName: "pruneMessagesForAgent", isSignal: true, isRequired: false, transformFunction: null }, skipHealthCheck: { classPropertyName: "skipHealthCheck", publicName: "skipHealthCheck", isSignal: true, isRequired: false, transformFunction: null }, initialChatHistory: { classPropertyName: "initialChatHistory", publicName: "initialChatHistory", isSignal: true, isRequired: false, transformFunction: null }, groundingContextProvider: { classPropertyName: "groundingContextProvider", publicName: "groundingContextProvider", isSignal: true, isRequired: false, transformFunction: null }, userAnalyticsContext: { classPropertyName: "userAnalyticsContext", publicName: "userAnalyticsContext", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onMessageFinish: "onMessageFinish", onToolResult: "onToolResult", onFeedback: "onFeedback", onMessagesChange: "onMessagesChange" }, viewQueries: [{ propertyName: "assistantMessageComponents", predicate: AiChatAssistantMessageComponent, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "@let agentHealthErrorMsg = _agentHealthError();\n@if (agentHealthErrorMsg) {\n  @if (isLoadingAiResponse()) {\n    <c8y-loading class=\"m-auto\"></c8y-loading>\n  } @else {\n    <c8y-ui-empty-state\n      class=\"m-auto\"\n      [icon]=\"'settings'\"\n      [title]=\"'AI agent is not available.' | translate\"\n    >\n      <span>{{ agentHealthErrorMsg }}</span>\n\n      @if (canCreate) {\n        <div class=\"text-center m-t-16 m-b-16\">\n          <button\n            class=\"btn btn-primary\"\n            (click)=\"createAgent()\"\n          >\n            {{ 'Create agent' | translate }}\n          </button>\n        </div>\n      } @else {\n        <p\n          class=\"text-pre-wrap m-t-8\"\n          data-cy=\"agent-health-detailed-messages\"\n        >\n          <small>{{ agentHealthDetailedMessages() }}</small>\n        </p>\n      }\n\n      <p c8y-guide-docs>\n        <small\n          translate\n          ngNonBindable\n        >\n          Find out more in the\n          <a c8y-guide-href=\"/docs/ai\">user documentation</a>.\n        </small>\n      </p>\n    </c8y-ui-empty-state>\n  }\n}\n\n@if (!agentHealthErrorMsg) {\n  <c8y-ai-chat\n    (onMessage)=\"sendMessage($event)\"\n    [isLoading]=\"isLoadingAiResponse()\"\n    (onCancel)=\"cancel()\"\n    [config]=\"chatConfig() ?? {}\"\n    [prompt]=\"prompt\"\n    [suggestionsTemplate]=\"suggestionsRef\"\n    [welcomeTemplate]=\"welcomeTemplate()\"\n    [cumulativeUsage]=\"chatConfig()?.showCumulativeUsage ? cumulativeUsage() : null\"\n  >\n    @let messages$ = messages();\n    @for (message of messages$; track $index; let i = $index) {\n      <c8y-ai-chat-message [message]=\"message\">\n        @if (message.role !== 'user' && model) {\n          <!-- Visually hidden label included when users copy-paste from the chat, e.g. to report issues. It's quite helpful to know the model. -->\n          <span\n            class=\"hidden-copy-label\"\n            aria-hidden=\"true\"\n          >\n            {{ `(Using model: ${model})` }}</span\n          >\n        }\n\n        @if (message.role === 'user') {\n          <div\n            class=\"message-content\"\n            data-cy=\"user-message-content\"\n            [innerHTML]=\"message.content | markdownToHtml | async\"\n          ></div>\n        } @else if (\n          message.role === 'assistant' && isLoadingAiResponse() && i === messages$.length - 1\n        ) {\n          <div>\n            <!-- Last assistant message uses reactive computed context -->\n            <ng-container\n              [ngComponentOutlet]=\"assistantMessageComponent()\"\n              [ngComponentOutletInputs]=\"{ assistantMessageContext: lastAssistantMessageContext() }\"\n            ></ng-container>\n          </div>\n        } @else {\n          <ng-container\n            [ngComponentOutlet]=\"assistantMessageComponent()\"\n            [ngComponentOutletInputs]=\"{\n              assistantMessageContext: {\n                message: message,\n                config: assistantMessageDisplayConfig(),\n                isMessageLoading: isLoadingAiResponse() && i === messages$.length - 1,\n                messageDisplayIndex: messages$.length - 1 - i\n              }\n            }\"\n          ></ng-container>\n        }\n\n        @let isLastMessage = i === messages$.length - 1;\n\n        @if (message.role === 'user') {\n          <c8y-ai-chat-message-action\n            icon=\"pencil\"\n            [tooltip]=\"'Edit and resend this message' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"reprompt(message)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (\n          message.role === 'assistant' &&\n          i > 0 &&\n          chatConfig()?.showUsagePerMessage &&\n          message.usage\n        ) {\n          <c8y-ai-chat-message-action [custom]=\"true\">\n            <span\n              class=\"tag tag--info m-l-auto\"\n              [tooltip]=\"\n                'Input tokens: {{ input }} \\nOutput tokens: {{ output }}'\n                  | translate\n                    : {\n                        input: message.usage.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n                        output: message.usage.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n                      }\n              \"\n            >\n              {{ 'Tokens used: {{total}}' | translate: { total: message.usage.totalTokens || 0 |\n              c8yNumber: 'floor' : '1.0-0' } }}\n            </span>\n          </c8y-ai-chat-message-action>\n        }\n\n        @if (message.role === 'assistant' && i > 0 && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"thumbs-up\"\n            [tooltip]=\"'This is useful' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"rate(message, true)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (message.role === 'assistant' && i > 0 && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"thumbs-down\"\n            [tooltip]=\"'This is not useful' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"rate(message, false)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (\n          message.role === 'assistant' &&\n          !isLoadingAiResponse() &&\n          chatConfig()?.showDeleteAction &&\n          isLastMessage\n        ) {\n          <c8y-ai-chat-message-action\n            icon=\"delete-bin\"\n            [tooltip]=\"'Delete last exchange' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"deleteLastExchange()\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        <!-- Only allow regenerating the last message, otherwise we could be deleting a lot of useful message history -->\n        @if (message.role === 'assistant' && i > 0 && isLastMessage && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"refresh\"\n            [tooltip]=\"'Regenerate this response' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"reload(message)\"\n          ></c8y-ai-chat-message-action>\n        }\n      </c8y-ai-chat-message>\n    }\n\n    @let agentErrorMsg = agentRequestError();\n    @if (agentErrorMsg) {\n      <c8y-ai-chat-message [message]=\"{ role: 'assistant', content: agentErrorMsg }\">\n        <div\n          class=\"alert alert-danger d-flex a-i-center gap-8\"\n          role=\"alert\"\n        >\n          <!-- Since errors come from the backend the only translation is for the fallback error message supplied by the UI. -->\n          <div class=\"flex-grow text-pre-wrap\">{{ agentErrorMsg | translate }}</div>\n        </div>\n      </c8y-ai-chat-message>\n    }\n\n    <ng-template #suggestionsRef>\n      @if (!isLoadingAiResponse()) {\n        <!-- As soon as we have any suggestions (even empty) these take priority over what we restored from history -->\n        @let activeSuggestions = suggestions() === undefined ? _restoredSuggestions : suggestions();\n        @for (suggestion of activeSuggestions; track $index) {\n          <c8y-ai-chat-suggestion\n            [icon]=\"suggestion.icon || 'c8y-bulb'\"\n            [useAiButtons]=\"true\"\n            [prompt]=\"suggestion.prompt\"\n            [label]=\"suggestion.label ?? suggestion.prompt\"\n            (suggestionClicked)=\"sendMessage($event)\"\n            [disabled]=\"isLoadingAiResponse()\"\n          ></c8y-ai-chat-suggestion>\n        }\n      }\n    </ng-template>\n  </c8y-ai-chat>\n}\n", styles: ["@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fade-in-out{animation:fadeIn .2s ease-in}.fade-in-out.ng-leave-active{animation:fadeOut .2s ease-out}.hidden-copy-label{display:block;font-size:0;line-height:0;-webkit-user-select:text;user-select:text}\n"], dependencies: [{ kind: "component", type: AiChatComponent, selector: "c8y-ai-chat", inputs: ["isLoading", "disabled", "prompt", "suggestionsTemplate", "welcomeTemplate", "cumulativeUsage", "config"], outputs: ["onMessage", "onCancel"] }, { kind: "component", type: AiChatSuggestionComponent, selector: "c8y-ai-chat-suggestion", inputs: ["label", "prompt", "icon", "useAiButtons", "disabled"], outputs: ["suggestionClicked"] }, { kind: "component", type: AiChatMessageComponent, selector: "c8y-ai-chat-message", inputs: ["role", "message"] }, { kind: "component", type: AiChatMessageActionComponent, selector: "c8y-ai-chat-message-action", inputs: ["custom", "disabled", "tooltip", "icon"], outputs: ["actionClicked"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "ngmodule", type: CollapseModule }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: NumberPipe, name: "c8yNumber" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AgentChatComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-agent-chat', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
                        AiChatComponent,
                        AiChatSuggestionComponent,
                        AiChatMessageComponent,
                        AiChatMessageActionComponent,
                        LoadingComponent,
                        MarkdownToHtmlPipe,
                        AsyncPipe,
                        NgComponentOutlet,
                        C8yTranslatePipe,
                        EmptyStateComponent,
                        CollapseModule,
                        C8yTranslateDirective,
                        GuideDocsComponent,
                        GuideHrefDirective,
                        TooltipModule,
                        NumberPipe
                    ], template: "@let agentHealthErrorMsg = _agentHealthError();\n@if (agentHealthErrorMsg) {\n  @if (isLoadingAiResponse()) {\n    <c8y-loading class=\"m-auto\"></c8y-loading>\n  } @else {\n    <c8y-ui-empty-state\n      class=\"m-auto\"\n      [icon]=\"'settings'\"\n      [title]=\"'AI agent is not available.' | translate\"\n    >\n      <span>{{ agentHealthErrorMsg }}</span>\n\n      @if (canCreate) {\n        <div class=\"text-center m-t-16 m-b-16\">\n          <button\n            class=\"btn btn-primary\"\n            (click)=\"createAgent()\"\n          >\n            {{ 'Create agent' | translate }}\n          </button>\n        </div>\n      } @else {\n        <p\n          class=\"text-pre-wrap m-t-8\"\n          data-cy=\"agent-health-detailed-messages\"\n        >\n          <small>{{ agentHealthDetailedMessages() }}</small>\n        </p>\n      }\n\n      <p c8y-guide-docs>\n        <small\n          translate\n          ngNonBindable\n        >\n          Find out more in the\n          <a c8y-guide-href=\"/docs/ai\">user documentation</a>.\n        </small>\n      </p>\n    </c8y-ui-empty-state>\n  }\n}\n\n@if (!agentHealthErrorMsg) {\n  <c8y-ai-chat\n    (onMessage)=\"sendMessage($event)\"\n    [isLoading]=\"isLoadingAiResponse()\"\n    (onCancel)=\"cancel()\"\n    [config]=\"chatConfig() ?? {}\"\n    [prompt]=\"prompt\"\n    [suggestionsTemplate]=\"suggestionsRef\"\n    [welcomeTemplate]=\"welcomeTemplate()\"\n    [cumulativeUsage]=\"chatConfig()?.showCumulativeUsage ? cumulativeUsage() : null\"\n  >\n    @let messages$ = messages();\n    @for (message of messages$; track $index; let i = $index) {\n      <c8y-ai-chat-message [message]=\"message\">\n        @if (message.role !== 'user' && model) {\n          <!-- Visually hidden label included when users copy-paste from the chat, e.g. to report issues. It's quite helpful to know the model. -->\n          <span\n            class=\"hidden-copy-label\"\n            aria-hidden=\"true\"\n          >\n            {{ `(Using model: ${model})` }}</span\n          >\n        }\n\n        @if (message.role === 'user') {\n          <div\n            class=\"message-content\"\n            data-cy=\"user-message-content\"\n            [innerHTML]=\"message.content | markdownToHtml | async\"\n          ></div>\n        } @else if (\n          message.role === 'assistant' && isLoadingAiResponse() && i === messages$.length - 1\n        ) {\n          <div>\n            <!-- Last assistant message uses reactive computed context -->\n            <ng-container\n              [ngComponentOutlet]=\"assistantMessageComponent()\"\n              [ngComponentOutletInputs]=\"{ assistantMessageContext: lastAssistantMessageContext() }\"\n            ></ng-container>\n          </div>\n        } @else {\n          <ng-container\n            [ngComponentOutlet]=\"assistantMessageComponent()\"\n            [ngComponentOutletInputs]=\"{\n              assistantMessageContext: {\n                message: message,\n                config: assistantMessageDisplayConfig(),\n                isMessageLoading: isLoadingAiResponse() && i === messages$.length - 1,\n                messageDisplayIndex: messages$.length - 1 - i\n              }\n            }\"\n          ></ng-container>\n        }\n\n        @let isLastMessage = i === messages$.length - 1;\n\n        @if (message.role === 'user') {\n          <c8y-ai-chat-message-action\n            icon=\"pencil\"\n            [tooltip]=\"'Edit and resend this message' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"reprompt(message)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (\n          message.role === 'assistant' &&\n          i > 0 &&\n          chatConfig()?.showUsagePerMessage &&\n          message.usage\n        ) {\n          <c8y-ai-chat-message-action [custom]=\"true\">\n            <span\n              class=\"tag tag--info m-l-auto\"\n              [tooltip]=\"\n                'Input tokens: {{ input }} \\nOutput tokens: {{ output }}'\n                  | translate\n                    : {\n                        input: message.usage.inputTokens || 0 | c8yNumber: 'floor' : '1.0-0',\n                        output: message.usage.outputTokens || 0 | c8yNumber: 'floor' : '1.0-0'\n                      }\n              \"\n            >\n              {{ 'Tokens used: {{total}}' | translate: { total: message.usage.totalTokens || 0 |\n              c8yNumber: 'floor' : '1.0-0' } }}\n            </span>\n          </c8y-ai-chat-message-action>\n        }\n\n        @if (message.role === 'assistant' && i > 0 && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"thumbs-up\"\n            [tooltip]=\"'This is useful' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"rate(message, true)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (message.role === 'assistant' && i > 0 && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"thumbs-down\"\n            [tooltip]=\"'This is not useful' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"rate(message, false)\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        @if (\n          message.role === 'assistant' &&\n          !isLoadingAiResponse() &&\n          chatConfig()?.showDeleteAction &&\n          isLastMessage\n        ) {\n          <c8y-ai-chat-message-action\n            icon=\"delete-bin\"\n            [tooltip]=\"'Delete last exchange' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"deleteLastExchange()\"\n          ></c8y-ai-chat-message-action>\n        }\n\n        <!-- Only allow regenerating the last message, otherwise we could be deleting a lot of useful message history -->\n        @if (message.role === 'assistant' && i > 0 && isLastMessage && !isLoadingAiResponse()) {\n          <c8y-ai-chat-message-action\n            icon=\"refresh\"\n            [tooltip]=\"'Regenerate this response' | translate\"\n            [disabled]=\"isLoadingAiResponse()\"\n            (click)=\"reload(message)\"\n          ></c8y-ai-chat-message-action>\n        }\n      </c8y-ai-chat-message>\n    }\n\n    @let agentErrorMsg = agentRequestError();\n    @if (agentErrorMsg) {\n      <c8y-ai-chat-message [message]=\"{ role: 'assistant', content: agentErrorMsg }\">\n        <div\n          class=\"alert alert-danger d-flex a-i-center gap-8\"\n          role=\"alert\"\n        >\n          <!-- Since errors come from the backend the only translation is for the fallback error message supplied by the UI. -->\n          <div class=\"flex-grow text-pre-wrap\">{{ agentErrorMsg | translate }}</div>\n        </div>\n      </c8y-ai-chat-message>\n    }\n\n    <ng-template #suggestionsRef>\n      @if (!isLoadingAiResponse()) {\n        <!-- As soon as we have any suggestions (even empty) these take priority over what we restored from history -->\n        @let activeSuggestions = suggestions() === undefined ? _restoredSuggestions : suggestions();\n        @for (suggestion of activeSuggestions; track $index) {\n          <c8y-ai-chat-suggestion\n            [icon]=\"suggestion.icon || 'c8y-bulb'\"\n            [useAiButtons]=\"true\"\n            [prompt]=\"suggestion.prompt\"\n            [label]=\"suggestion.label ?? suggestion.prompt\"\n            (suggestionClicked)=\"sendMessage($event)\"\n            [disabled]=\"isLoadingAiResponse()\"\n          ></c8y-ai-chat-suggestion>\n        }\n      }\n    </ng-template>\n  </c8y-ai-chat>\n}\n", styles: ["@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fade-in-out{animation:fadeIn .2s ease-in}.fade-in-out.ng-leave-active{animation:fadeOut .2s ease-out}.hidden-copy-label{display:block;font-size:0;line-height:0;-webkit-user-select:text;user-select:text}\n"] }]
        }], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], suggestions: [{ type: i0.Input, args: [{ isSignal: true, alias: "suggestions", required: false }] }], chatConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "chatConfig", required: false }] }], welcomeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "welcomeTemplate", required: false }] }], autoCreateAgents: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCreateAgents", required: false }] }], variables: [{ type: i0.Input, args: [{ isSignal: true, alias: "variables", required: false }] }], assistantMessageComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "assistantMessageComponent", required: false }] }], assistantMessageDisplayConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "assistantMessageDisplayConfig", required: false }] }], preprocessAgentMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "preprocessAgentMessage", required: false }] }], pruneMessagesForAgent: [{ type: i0.Input, args: [{ isSignal: true, alias: "pruneMessagesForAgent", required: false }] }], skipHealthCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipHealthCheck", required: false }] }], initialChatHistory: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialChatHistory", required: false }] }], groundingContextProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "groundingContextProvider", required: false }] }], userAnalyticsContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "userAnalyticsContext", required: false }] }], onMessageFinish: [{ type: i0.Output, args: ["onMessageFinish"] }], onToolResult: [{ type: i0.Output, args: ["onToolResult"] }], onFeedback: [{ type: i0.Output, args: ["onFeedback"] }], onMessagesChange: [{ type: i0.Output, args: ["onMessagesChange"] }], assistantMessageComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => AiChatAssistantMessageComponent), { isSignal: true }] }] } });

class WidgetAiChatSectionComponent {
    constructor() {
        this._componentConfig = signal({}, ...(ngDevMode ? [{ debugName: "_componentConfig" }] : []));
        this.suggestions = [];
        /** Provide the specified `contextVariableName` as a variable that can be used in the prompt. */
        this.useContextAsVariable = true;
        this.contextVariableName = 'c8yContext';
        this.variables$ = of({});
        this.injector = inject(Injector);
        this.widgetConfigService = inject(WidgetConfigService);
        this.alertService = inject(AlertService);
        this.contextRouteService = inject(ContextRouteService);
        /**
         * A callback that is invoked when a tool result is returned. The function can return either:
         * - `undefined` or `void`: No action is taken.
         * - an object which is stored to the widget configuration.
         */
        this.onToolResult = () => undefined;
    }
    /**
     * If set, the variables will be loaded async on each round-trip.
     * This is useful if the variables depend on some async data source or if they need to be updated on each round-trip.
     *
     * @example
     * ```ts
     * variables = combineLatest([
     *   this.someService.getData(),
     *   this.anotherService.getOtherData()
     * ]).pipe(
     *   map(([data, otherData]) => ({
     *     myVarOne: data,
     *     myVarTwo: otherData
     *   }))
     * );
     * ```
     */
    set variables(val) {
        if (isObservable(val)) {
            this.variables$ = val;
        }
        else {
            this.variables$ = of(val);
        }
    }
    get variables() {
        return this.variables$;
    }
    /**
     * @ignore
     */
    async ngOnInit() {
        if (!this.variables && this.useContextAsVariable) {
            const { contextData } = this.contextRouteService.activatedContextData;
            this.variables = {
                [this.contextVariableName]: contextData || this.widgetConfigService.currentConfig.device
            };
        }
        if (this.loadComponentConfig) {
            const config = await this.loadComponentConfig(this.injector);
            this._componentConfig.set(config);
        }
    }
    /**
     * Handles the tool result returned from the AI agent.
     * @param tool The tool result returned from the AI agent.
     */
    toolResultHandler(tool) {
        try {
            const parsedResult = this.onToolResult(tool);
            if (!parsedResult) {
                return;
            }
            this.widgetConfigService.updateConfig({
                config: {
                    ...this.widgetConfigService.currentConfig,
                    ...parsedResult
                }
            }, true);
        }
        catch (e) {
            this.alertService.danger(gettext('There was an error processing the AI response.'));
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetAiChatSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: WidgetAiChatSectionComponent, isStandalone: true, selector: "c8y-widget-ai-chat-section", inputs: { loadComponentConfig: "loadComponentConfig", suggestions: "suggestions", agent: "agent", chatConfig: "chatConfig", useContextAsVariable: "useContextAsVariable", contextVariableName: "contextVariableName", variables: "variables", onToolResult: "onToolResult" }, ngImport: i0, template: "<c8y-widget-config-feedback>\n  <div\n    class=\"m-l-4 btn-ai btn-ai-hint btn-sm\"\n    [title]=\"'AI code assistant' | translate\"\n  >\n    <span></span>\n  </div>\n</c8y-widget-config-feedback>\n\n<c8y-agent-chat\n  #agentChat\n  [chatConfig]=\"chatConfig\"\n  [suggestions]=\"agentChat.isWelcoming() ? suggestions : []\"\n  [agent]=\"agent\"\n  [variables]=\"variables | async\"\n  (onToolResult)=\"toolResultHandler($event)\"\n  [preprocessAgentMessage]=\"_componentConfig().preprocessAgentMessage\"\n  [pruneMessagesForAgent]=\"_componentConfig().pruneMessagesForAgent\"\n  [assistantMessageDisplayConfig]=\"_componentConfig().assistantMessageDisplayConfig || {}\"\n></c8y-agent-chat>\n", dependencies: [{ kind: "component", type: AgentChatComponent, selector: "c8y-agent-chat", inputs: ["agent", "suggestions", "chatConfig", "welcomeTemplate", "autoCreateAgents", "variables", "assistantMessageComponent", "assistantMessageDisplayConfig", "preprocessAgentMessage", "pruneMessagesForAgent", "skipHealthCheck", "initialChatHistory", "groundingContextProvider", "userAnalyticsContext"], outputs: ["onMessageFinish", "onToolResult", "onFeedback", "onMessagesChange"] }, { kind: "component", type: WidgetConfigFeedbackComponent, selector: "c8y-widget-config-feedback" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetAiChatSectionComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-widget-ai-chat-section', imports: [AgentChatComponent, C8yTranslatePipe, WidgetConfigFeedbackComponent, AsyncPipe], standalone: true, template: "<c8y-widget-config-feedback>\n  <div\n    class=\"m-l-4 btn-ai btn-ai-hint btn-sm\"\n    [title]=\"'AI code assistant' | translate\"\n  >\n    <span></span>\n  </div>\n</c8y-widget-config-feedback>\n\n<c8y-agent-chat\n  #agentChat\n  [chatConfig]=\"chatConfig\"\n  [suggestions]=\"agentChat.isWelcoming() ? suggestions : []\"\n  [agent]=\"agent\"\n  [variables]=\"variables | async\"\n  (onToolResult)=\"toolResultHandler($event)\"\n  [preprocessAgentMessage]=\"_componentConfig().preprocessAgentMessage\"\n  [pruneMessagesForAgent]=\"_componentConfig().pruneMessagesForAgent\"\n  [assistantMessageDisplayConfig]=\"_componentConfig().assistantMessageDisplayConfig || {}\"\n></c8y-agent-chat>\n" }]
        }], propDecorators: { loadComponentConfig: [{
                type: Input
            }], suggestions: [{
                type: Input
            }], agent: [{
                type: Input
            }], chatConfig: [{
                type: Input
            }], useContextAsVariable: [{
                type: Input
            }], contextVariableName: [{
                type: Input
            }], variables: [{
                type: Input
            }], onToolResult: [{
                type: Input
            }] } });

/**
 * Generated bundle index. Do not edit.
 */

export { AgentChatComponent, WidgetAiChatSectionComponent };
//# sourceMappingURL=c8y-ngx-components-ai-agent-chat.mjs.map
