@c8y/ngx-components 1024.15.16 → 1024.16.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +303 -63
  2. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
  3. package/fesm2022/c8y-ngx-components-ai-agents-html.mjs +9 -0
  4. package/fesm2022/c8y-ngx-components-ai-agents-html.mjs.map +1 -1
  5. package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs +4 -0
  6. package/fesm2022/c8y-ngx-components-ai-agents-scada.mjs.map +1 -1
  7. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +394 -52
  8. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
  9. package/fesm2022/c8y-ngx-components-ai.mjs +329 -19
  10. package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
  11. package/fesm2022/c8y-ngx-components-bookmarks.mjs +1 -1
  12. package/fesm2022/c8y-ngx-components-bookmarks.mjs.map +1 -1
  13. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +5 -1
  14. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  15. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs +96 -77
  16. package/fesm2022/c8y-ngx-components-datapoint-explorer-view.mjs.map +1 -1
  17. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +92 -47
  18. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
  19. package/fesm2022/c8y-ngx-components-device-profile.mjs +1 -1
  20. package/fesm2022/c8y-ngx-components-device-profile.mjs.map +1 -1
  21. package/fesm2022/c8y-ngx-components-echart.mjs +25 -4
  22. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  23. package/fesm2022/c8y-ngx-components-ecosystem.mjs +1 -1
  24. package/fesm2022/c8y-ngx-components-ecosystem.mjs.map +1 -1
  25. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs +1 -1
  26. package/fesm2022/c8y-ngx-components-protocol-opcua.mjs.map +1 -1
  27. package/fesm2022/c8y-ngx-components-repository-firmware.mjs +1 -1
  28. package/fesm2022/c8y-ngx-components-repository-firmware.mjs.map +1 -1
  29. package/fesm2022/c8y-ngx-components-repository-software.mjs +1 -1
  30. package/fesm2022/c8y-ngx-components-repository-software.mjs.map +1 -1
  31. package/fesm2022/c8y-ngx-components-sub-assets.mjs +30 -10
  32. package/fesm2022/c8y-ngx-components-sub-assets.mjs.map +1 -1
  33. package/fesm2022/c8y-ngx-components-time-context.mjs +13 -9
  34. package/fesm2022/c8y-ngx-components-time-context.mjs.map +1 -1
  35. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs +1 -1
  36. package/fesm2022/c8y-ngx-components-trusted-certificates.mjs.map +1 -1
  37. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +3 -2
  38. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -1
  39. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs +4 -6
  40. package/fesm2022/c8y-ngx-components-widgets-implementations-asset-table.mjs.map +1 -1
  41. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs +3 -2
  42. package/fesm2022/c8y-ngx-components-widgets-implementations-scada.mjs.map +1 -1
  43. package/fesm2022/c8y-ngx-components.mjs +76 -19
  44. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  45. package/locales/de.po +147 -16
  46. package/locales/es.po +143 -11
  47. package/locales/fr.po +459 -324
  48. package/locales/ja_JP.po +143 -11
  49. package/locales/ko.po +143 -11
  50. package/locales/locales.pot +136 -10
  51. package/locales/nl.po +143 -11
  52. package/locales/pl.po +143 -11
  53. package/locales/pt_BR.po +143 -11
  54. package/locales/zh_CN.po +143 -11
  55. package/locales/zh_TW.po +143 -11
  56. package/package.json +1 -1
  57. package/types/c8y-ngx-components-ai-agent-chat.d.ts +111 -4
  58. package/types/c8y-ngx-components-ai-agent-chat.d.ts.map +1 -1
  59. package/types/c8y-ngx-components-ai-agents-html.d.ts.map +1 -1
  60. package/types/c8y-ngx-components-ai-agents-scada.d.ts.map +1 -1
  61. package/types/c8y-ngx-components-ai-ai-chat.d.ts +224 -10
  62. package/types/c8y-ngx-components-ai-ai-chat.d.ts.map +1 -1
  63. package/types/c8y-ngx-components-ai.d.ts +176 -6
  64. package/types/c8y-ngx-components-ai.d.ts.map +1 -1
  65. package/types/c8y-ngx-components-context-dashboard.d.ts.map +1 -1
  66. package/types/c8y-ngx-components-datapoint-explorer-view.d.ts +7 -2
  67. package/types/c8y-ngx-components-datapoint-explorer-view.d.ts.map +1 -1
  68. package/types/c8y-ngx-components-datapoint-selector.d.ts +63 -50
  69. package/types/c8y-ngx-components-datapoint-selector.d.ts.map +1 -1
  70. package/types/c8y-ngx-components-echart.d.ts +12 -0
  71. package/types/c8y-ngx-components-echart.d.ts.map +1 -1
  72. package/types/c8y-ngx-components-sub-assets.d.ts +10 -1
  73. package/types/c8y-ngx-components-sub-assets.d.ts.map +1 -1
  74. package/types/c8y-ngx-components-time-context.d.ts +2 -0
  75. package/types/c8y-ngx-components-time-context.d.ts.map +1 -1
  76. package/types/c8y-ngx-components-widgets-implementations-scada.d.ts.map +1 -1
  77. package/types/c8y-ngx-components.d.ts +16 -2
  78. package/types/c8y-ngx-components.d.ts.map +1 -1
