{"version":3,"file":"c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs","sources":["../../widgets/definitions/html-widget-ai-config/html-widget-agent.definitions.ts","../../widgets/definitions/html-widget-ai-config/ai-html-widget-config.factory.ts","../../widgets/definitions/html-widget-ai-config/index.ts","../../widgets/definitions/html-widget-ai-config/c8y-ngx-components-widgets-definitions-html-widget-ai-config.ts"],"sourcesContent":["import type { ClientAgentDefinition } from '@c8y/ngx-components/ai';\nimport { HTML_AGENT } from '@c8y/ngx-components/ai/agents/html';\nimport { gettext } from '@c8y/ngx-components/gettext';\n\ndeclare const __MODE__: 'development' | 'production' | undefined;\n\n// Treat as \"production\" when run from Jest\nconst mode = (typeof __MODE__ !== 'undefined' ? __MODE__ : 'production') as\n  | 'development'\n  | 'production';\n\nexport const HTML_WIDGET_AGENT_DEFINITIONS: ClientAgentDefinition = {\n  snapshot: mode === 'development',\n  label: gettext('HTML Widget Code assistant'),\n  definition: HTML_AGENT\n};\n","import { inject, Injectable, Injector } from '@angular/core';\nimport { ExtensionFactory, PreviewService } from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n  AIService,\n  AIStreamResponse,\n  WidgetAiChatSectionComponentConfig,\n  ToolCallPart,\n  AIAssistantMessage\n} from '@c8y/ngx-components/ai';\nimport { WidgetAiChatSectionComponent } from '@c8y/ngx-components/ai/agent-chat';\nimport type { WidgetConfigSectionDefinition } from '@c8y/ngx-components/context-dashboard';\nimport { WidgetConfigService } from '@c8y/ngx-components/context-dashboard';\nimport { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions';\nimport { combineLatest, first, from, map, Observable } from 'rxjs';\nimport { HTML_WIDGET_AGENT_DEFINITIONS } from './html-widget-agent.definitions';\nimport type {\n  HtmlWidgetConfig,\n  HtmlWidgetConfigService\n} from '@c8y/ngx-components/widgets/implementations/html-widget';\n\ntype CodeChangeToolCall = ToolCallPart<{ code: string }>;\n\n@Injectable({\n  providedIn: 'root'\n})\nexport class AIHtmlWidgetConfigFactory implements ExtensionFactory<WidgetConfigSectionDefinition> {\n  private readonly betaPreviewService = inject(PreviewService);\n  private readonly aiService = inject(AIService);\n  /** The root injector (nb: components cannot be injected from here). */\n  private readonly injector = inject(Injector);\n\n  private readonly codeTag = 'c8y-code-extract';\n  private readonly codeToolName = 'c8y-html-widget-code';\n  private readonly queryToolName = 'cumulocity-api-request';\n  private readonly widgetConfigService = inject(WidgetConfigService);\n\n  private readonly aiWidgetConfigDefinition: WidgetConfigSectionDefinition<WidgetAiChatSectionComponent> =\n    {\n      widgetId: defaultWidgetIds.HTML,\n      label: gettext('AI Code Assistant'),\n      loadComponent: () =>\n        import('@c8y/ngx-components/ai/agent-chat').then(m => m.WidgetAiChatSectionComponent),\n      initialState: {\n        // configuration to pass to WidgetAiChatSectionComponent\n        agent: HTML_WIDGET_AGENT_DEFINITIONS,\n        chatConfig: {\n          title: gettext(\n            'I’m your AI Code Assistant, here to help you build powerful widgets for your dashboard.'\n          ),\n          welcomeText: gettext(\n            'Describe the widget you want or select one of the options below to get started.'\n          ),\n          showCumulativeUsage: true,\n          showUsagePerMessage: true,\n          showDeleteAction: true\n        },\n\n        loadComponentConfig: this.loadWidgetAiChatComponentConfig.bind(this),\n\n        variables: this.widgetConfigService.currentConfig$.pipe(\n          map((htmlWidgetConfig: HtmlWidgetConfig) => ({\n            currentHtmlWidgetCode: htmlWidgetConfig?.config?.code || '',\n            c8yContext: htmlWidgetConfig?.device || {}\n          }))\n        ),\n\n        suggestions: [\n          {\n            label: gettext('Measurement widget'),\n            prompt: gettext('Create a widget that shows the current measurement of this device.')\n          },\n          {\n            label: gettext('Device status widget'),\n            prompt: gettext('Create a widget that shows the status of my devices.')\n          },\n          {\n            label: gettext('Critical alarm widget'),\n            prompt: gettext('Create a widget that shows all critical alarms.')\n          }\n        ]\n      },\n      priority: 100,\n      injector: this.injector\n    };\n\n  get(): Observable<WidgetConfigSectionDefinition[]> {\n    return combineLatest([\n      from(this.aiService.getAgentHealth()),\n      this.betaPreviewService.getState$('ui.html-widget.v2').pipe(first())\n    ]).pipe(\n      map(([aiHealthCheck, state]) => {\n        if (state && aiHealthCheck.isProviderConfigured) {\n          return [this.aiWidgetConfigDefinition];\n        }\n        return [];\n      })\n    );\n  }\n\n  private async loadWidgetAiChatComponentConfig(\n    componentInjector: Injector\n  ): Promise<WidgetAiChatSectionComponentConfig> {\n    const { HtmlWidgetConfigService, HtmlAiChatToolDetailsComponent } = await import(\n      '@c8y/ngx-components/widgets/implementations/html-widget'\n    );\n    const htmlWidgetConfigService = componentInjector.get(HtmlWidgetConfigService);\n\n    return {\n      preprocessAgentMessage: (message, changed) =>\n        this.preprocessAgentMessage(message, changed, htmlWidgetConfigService),\n\n      assistantMessageDisplayConfig: {\n        toolDetailsComponent: toolCallPart => {\n          if (toolCallPart.toolName === this.codeToolName) {\n            return HtmlAiChatToolDetailsComponent;\n          }\n          return undefined;\n        },\n\n        toolCallConfig: {\n          [this.queryToolName]: {\n            executingLabel: gettext('Analyzing query…'),\n            completedLabel: gettext('Query analyzed')\n          },\n          [this.codeToolName]: {\n            executingLabel: gettext('Creating widget…'),\n            completedLabel: gettext('Widget created')\n          }\n        },\n        // To match current behaviour and to help with development, for now we show the JSON for all tool calls\n        showDefaultToolDetails: 'all'\n      }\n    };\n  }\n\n  private applyCurrentCode(code: string, htmlWidgetConfigService: HtmlWidgetConfigService) {\n    const newConfig = {\n      code: code,\n      css: '',\n      devMode: true,\n      legacy: false,\n      options: { advancedSecurity: false, cssEncapsulation: false }\n    };\n    htmlWidgetConfigService.configChanged$.next(newConfig);\n    htmlWidgetConfigService.widgetConfigService.updateConfig({ config: newConfig });\n  }\n\n  protected preprocessAgentMessage(\n    message: AIAssistantMessage,\n    changedPart: AIStreamResponse['changedPart'],\n    htmlWidgetConfigService: HtmlWidgetConfigService\n  ): AIAssistantMessage {\n    // Only apply this pre-processing to text parts, since that's what the agent will use to send the code\n    if (!changedPart || changedPart.type !== 'text') {\n      return message;\n    }\n\n    // Rewrite HTML content generated by the agent as text content as if it had come from a tool call\n\n    // Find last tool named this.codeToolName in message.content, or undefined\n    const codeTool = message.content.reduce<CodeChangeToolCall | undefined>(\n      (last, part) =>\n        'toolName' in part && part.toolName === this.codeToolName\n          ? (part as CodeChangeToolCall)\n          : last,\n      undefined\n    );\n\n    if (codeTool && codeTool.type !== 'tool-result') {\n      // A code update tool call is in progress - accumulate text into input\n      const input = `${codeTool.input?.code || ''}${changedPart.text}`;\n      codeTool.input.code = input;\n      changedPart.text = '';\n\n      const closeIdx = input.indexOf(`</${this.codeTag}>`);\n      if (closeIdx !== -1) {\n        // Found closing tag - convert to result and start new step\n        const beforeClose = input.substring(0, closeIdx);\n        const afterClose = input.substring(closeIdx + `</${this.codeTag}>`.length);\n\n        codeTool.input.code = beforeClose;\n        codeTool.type = 'tool-result';\n\n        // Always create a new (fake) step after this (just like a real AI), since final step can't contain tool calls\n        message.content.push({ type: 'step-start' });\n        if (afterClose.length > 0) {\n          message.content.push({ type: 'text', text: afterClose });\n        }\n\n        // Since this is a fake tool call not a real one, the tool result callback won't trigger so do this manually\n        this.applyCurrentCode(codeTool.input.code, htmlWidgetConfigService);\n      } else {\n        // Replace with a new instance so that change detection works\n        message.content[message.content.findIndex(part => part === codeTool)] = { ...codeTool };\n      }\n    } else {\n      // No in-progress code tag, so check for one\n      const openIdx = changedPart.text.indexOf(`<${this.codeTag}>`);\n      if (openIdx !== -1) {\n        const afterOpen = changedPart.text.substring(openIdx + `<${this.codeTag}>`.length);\n        changedPart.text = changedPart.text.substring(0, openIdx);\n\n        // Start a new \"step\" to keep it separate and simple\n        message.content.push({ type: 'step-start' });\n        message.content.push({\n          type: 'tool-input-streaming',\n          toolName: this.codeToolName,\n          toolCallId: this.codeToolName + message.content.length, // add part number just to ensure uniqueness\n          input: { code: afterOpen }\n        } satisfies CodeChangeToolCall);\n      }\n    }\n\n    if (changedPart.text === '') {\n      // Remove any empty changedPart from content\n      message.content.splice(\n        message.content.findIndex(part => part === changedPart),\n        1\n      );\n    }\n\n    return message;\n  }\n}\n","import { hookWidgetConfig } from '@c8y/ngx-components/context-dashboard';\nimport { AIHtmlWidgetConfigFactory } from './ai-html-widget-config.factory';\n\nexport const htmlWidgetAIChatProviders = [hookWidgetConfig(AIHtmlWidgetConfigFactory)];\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;AAMA;AACA,MAAM,IAAI,IAAI,OAAO,QAAQ,KAAK,WAAW,GAAG,QAAQ,GAAG,YAAY,CAEvD;AAET,MAAM,6BAA6B,GAA0B;IAClE,QAAQ,EAAE,IAAI,KAAK,aAAa;AAChC,IAAA,KAAK,EAAE,OAAO,CAAC,4BAA4B,CAAC;AAC5C,IAAA,UAAU,EAAE;CACb;;MCWY,yBAAyB,CAAA;AAHtC,IAAA,WAAA,GAAA;AAImB,QAAA,IAAA,CAAA,kBAAkB,GAAG,MAAM,CAAC,cAAc,CAAC;AAC3C,QAAA,IAAA,CAAA,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;;AAE7B,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAE3B,IAAA,CAAA,OAAO,GAAG,kBAAkB;QAC5B,IAAA,CAAA,YAAY,GAAG,sBAAsB;QACrC,IAAA,CAAA,aAAa,GAAG,wBAAwB;AACxC,QAAA,IAAA,CAAA,mBAAmB,GAAG,MAAM,CAAC,mBAAmB,CAAC;AAEjD,QAAA,IAAA,CAAA,wBAAwB,GACvC;YACE,QAAQ,EAAE,gBAAgB,CAAC,IAAI;AAC/B,YAAA,KAAK,EAAE,OAAO,CAAC,mBAAmB,CAAC;AACnC,YAAA,aAAa,EAAE,MACb,OAAO,mCAAmC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,4BAA4B,CAAC;AACvF,YAAA,YAAY,EAAE;;AAEZ,gBAAA,KAAK,EAAE,6BAA6B;AACpC,gBAAA,UAAU,EAAE;AACV,oBAAA,KAAK,EAAE,OAAO,CACZ,yFAAyF,CAC1F;AACD,oBAAA,WAAW,EAAE,OAAO,CAClB,iFAAiF,CAClF;AACD,oBAAA,mBAAmB,EAAE,IAAI;AACzB,oBAAA,mBAAmB,EAAE,IAAI;AACzB,oBAAA,gBAAgB,EAAE;AACnB,iBAAA;gBAED,mBAAmB,EAAE,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC;AAEpE,gBAAA,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,IAAI,CACrD,GAAG,CAAC,CAAC,gBAAkC,MAAM;AAC3C,oBAAA,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE;AAC3D,oBAAA,UAAU,EAAE,gBAAgB,EAAE,MAAM,IAAI;AACzC,iBAAA,CAAC,CAAC,CACJ;AAED,gBAAA,WAAW,EAAE;AACX,oBAAA;AACE,wBAAA,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC;AACpC,wBAAA,MAAM,EAAE,OAAO,CAAC,oEAAoE;AACrF,qBAAA;AACD,oBAAA;AACE,wBAAA,KAAK,EAAE,OAAO,CAAC,sBAAsB,CAAC;AACtC,wBAAA,MAAM,EAAE,OAAO,CAAC,sDAAsD;AACvE,qBAAA;AACD,oBAAA;AACE,wBAAA,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC;AACvC,wBAAA,MAAM,EAAE,OAAO,CAAC,iDAAiD;AAClE;AACF;AACF,aAAA;AACD,YAAA,QAAQ,EAAE,GAAG;YACb,QAAQ,EAAE,IAAI,CAAC;SAChB;AA4IJ,IAAA;IA1IC,GAAG,GAAA;AACD,QAAA,OAAO,aAAa,CAAC;AACnB,YAAA,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC;AACrC,YAAA,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE;AACpE,SAAA,CAAC,CAAC,IAAI,CACL,GAAG,CAAC,CAAC,CAAC,aAAa,EAAE,KAAK,CAAC,KAAI;AAC7B,YAAA,IAAI,KAAK,IAAI,aAAa,CAAC,oBAAoB,EAAE;AAC/C,gBAAA,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC;YACxC;AACA,YAAA,OAAO,EAAE;QACX,CAAC,CAAC,CACH;IACH;IAEQ,MAAM,+BAA+B,CAC3C,iBAA2B,EAAA;QAE3B,MAAM,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,GAAG,MAAM,OACxE,yDAAyD,CAC1D;QACD,MAAM,uBAAuB,GAAG,iBAAiB,CAAC,GAAG,CAAC,uBAAuB,CAAC;QAE9E,OAAO;AACL,YAAA,sBAAsB,EAAE,CAAC,OAAO,EAAE,OAAO,KACvC,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,uBAAuB,CAAC;AAExE,YAAA,6BAA6B,EAAE;gBAC7B,oBAAoB,EAAE,YAAY,IAAG;oBACnC,IAAI,YAAY,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,EAAE;AAC/C,wBAAA,OAAO,8BAA8B;oBACvC;AACA,oBAAA,OAAO,SAAS;gBAClB,CAAC;AAED,gBAAA,cAAc,EAAE;AACd,oBAAA,CAAC,IAAI,CAAC,aAAa,GAAG;AACpB,wBAAA,cAAc,EAAE,OAAO,CAAC,kBAAkB,CAAC;AAC3C,wBAAA,cAAc,EAAE,OAAO,CAAC,gBAAgB;AACzC,qBAAA;AACD,oBAAA,CAAC,IAAI,CAAC,YAAY,GAAG;AACnB,wBAAA,cAAc,EAAE,OAAO,CAAC,kBAAkB,CAAC;AAC3C,wBAAA,cAAc,EAAE,OAAO,CAAC,gBAAgB;AACzC;AACF,iBAAA;;AAED,gBAAA,sBAAsB,EAAE;AACzB;SACF;IACH;IAEQ,gBAAgB,CAAC,IAAY,EAAE,uBAAgD,EAAA;AACrF,QAAA,MAAM,SAAS,GAAG;AAChB,YAAA,IAAI,EAAE,IAAI;AACV,YAAA,GAAG,EAAE,EAAE;AACP,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK;SAC5D;AACD,QAAA,uBAAuB,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC;QACtD,uBAAuB,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACjF;AAEU,IAAA,sBAAsB,CAC9B,OAA2B,EAC3B,WAA4C,EAC5C,uBAAgD,EAAA;;QAGhD,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,EAAE;AAC/C,YAAA,OAAO,OAAO;QAChB;;;QAKA,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CACrC,CAAC,IAAI,EAAE,IAAI,KACT,UAAU,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AAC3C,cAAG;AACH,cAAE,IAAI,EACV,SAAS,CACV;QAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,KAAK,aAAa,EAAE;;AAE/C,YAAA,MAAM,KAAK,GAAG,CAAA,EAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,CAAA,EAAG,WAAW,CAAC,IAAI,EAAE;AAChE,YAAA,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK;AAC3B,YAAA,WAAW,CAAC,IAAI,GAAG,EAAE;AAErB,YAAA,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA,EAAA,EAAK,IAAI,CAAC,OAAO,CAAA,CAAA,CAAG,CAAC;AACpD,YAAA,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE;;gBAEnB,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC;AAChD,gBAAA,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,GAAG,CAAA,EAAA,EAAK,IAAI,CAAC,OAAO,CAAA,CAAA,CAAG,CAAC,MAAM,CAAC;AAE1E,gBAAA,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,WAAW;AACjC,gBAAA,QAAQ,CAAC,IAAI,GAAG,aAAa;;gBAG7B,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC5C,gBAAA,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;AACzB,oBAAA,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;gBAC1D;;gBAGA,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,uBAAuB,CAAC;YACrE;iBAAO;;gBAEL,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE;YACzF;QACF;aAAO;;AAEL,YAAA,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,CAAA,CAAA,CAAG,CAAC;AAC7D,YAAA,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE;AAClB,gBAAA,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,CAAA,CAAA,EAAI,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC;AAClF,gBAAA,WAAW,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC;;gBAGzD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC5C,gBAAA,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;AACnB,oBAAA,IAAI,EAAE,sBAAsB;oBAC5B,QAAQ,EAAE,IAAI,CAAC,YAAY;oBAC3B,UAAU,EAAE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM;AACtD,oBAAA,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS;AACI,iBAAA,CAAC;YACjC;QACF;AAEA,QAAA,IAAI,WAAW,CAAC,IAAI,KAAK,EAAE,EAAE;;YAE3B,OAAO,CAAC,OAAO,CAAC,MAAM,CACpB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,KAAK,WAAW,CAAC,EACvD,CAAC,CACF;QACH;AAEA,QAAA,OAAO,OAAO;IAChB;+GArMW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAzB,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,yBAAyB,cAFxB,MAAM,EAAA,CAAA,CAAA;;4FAEP,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAHrC,UAAU;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,UAAU,EAAE;AACb,iBAAA;;;ACtBM,MAAM,yBAAyB,GAAG,CAAC,gBAAgB,CAAC,yBAAyB,CAAC;;ACHrF;;AAEG;;;;"}