@@ -15,13 +15,13 @@ var DataStreamType;
15
15
  DataStreamType["TOOL_CALL_STREAMING"] = "tool-call-streaming";
16
16
  DataStreamType["TOOL_CALL_DELTA"] = "tool-call-delta";
17
17
  DataStreamType["TOOL_RESULT"] = "tool-result";
18
+ DataStreamType["TOOL_APPROVAL_REQUEST"] = "tool-approval-request";
18
19
  DataStreamType["REASONING"] = "reasoning";
19
20
  DataStreamType["REASONING_DELTA"] = "reasoning-delta";
20
21
  DataStreamType["REDACTED_REASONING"] = "redacted-reasoning";
21
22
  DataStreamType["REASONING_SIGNATURE"] = "reasoning-signature";
22
23
  DataStreamType["FINISH_REASONING"] = "finish-reasoning";
23
24
  DataStreamType["FINISH"] = "finish";
24
- DataStreamType["FINISH_STEP"] = "finish-step";
25
25
  DataStreamType["ERROR"] = "error";
26
26
  DataStreamType["DATA"] = "data";
27
27
  DataStreamType["MESSAGE_ANNOTATIONS"] = "message-annotations";
@@ -57,7 +57,8 @@ class AIService {
57
57
  headers: { 'Content-Type': 'application/json' }
58
58
  });
59
59
  if (!response.ok) {
60
- throw new Error(`Failed to create agent: ${response.statusText}`);
60
+ const detail = await response.text().catch(() => '');
61
+ throw new Error(`Failed to create agent: ${response.statusText}${detail ? ` - ${detail}` : ''}`);
61
62
  }
62
63
  }
63
64
  /**
@@ -294,32 +295,174 @@ class AIService {
294
295
  // We will need to rewrite this to include tool outputs soon (effectively this means targetting LanguageModelV*ToolResultOutput).
295
296
  // Once we have more fields than just role and content, we will need to convert between our format and
296
297
  // the Vercel format expected by the agent manager (e.g. steps->content parts)
297
- return messages.map(m => {
298
+ // The result of an approved tool call streams in with the NEXT assistant message after the
299
+ // resume, but on the wire it must sit next to its approval exchange (the agent strips
300
+ // approval parts from the model prompt, so an exchange without a result leaves a dangling
301
+ // tool call the model provider rejects). Index all results up front so
302
+ // convertAssistantMessageWithApprovals can pull them into the right message.
303
+ const resultsByToolCallId = new Map();
304
+ for (const m of messages) {
305
+ if (m.role !== 'assistant')
306
+ continue;
307
+ for (const part of m.content) {
308
+ if (part.type === 'tool-result') {
309
+ resultsByToolCallId.set(part.toolCallId, part);
310
+ }
311
+ }
312
+ }
313
+ const serializedToolCallIds = new Set();
314
+ return messages.flatMap(m => {
298
315
  if (m.role !== 'assistant') {
299
- return {
300
- role: m.role,
301
- content: m.content
302
- };
316
+ return [
317
+ {
318
+ role: m.role,
319
+ content: m.content
320
+ }
321
+ ];
303
322
  }
304
323
  // For assistant messages, check if there's an object part
305
324
  const objectPart = m.content.find((part) => part.type === 'object');
306
325
  if (objectPart) {
307
326
  // If there's an object part, parse and send the JSON object as content
308
- return {
309
- role: m.role,
310
- content: objectPart.jsonContent
311
- };
327
+ return [
328
+ {
329
+ role: m.role,
330
+ content: objectPart.jsonContent
331
+ }
332
+ ];
333
+ }
334
+ // Messages containing tool approvals must round-trip with full fidelity so the agent
335
+ // can match the user's decision to the pending tool call.
336
+ if (m.content.some(part => part.type === 'tool-approval-requested' || part.type === 'tool-approval-responded')) {
337
+ return this.convertAssistantMessageWithApprovals(m, resultsByToolCallId, serializedToolCallIds);
312
338
  }
313
339
  // Otherwise, send text parts as joined string
314
- return {
315
- role: m.role,
316
- content: m.content
317
- .filter((part) => part.type === 'text')
318
- .map(part => part.text)
319
- .join('\n\n')
320
- };
340
+ return [
341
+ {
342
+ role: m.role,
343
+ content: m.content
344
+ .filter((part) => part.type === 'text')
345
+ .map(part => part.text)
346
+ .join('\n\n')
347
+ }
348
+ ];
321
349
  });
322
350
  }
351
+ /**
352
+ * Converts an assistant message containing tool approval parts into the wire format expected by
353
+ * the agent: an assistant message carrying `tool-call` and `tool-approval-request` parts, followed
354
+ * (once the user has decided) by a `tool` role message carrying the matching `tool-approval-response`
355
+ * parts.
356
+ *
357
+ * The agent strips approval parts from the prompt it sends to the model provider, so a *resolved*
358
+ * exchange must also carry the call's outcome in its tool message: the executed result (found via
359
+ * `resultsByToolCallId`, since it streams in with the following assistant message) or an
360
+ * `execution-denied` marker. Each such result is serialized exactly once - `serializedToolCallIds`
361
+ * tracks consumed calls so the copy stored in the following message is not emitted again as a
362
+ * duplicate tool call.
363
+ */
364
+ convertAssistantMessageWithApprovals(m, resultsByToolCallId, serializedToolCallIds) {
365
+ const assistantContent = [];
366
+ const toolContent = [];
367
+ for (const part of m.content) {
368
+ switch (part.type) {
369
+ case 'text':
370
+ assistantContent.push({ type: 'text', text: part.text });
371
+ break;
372
+ case 'tool-approval-requested':
373
+ case 'tool-approval-responded':
374
+ if (!part.approval)
375
+ break;
376
+ assistantContent.push({
377
+ type: 'tool-call',
378
+ toolCallId: part.toolCallId,
379
+ toolName: part.toolName,
380
+ input: part.input ?? {}
381
+ });
382
+ assistantContent.push({
383
+ type: 'tool-approval-request',
384
+ approvalId: part.approval.approvalId,
385
+ toolCallId: part.toolCallId
386
+ });
387
+ if (part.approval.approved !== undefined) {
388
+ toolContent.push({
389
+ type: 'tool-approval-response',
390
+ approvalId: part.approval.approvalId,
391
+ approved: part.approval.approved,
392
+ ...(part.approval.reason ? { reason: part.approval.reason } : {})
393
+ });
394
+ if (!serializedToolCallIds.has(part.toolCallId)) {
395
+ if (!part.approval.approved) {
396
+ toolContent.push({
397
+ type: 'tool-result',
398
+ toolCallId: part.toolCallId,
399
+ toolName: part.toolName,
400
+ output: {
401
+ type: 'execution-denied',
402
+ ...(part.approval.reason ? { reason: part.approval.reason } : {})
403
+ }
404
+ });
405
+ serializedToolCallIds.add(part.toolCallId);
406
+ }
407
+ else {
408
+ const result = resultsByToolCallId.get(part.toolCallId);
409
+ if (result) {
410
+ toolContent.push({
411
+ type: 'tool-result',
412
+ toolCallId: part.toolCallId,
413
+ toolName: part.toolName,
414
+ output: this.convertToolResultOutputForAgent(result)
415
+ });
416
+ serializedToolCallIds.add(part.toolCallId);
417
+ }
418
+ // No result yet: this approval is the one triggering the resume,
419
+ // and the agent executes the tool itself.
420
+ }
421
+ }
422
+ }
423
+ break;
424
+ case 'tool-result':
425
+ // Skip results already serialized next to their approval exchange in an earlier message
426
+ if (serializedToolCallIds.has(part.toolCallId))
427
+ break;
428
+ assistantContent.push({
429
+ type: 'tool-call',
430
+ toolCallId: part.toolCallId,
431
+ toolName: part.toolName,
432
+ input: part.input ?? {}
433
+ });
434
+ toolContent.push({
435
+ type: 'tool-result',
436
+ toolCallId: part.toolCallId,
437
+ toolName: part.toolName,
438
+ output: this.convertToolResultOutputForAgent(part)
439
+ });
440
+ break;
441
+ default:
442
+ // reasoning and step-start parts are not needed by the agent
443
+ break;
444
+ }
445
+ }
446
+ const result = [{ role: 'assistant', content: assistantContent }];
447
+ if (toolContent.length > 0) {
448
+ result.push({ role: 'tool', content: toolContent });
449
+ }
450
+ return result;
451
+ }
452
+ /** Converts a tool output stored in our format back to the LanguageModelV*ToolResultOutput wire format. */
453
+ convertToolResultOutputForAgent(part) {
454
+ if (part.outputType === 'content') {
455
+ return { type: 'content', value: part.output };
456
+ }
457
+ if (part.error) {
458
+ return typeof part.output === 'string'
459
+ ? { type: 'error-text', value: part.output }
460
+ : { type: 'error-json', value: part.output ?? null };
461
+ }
462
+ return typeof part.output === 'string'
463
+ ? { type: 'text', value: part.output }
464
+ : { type: 'json', value: part.output ?? null };
465
+ }
323
466
  /**
324
467
  * Convert a tool output from the MCP/wire format (which is different/more compact than LanguageModelV3ToolResultOutput)
325
468
  * into our own standard representation, which can be roundtripped to the format we need to provide when making future requests.
@@ -464,6 +607,14 @@ class AIService {
464
607
  changedPart: this.addToolCallInfo('tool-result', data, message.content)
465
608
  });
466
609
  return;
610
+ case DataStreamType.TOOL_APPROVAL_REQUEST: {
611
+ // Emitted when a tool with `needsApproval` is called; the agent stops until the
612
+ // conversation is resent with the user's decision (see convertAssistantMessageWithApprovals).
613
+ const approvalPart = this.addToolCallInfo('tool-approval-requested', data.toolCall || data, message.content);
614
+ approvalPart.approval = { approvalId: data.approvalId };
615
+ observer.next({ message, changedPart: approvalPart });
616
+ return;
617
+ }
467
618
  case DataStreamType.FINISH:
468
619
  message.finishReason = data.finishReason; // FUTURE could be anything, may not match the options in AIMessage
469
620
  message.usage = data.totalUsage;
@@ -512,6 +663,19 @@ function defaultPruneMessagesForAgent(messages) {
512
663
  // to avoid too many tokens and context window consumption.
513
664
  // Some applications will want to entirely remove specific tool calls that have only transient use,
514
665
  // which can be done by providing their own pruneMessagesForAgent function.
666
+ // Tool calls that went through an approval exchange must keep their results as well:
667
+ // the wire format pairs each resolved approval with its result, so pruning the result
668
+ // would leave the approved tool call dangling in the prompt sent to the model.
669
+ const approvalToolCallIds = new Set();
670
+ for (const m of messages) {
671
+ if (m.role !== 'assistant')
672
+ continue;
673
+ for (const part of m.content) {
674
+ if (part.type === 'tool-approval-requested' || part.type === 'tool-approval-responded') {
675
+ approvalToolCallIds.add(part.toolCallId);
676
+ }
677
+ }
678
+ }
515
679
  return messages.map(m => {
516
680
  if (m.role !== 'assistant') {
517
681
  return {
@@ -521,6 +685,11 @@ function defaultPruneMessagesForAgent(messages) {
521
685
  }
522
686
  const textParts = m.content.filter((part) => part.type === 'text');
523
687
  const objectParts = m.content.filter((part) => part.type === 'object');
688
+ // Approval parts must survive pruning: the agent needs the pending tool call and the
689
+ // user's decision to resume a conversation that stopped for approval.
690
+ const approvalParts = m.content.filter((part) => part.type === 'tool-approval-requested' ||
691
+ part.type === 'tool-approval-responded' ||
692
+ (part.type === 'tool-result' && approvalToolCallIds.has(part.toolCallId)));
524
693
  const content = [];
525
694
  // Only add text part if there are text parts
526
695
  if (textParts.length > 0) {
@@ -531,6 +700,7 @@ function defaultPruneMessagesForAgent(messages) {
531
700
  }
532
701
  // Preserve object parts for object agents
533
702
  content.push(...objectParts);
703
+ content.push(...approvalParts);
534
704
  return {
535
705
  role: m.role,
536
706
  content
@@ -538,9 +708,149 @@ function defaultPruneMessagesForAgent(messages) {
538
708
  });
539
709
  }
540
710
 
711
+ /**
712
+ * Remembers the user's "always approve" decisions for tool calls that require human approval.
713
+ *
714
+ * Decisions are held in memory only and are scoped to a chat session: each chat component
715
+ * instance identifies its conversation with a unique session ID, so approvals never outlive
716
+ * the conversation they were given in. Navigating away and back, reopening a dialog, or
717
+ * starting a new conversation creates a new session that prompts again. This applies both to
718
+ * single tools ("always approve this tool") and to the session-wide YOLO mode
719
+ * ({@link rememberAllTools}, "always approve all tools").
720
+ *
721
+ * Tools configured with `needsApproval` always prompt regardless of these preferences; that
722
+ * is enforced by the chat component, since it requires the agent definition. The chat
723
+ * component also offers a global "approve all tools" option persisted as a user preference,
724
+ * which is not managed by this service.
725
+ *
726
+ * Only approvals are remembered, never denials.
727
+ */
728
+ class AiToolApprovalMemoryService {
729
+ constructor() {
730
+ /** Remembered approvals, keyed by chat session ID. */
731
+ this.sessions = new Map();
732
+ this.nextSessionId = 0;
733
+ }
734
+ /**
735
+ * Returns a new chat session ID, unique for the lifetime of the page. Chat components request
736
+ * one per conversation and pass it to the other methods to scope their approvals.
737
+ */
738
+ newSessionId() {
739
+ return `chat-session-${++this.nextSessionId}`;
740
+ }
741
+ /** Remembers that calls of the given tool are always approved within the given chat session. */
742
+ rememberTool(sessionId, toolName) {
743
+ this.session(sessionId).tools.add(toolName);
744
+ }
745
+ /**
746
+ * Enables YOLO mode for the given chat session: every tool of the conversation is approved
747
+ * automatically, without prompting, until the conversation ends.
748
+ */
749
+ rememberAllTools(sessionId) {
750
+ this.session(sessionId).approveAll = true;
751
+ }
752
+ /** Whether the user chose to always approve the given tool within the given chat session. */
753
+ isToolRemembered(sessionId, toolName) {
754
+ return this.sessions.get(sessionId)?.tools.has(toolName) ?? false;
755
+ }
756
+ /** Whether YOLO mode is enabled for the given chat session (see {@link rememberAllTools}). */
757
+ isApproveAllToolsEnabled(sessionId) {
758
+ return this.sessions.get(sessionId)?.approveAll ?? false;
759
+ }
760
+ /** Forgets the remembered approvals of a chat session, e.g. when the conversation ends. */
761
+ endSession(sessionId) {
762
+ this.sessions.delete(sessionId);
763
+ }
764
+ /** Forgets all remembered approvals of all sessions. */
765
+ clear() {
766
+ this.sessions.clear();
767
+ }
768
+ session(sessionId) {
769
+ let session = this.sessions.get(sessionId);
770
+ if (!session) {
771
+ session = { approveAll: false, tools: new Set() };
772
+ this.sessions.set(sessionId, session);
773
+ }
774
+ return session;
775
+ }
776
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
777
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, providedIn: 'root' }); }
778
+ }
779
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: AiToolApprovalMemoryService, decorators: [{
780
+ type: Injectable,
781
+ args: [{ providedIn: 'root' }]
782
+ }] });
783
+
784
+ /** REST resource segments the `cumulocity-api-request` tool commonly queries, mapped to a readable plural noun. */
785
+ const RESOURCE_LABELS = {
786
+ managedObjects: gettext('inventory objects'),
787
+ alarms: gettext('alarms'),
788
+ measurements: gettext('measurements'),
789
+ events: gettext('events'),
790
+ operations: gettext('operations'),
791
+ users: gettext('users'),
792
+ applications: gettext('applications'),
793
+ identity: gettext('external IDs')
794
+ };
795
+ /** Common `fragmentType`/`type` filter values used to narrow an inventory query, mapped to a more specific noun than the generic "inventory objects" label. */
796
+ const FRAGMENT_TYPE_LABELS = {
797
+ c8y_IsDevice: gettext('devices'),
798
+ c8y_IsDeviceGroup: gettext('device groups'),
799
+ c8y_IsAsset: gettext('assets')
800
+ };
801
+ /**
802
+ * A `labelProvider` (see `ToolCallConfig`) for the `cumulocity-api-request` tool: the generic
803
+ * Cumulocity REST query tool offered to AI agents to look up API data. For a tool call awaiting
804
+ * approval, produces a human-readable summary of the query - e.g. "Look up: alarms with status:
805
+ * active" -
806
+ * instead of the raw `{ "query": "/alarm/alarms?status=ACTIVE" }`, so the user can judge the
807
+ * request without reading the API path themselves.
808
+ *
809
+ * Only applies to the `tool-approval-requested`/`tool-approval-responded` states; for every other
810
+ * state this returns `undefined` so the caller's own `executingLabel`/`completedLabel` is used
811
+ * instead. It also returns `undefined` for any query it doesn't recognize, which keeps the generic
812
+ * "Tool cumulocity-api-request requires approval" text and the auto-expanded raw input as a
813
+ * fallback - recognizes the common read endpoints (inventory, alarms, measurements, events,
814
+ * operations, users, applications, identity).
815
+ *
816
+ * Assumes the tool's `query` input is a REST path with no separate `method`/body fields, matching
817
+ * its documented purpose of verifying/looking up API data.
818
+ */
819
+ function cumulocityApiRequestLabelProvider(tool, translateService) {
820
+ if (tool.type !== 'tool-approval-requested' && tool.type !== 'tool-approval-responded') {
821
+ return undefined;
822
+ }
823
+ const query = tool.input?.query;
824
+ const summary = summarizeQuery(query, translateService);
825
+ if (!summary)
826
+ return undefined;
827
+ if (tool.type === 'tool-approval-responded') {
828
+ return translateService.instant(tool.approval?.approved ? gettext('Approved: {{summary}}') : gettext('Denied: {{summary}}'), { summary });
829
+ }
830
+ return summary;
831
+ }
832
+ function summarizeQuery(query, translateService) {
833
+ if (!query)
834
+ return undefined;
835
+ const [path, search] = query.split('?');
836
+ const resourceKey = path.split('/').find(segment => RESOURCE_LABELS[segment]);
837
+ if (!resourceKey)
838
+ return undefined;
839
+ const params = new URLSearchParams(search || '');
840
+ const fragmentType = params.get('fragmentType') || params.get('type');
841
+ const resource = translateService.instant((fragmentType && FRAGMENT_TYPE_LABELS[fragmentType]) || RESOURCE_LABELS[resourceKey]);
842
+ const status = params.get('status');
843
+ return status
844
+ ? translateService.instant(gettext('Look up: {{resource}} with status: {{status}}'), {
845
+ status: status.toLowerCase(),
846
+ resource
847
+ })
848
+ : translateService.instant(gettext('Look up: {{resource}}'), { resource });
849
+ }
850
+
541
851
  /**
542
852
  * Generated bundle index. Do not edit.
543
853
  */
544
854
 
545
- export { AIService, DataStreamType, defaultPruneMessagesForAgent };
855
+ export { AIService, AiToolApprovalMemoryService, DataStreamType, cumulocityApiRequestLabelProvider, defaultPruneMessagesForAgent };
546
856
  //# sourceMappingURL=c8y-ngx-components-ai.mjs.map