@memberjunction/ng-core-entity-forms 6.2.0-edge.4 → 6.2.0-edge.5

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 (132) hide show
  1. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts +7 -0
  2. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
  3. package/dist/lib/custom/AIAgents/ai-agent-form.component.js +31 -17
  4. package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
  5. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts +7 -1
  6. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts.map +1 -1
  7. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js +191 -178
  8. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js.map +1 -1
  9. package/dist/lib/custom/AIPromptRuns/prompt-run-data-capture-note.component.d.ts +48 -0
  10. package/dist/lib/custom/AIPromptRuns/prompt-run-data-capture-note.component.d.ts.map +1 -0
  11. package/dist/lib/custom/AIPromptRuns/prompt-run-data-capture-note.component.js +195 -0
  12. package/dist/lib/custom/AIPromptRuns/prompt-run-data-capture-note.component.js.map +1 -0
  13. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js +324 -322
  14. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js.map +1 -1
  15. package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts.map +1 -1
  16. package/dist/lib/custom/RecordProcesses/record-process-form.component.js +6 -3
  17. package/dist/lib/custom/RecordProcesses/record-process-form.component.js.map +1 -1
  18. package/dist/lib/custom/SearchScopes/searchscope-form.component.d.ts +3 -3
  19. package/dist/lib/custom/SearchScopes/searchscope-form.component.d.ts.map +1 -1
  20. package/dist/lib/custom/SearchScopes/searchscope-form.component.js +5 -7
  21. package/dist/lib/custom/SearchScopes/searchscope-form.component.js.map +1 -1
  22. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts.map +1 -1
  23. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js +8 -8
  24. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js.map +1 -1
  25. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts +37 -5
  26. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts.map +1 -1
  27. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js +155 -79
  28. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js.map +1 -1
  29. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts +8 -0
  30. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts.map +1 -1
  31. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js +59 -8
  32. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js.map +1 -1
  33. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +34 -9
  34. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
  35. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +164 -43
  36. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
  37. package/dist/lib/custom/ai-agent-run/run-tree-timeline-projection.js +3 -1
  38. package/dist/lib/custom/ai-agent-run/run-tree-timeline-projection.js.map +1 -1
  39. package/dist/lib/custom/custom-forms.module.d.ts +82 -80
  40. package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
  41. package/dist/lib/custom/custom-forms.module.js +10 -0
  42. package/dist/lib/custom/custom-forms.module.js.map +1 -1
  43. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.d.ts.map +1 -1
  44. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js +65 -47
  45. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js.map +1 -1
  46. package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js +115 -117
  47. package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js.map +1 -1
  48. package/dist/lib/generated/Entities/MJAIAgentRunStep/mjaiagentrunstep.form.component.d.ts.map +1 -1
  49. package/dist/lib/generated/Entities/MJAIAgentRunStep/mjaiagentrunstep.form.component.js +64 -24
  50. package/dist/lib/generated/Entities/MJAIAgentRunStep/mjaiagentrunstep.form.component.js.map +1 -1
  51. package/dist/lib/generated/Entities/MJAIAgentSession/mjaiagentsession.form.component.js +74 -52
  52. package/dist/lib/generated/Entities/MJAIAgentSession/mjaiagentsession.form.component.js.map +1 -1
  53. package/dist/lib/generated/Entities/MJAIAgentSessionRecordingShard/mjaiagentsessionrecordingshard.form.component.d.ts +10 -0
  54. package/dist/lib/generated/Entities/MJAIAgentSessionRecordingShard/mjaiagentsessionrecordingshard.form.component.d.ts.map +1 -0
  55. package/dist/lib/generated/Entities/MJAIAgentSessionRecordingShard/mjaiagentsessionrecordingshard.form.component.js +86 -0
  56. package/dist/lib/generated/Entities/MJAIAgentSessionRecordingShard/mjaiagentsessionrecordingshard.form.component.js.map +1 -0
  57. package/dist/lib/generated/Entities/MJAIAgentUserPolicy/mjaiagentuserpolicy.form.component.d.ts +10 -0
  58. package/dist/lib/generated/Entities/MJAIAgentUserPolicy/mjaiagentuserpolicy.form.component.d.ts.map +1 -0
  59. package/dist/lib/generated/Entities/MJAIAgentUserPolicy/mjaiagentuserpolicy.form.component.js +132 -0
  60. package/dist/lib/generated/Entities/MJAIAgentUserPolicy/mjaiagentuserpolicy.form.component.js.map +1 -0
  61. package/dist/lib/generated/Entities/MJAIBridgeAgentIdentity/mjaibridgeagentidentity.form.component.js +12 -10
  62. package/dist/lib/generated/Entities/MJAIBridgeAgentIdentity/mjaibridgeagentidentity.form.component.js.map +1 -1
  63. package/dist/lib/generated/Entities/MJAIModel/mjaimodel.form.component.d.ts.map +1 -1
  64. package/dist/lib/generated/Entities/MJAIModel/mjaimodel.form.component.js +86 -32
  65. package/dist/lib/generated/Entities/MJAIModel/mjaimodel.form.component.js.map +1 -1
  66. package/dist/lib/generated/Entities/MJAIPersona/mjaipersona.form.component.d.ts.map +1 -1
  67. package/dist/lib/generated/Entities/MJAIPersona/mjaipersona.form.component.js +25 -7
  68. package/dist/lib/generated/Entities/MJAIPersona/mjaipersona.form.component.js.map +1 -1
  69. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.d.ts.map +1 -1
  70. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.js +180 -160
  71. package/dist/lib/generated/Entities/MJAIPrompt/mjaiprompt.form.component.js.map +1 -1
  72. package/dist/lib/generated/Entities/MJActionParam/mjactionparam.form.component.js +16 -14
  73. package/dist/lib/generated/Entities/MJActionParam/mjactionparam.form.component.js.map +1 -1
  74. package/dist/lib/generated/Entities/MJArchiveConfiguration/mjarchiveconfiguration.form.component.js +23 -21
  75. package/dist/lib/generated/Entities/MJArchiveConfiguration/mjarchiveconfiguration.form.component.js.map +1 -1
  76. package/dist/lib/generated/Entities/MJArchiveRun/mjarchiverun.form.component.js +21 -19
  77. package/dist/lib/generated/Entities/MJArchiveRun/mjarchiverun.form.component.js.map +1 -1
  78. package/dist/lib/generated/Entities/MJCommunicationProvider/mjcommunicationprovider.form.component.js +30 -20
  79. package/dist/lib/generated/Entities/MJCommunicationProvider/mjcommunicationprovider.form.component.js.map +1 -1
  80. package/dist/lib/generated/Entities/MJConversationDetail/mjconversationdetail.form.component.js +69 -75
  81. package/dist/lib/generated/Entities/MJConversationDetail/mjconversationdetail.form.component.js.map +1 -1
  82. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.d.ts.map +1 -1
  83. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.js +35 -17
  84. package/dist/lib/generated/Entities/MJCredential/mjcredential.form.component.js.map +1 -1
  85. package/dist/lib/generated/Entities/MJFeaturePipelineVersion/mjfeaturepipelineversion.form.component.d.ts +10 -0
  86. package/dist/lib/generated/Entities/MJFeaturePipelineVersion/mjfeaturepipelineversion.form.component.d.ts.map +1 -0
  87. package/dist/lib/generated/Entities/MJFeaturePipelineVersion/mjfeaturepipelineversion.form.component.js +130 -0
  88. package/dist/lib/generated/Entities/MJFeaturePipelineVersion/mjfeaturepipelineversion.form.component.js.map +1 -0
  89. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.js +26 -14
  90. package/dist/lib/generated/Entities/MJFeatureValue/mjfeaturevalue.form.component.js.map +1 -1
  91. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.js +23 -19
  92. package/dist/lib/generated/Entities/MJFeatureValueCache/mjfeaturevaluecache.form.component.js.map +1 -1
  93. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js +29 -27
  94. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js.map +1 -1
  95. package/dist/lib/generated/Entities/MJProcessRunDetail/mjprocessrundetail.form.component.js +17 -15
  96. package/dist/lib/generated/Entities/MJProcessRunDetail/mjprocessrundetail.form.component.js.map +1 -1
  97. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.d.ts.map +1 -1
  98. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.js +27 -9
  99. package/dist/lib/generated/Entities/MJRecordProcess/mjrecordprocess.form.component.js.map +1 -1
  100. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.d.ts.map +1 -1
  101. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js +24 -6
  102. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js.map +1 -1
  103. package/dist/lib/generated/Entities/MJTestRun/mjtestrun.form.component.d.ts.map +1 -1
  104. package/dist/lib/generated/Entities/MJTestRun/mjtestrun.form.component.js +29 -11
  105. package/dist/lib/generated/Entities/MJTestRun/mjtestrun.form.component.js.map +1 -1
  106. package/dist/lib/generated/Entities/MJTheme/mjtheme.form.component.d.ts.map +1 -1
  107. package/dist/lib/generated/Entities/MJTheme/mjtheme.form.component.js +13 -3
  108. package/dist/lib/generated/Entities/MJTheme/mjtheme.form.component.js.map +1 -1
  109. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.d.ts.map +1 -1
  110. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js +176 -122
  111. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js.map +1 -1
  112. package/dist/lib/generated/Entities/MJUserRoutine/mjuserroutine.form.component.js +31 -25
  113. package/dist/lib/generated/Entities/MJUserRoutine/mjuserroutine.form.component.js.map +1 -1
  114. package/dist/lib/generated/generated-forms.module.d.ts +129 -126
  115. package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
  116. package/dist/lib/generated/generated-forms.module.js +9 -0
  117. package/dist/lib/generated/generated-forms.module.js.map +1 -1
  118. package/dist/lib/panels/feature-values/feature-values.logic.d.ts +55 -0
  119. package/dist/lib/panels/feature-values/feature-values.logic.d.ts.map +1 -0
  120. package/dist/lib/panels/feature-values/feature-values.logic.js +88 -0
  121. package/dist/lib/panels/feature-values/feature-values.logic.js.map +1 -0
  122. package/dist/lib/panels/feature-values/feature-values.panel.d.ts +39 -0
  123. package/dist/lib/panels/feature-values/feature-values.panel.d.ts.map +1 -0
  124. package/dist/lib/panels/feature-values/feature-values.panel.js +303 -0
  125. package/dist/lib/panels/feature-values/feature-values.panel.js.map +1 -0
  126. package/dist/lib/shared/components/template-editor.component.js +2 -2
  127. package/dist/lib/shared/components/template-editor.component.js.map +1 -1
  128. package/dist/public-api.d.ts +1 -0
  129. package/dist/public-api.d.ts.map +1 -1
  130. package/dist/public-api.js +1 -0
  131. package/dist/public-api.js.map +1 -1
  132. package/package.json +48 -48
@@ -1,6 +1,7 @@
1
- import { Component, Input, Output, EventEmitter } from '@angular/core';
1
+ import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
2
  import { MJAIAgentRunStepEntity } from '@memberjunction/core-entities';
3
3
  import { ResolveTaskGraphPositions } from '@memberjunction/ai-core-plus';
4
+ import { TaskGraphRunViewComponent } from '@memberjunction/ng-task-graph-editor';
4
5
  import { ParseJSONRecursive } from '@memberjunction/global';
5
6
  import * as i0 from "@angular/core";
6
7
  import * as i1 from "@angular/common";
@@ -136,17 +137,23 @@ function AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Template(rf,
136
137
  i0.ɵɵelementStart(3, "button", 32);
137
138
  i0.ɵɵlistener("click", function AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.RequestSaveAsWorkflow()); });
138
139
  i0.ɵɵelement(4, "i", 33);
139
- i0.ɵɵtext(5, " Save as Workflow ");
140
+ i0.ɵɵtext(5);
140
141
  i0.ɵɵelementEnd()();
141
142
  i0.ɵɵconditionalCreate(6, AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Conditional_6_Template, 1, 3, "mj-task-graph-run-view", 34)(7, AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Conditional_7_Template, 1, 5, "mj-task-graph-editor", 35);
142
143
  i0.ɵɵelementEnd();
143
144
  } if (rf & 2) {
144
- let tmp_4_0;
145
+ let tmp_7_0;
145
146
  const ctx_r1 = i0.ɵɵnextContext(2);
146
147
  i0.ɵɵadvance(2);
147
148
  i0.ɵɵconditional(ctx_r1.stepTaskGraphWasFolded ? 2 : -1);
148
- i0.ɵɵadvance(4);
149
- i0.ɵɵconditional((tmp_4_0 = ctx_r1.stepTaskGraphParentTaskID) ? 6 : 7, tmp_4_0);
149
+ i0.ɵɵadvance();
150
+ i0.ɵɵproperty("disabled", ctx_r1.SavingWorkflow);
151
+ i0.ɵɵadvance();
152
+ i0.ɵɵproperty("ngClass", ctx_r1.SavingWorkflow ? "fa-spinner fa-spin" : "fa-floppy-disk");
153
+ i0.ɵɵadvance();
154
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.SavingWorkflow ? "Saving Workflow\u2026" : "Save as Workflow", " ");
155
+ i0.ɵɵadvance();
156
+ i0.ɵɵconditional((tmp_7_0 = ctx_r1.stepTaskGraphParentTaskID) ? 6 : 7, tmp_7_0);
150
157
  } }
151
158
  function AIAgentRunStepDetailComponent_Conditional_0_Conditional_29_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
152
159
  i0.ɵɵelementStart(0, "div", 40);
@@ -400,7 +407,7 @@ function AIAgentRunStepDetailComponent_Conditional_0_Template(rf, ctx) { if (rf
400
407
  i0.ɵɵconditionalCreate(25, AIAgentRunStepDetailComponent_Conditional_0_Conditional_25_Template, 4, 0, "div", 11);
401
408
  i0.ɵɵconditionalCreate(26, AIAgentRunStepDetailComponent_Conditional_0_Conditional_26_Template, 8, 6, "div", 12);
402
409
  i0.ɵɵelementStart(27, "div", 13);
403
- i0.ɵɵconditionalCreate(28, AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Template, 8, 2, "div", 14);
410
+ i0.ɵɵconditionalCreate(28, AIAgentRunStepDetailComponent_Conditional_0_Conditional_28_Template, 8, 5, "div", 14);
404
411
  i0.ɵɵconditionalCreate(29, AIAgentRunStepDetailComponent_Conditional_0_Conditional_29_Template, 3, 0, "div", 15);
405
412
  i0.ɵɵconditionalCreate(30, AIAgentRunStepDetailComponent_Conditional_0_Conditional_30_Template, 2, 9, "div", 16);
406
413
  i0.ɵɵconditionalCreate(31, AIAgentRunStepDetailComponent_Conditional_0_Conditional_31_Template, 14, 9, "div", 17);
@@ -573,11 +580,14 @@ export class AIAgentRunStepDetailComponent {
573
580
  set scratchpadSubTab(value) {
574
581
  this.ScratchpadSubTab = value;
575
582
  }
583
+ static { this.EMPTY_SKILL_INVOCATIONS = []; }
576
584
  constructor(cdr) {
577
585
  this.cdr = cdr;
578
586
  this.SelectedTimelineItem = null;
579
587
  /** The provider the host is on, so the run view reads through the right one. */
580
588
  this.Provider = null;
589
+ /** True while the host is persisting the workflow via WorkflowSaveOperation. */
590
+ this.SavingWorkflow = false;
581
591
  this.ClosePanel = new EventEmitter();
582
592
  /**
583
593
  * @deprecated Use {@link ClosePanel}.
@@ -624,10 +634,58 @@ export class AIAgentRunStepDetailComponent {
624
634
  this.SelectedItemJsonString = '{}';
625
635
  this.DetailPaneTab = 'diff';
626
636
  this.ScratchpadSubTab = 'diff';
637
+ this._cacheFingerprint = null;
638
+ this._cachedSelectedItem = null;
639
+ this._stepNoticeCache = null;
640
+ this._selectedItemJsonCache = '{}';
641
+ this._showStepPayloadDiffCache = false;
642
+ this._stepPayloadAtStartObjectCache = null;
643
+ this._stepPayloadAtEndObjectCache = null;
644
+ this._stepSkillInvocationsCache = AIAgentRunStepDetailComponent.EMPTY_SKILL_INVOCATIONS;
645
+ this._stepTaskGraphCache = null;
646
+ this._stepTaskGraphPositionsCache = null;
647
+ this._stepTaskGraphParentTaskIdCache = null;
648
+ this._stepTaskGraphWasFoldedCache = false;
649
+ this._stepScratchpadInputCache = null;
650
+ this._stepScratchpadOutputCache = null;
651
+ this._scratchpadTaskChangeCountCache = 0;
652
+ }
653
+ computeCacheFingerprint(item) {
654
+ if (!item) {
655
+ return '';
656
+ }
657
+ const d = item.data ?? {};
658
+ return `${item.id ?? ''}|${item.type ?? ''}|${item.status ?? ''}|${d.StepType ?? ''}|${Boolean(d.IsWorkflowStep)}|${d.PayloadAtStart ?? ''}|${d.PayloadAtEnd ?? ''}|${typeof d.Skills === 'string' ? d.Skills : JSON.stringify(d.Skills ?? '')}|${typeof d.InputData === 'string' ? d.InputData : JSON.stringify(d.InputData ?? '')}|${typeof d.OutputData === 'string' ? d.OutputData : JSON.stringify(d.OutputData ?? '')}`;
659
+ }
660
+ ensureStepDetailCache() {
661
+ const item = this.SelectedTimelineItem;
662
+ const fp = this.computeCacheFingerprint(item);
663
+ if (this._cachedSelectedItem === item && this._cacheFingerprint === fp) {
664
+ return;
665
+ }
666
+ this._cachedSelectedItem = item;
667
+ this._cacheFingerprint = fp;
668
+ this._stepNoticeCache = this.computeStepNotice(item);
669
+ this._selectedItemJsonCache = this.computeSelectedItemJson(item);
670
+ this._showStepPayloadDiffCache = this.computeShowStepPayloadDiff(item);
671
+ this._stepPayloadAtStartObjectCache = this.computeStepPayloadObject(item, 'PayloadAtStart');
672
+ this._stepPayloadAtEndObjectCache = this.computeStepPayloadObject(item, 'PayloadAtEnd');
673
+ this._stepSkillInvocationsCache = this.computeStepSkillInvocations(item);
674
+ this._stepTaskGraphCache = this.computeStepTaskGraph(item);
675
+ this._stepTaskGraphPositionsCache = this._stepTaskGraphCache
676
+ ? ResolveTaskGraphPositions(this._stepTaskGraphCache)
677
+ : null;
678
+ this._stepTaskGraphParentTaskIdCache = this.computeStepTaskGraphParentTaskID(item);
679
+ this._stepTaskGraphWasFoldedCache = this.computeStepTaskGraphWasFolded(item);
680
+ this._stepScratchpadInputCache = this.extractScratchpadFromField('InputData');
681
+ this._stepScratchpadOutputCache = this.extractScratchpadFromField('OutputData');
682
+ this._scratchpadTaskChangeCountCache = this.computeScratchpadTaskChangeCount(this._stepScratchpadInputCache, this._stepScratchpadOutputCache);
683
+ this.SelectedItemJsonString = this._selectedItemJsonCache;
627
684
  }
628
685
  ngOnChanges() {
686
+ this._cacheFingerprint = null;
687
+ this.ensureStepDetailCache();
629
688
  if (this.SelectedTimelineItem) {
630
- this.SelectedItemJsonString = this.GetSelectedItemJson();
631
689
  // Default to diff tab if step has payload diff, skills for Skill steps (the activation IS
632
690
  // the story there), scratchpad if available, otherwise json
633
691
  if (this.ShowStepPayloadDiff) {
@@ -670,7 +728,10 @@ export class AIAgentRunStepDetailComponent {
670
728
  * half-broken.
671
729
  */
672
730
  get StepNotice() {
673
- const item = this.SelectedTimelineItem;
731
+ this.ensureStepDetailCache();
732
+ return this._stepNoticeCache;
733
+ }
734
+ computeStepNotice(item) {
674
735
  if (!item?.data?.IsWorkflowStep)
675
736
  return null;
676
737
  switch (item.status) {
@@ -699,16 +760,20 @@ export class AIAgentRunStepDetailComponent {
699
760
  }
700
761
  }
701
762
  GetSelectedItemJson() {
702
- if (!this.SelectedTimelineItem)
763
+ this.ensureStepDetailCache();
764
+ return this._selectedItemJsonCache;
765
+ }
766
+ computeSelectedItemJson(item) {
767
+ if (!item)
703
768
  return '{}';
704
769
  // Get all the data from the entity
705
770
  let data;
706
- if (this.SelectedTimelineItem.data instanceof MJAIAgentRunStepEntity) {
771
+ if (item.data instanceof MJAIAgentRunStepEntity) {
707
772
  // If it's a step entity, we need to get the full run data
708
- data = this.SelectedTimelineItem.data.GetAll();
773
+ data = item.data.GetAll();
709
774
  }
710
775
  else {
711
- data = this.SelectedTimelineItem.data;
776
+ data = item.data;
712
777
  }
713
778
  // Apply recursive JSON parsing to the entire data object
714
779
  const parseOptions = {
@@ -727,10 +792,14 @@ export class AIAgentRunStepDetailComponent {
727
792
  * Check if selected timeline item is a step with payload changes
728
793
  */
729
794
  get ShowStepPayloadDiff() {
730
- if (!this.SelectedTimelineItem || this.SelectedTimelineItem.type !== 'step') {
795
+ this.ensureStepDetailCache();
796
+ return this._showStepPayloadDiffCache;
797
+ }
798
+ computeShowStepPayloadDiff(item) {
799
+ if (!item || item.type !== 'step') {
731
800
  return false;
732
801
  }
733
- const stepData = this.SelectedTimelineItem.data;
802
+ const stepData = item.data;
734
803
  if (stepData && (stepData.PayloadAtStart?.trim().length > 0
735
804
  || stepData.PayloadAtEnd?.trim().length > 0)) {
736
805
  return stepData.PayloadAtStart !== stepData.PayloadAtEnd;
@@ -747,33 +816,8 @@ export class AIAgentRunStepDetailComponent {
747
816
  * Get parsed PayloadAtStart for the selected step
748
817
  */
749
818
  get StepPayloadAtStartObject() {
750
- if (!this.SelectedTimelineItem || this.SelectedTimelineItem.type !== 'step') {
751
- return null;
752
- }
753
- const stepData = this.SelectedTimelineItem.data;
754
- if (!stepData?.PayloadAtStart)
755
- return null;
756
- try {
757
- // First, check if PayloadAtStart is a JSON string that needs to be parsed
758
- let payloadData = stepData.PayloadAtStart;
759
- try {
760
- // If PayloadAtStart is a JSON string, parse it first
761
- payloadData = JSON.parse(stepData.PayloadAtStart);
762
- }
763
- catch {
764
- // If it's not valid JSON, use it as-is
765
- payloadData = stepData.PayloadAtStart;
766
- }
767
- const parseOptions = {
768
- extractInlineJson: true,
769
- maxDepth: 100,
770
- debug: false
771
- };
772
- return ParseJSONRecursive(payloadData, parseOptions);
773
- }
774
- catch (e) {
775
- return null;
776
- }
819
+ this.ensureStepDetailCache();
820
+ return this._stepPayloadAtStartObjectCache;
777
821
  }
778
822
  /** @deprecated Use {@link StepPayloadAtStartObject}. */
779
823
  get stepPayloadAtStartObject() {
@@ -783,22 +827,28 @@ export class AIAgentRunStepDetailComponent {
783
827
  * Get parsed PayloadAtEnd for the selected step
784
828
  */
785
829
  get StepPayloadAtEndObject() {
786
- if (!this.SelectedTimelineItem || this.SelectedTimelineItem.type !== 'step') {
830
+ this.ensureStepDetailCache();
831
+ return this._stepPayloadAtEndObjectCache;
832
+ }
833
+ /** @deprecated Use {@link StepPayloadAtEndObject}. */
834
+ get stepPayloadAtEndObject() {
835
+ return this.StepPayloadAtEndObject;
836
+ }
837
+ computeStepPayloadObject(item, field) {
838
+ if (!item || item.type !== 'step') {
787
839
  return null;
788
840
  }
789
- const stepData = this.SelectedTimelineItem.data;
790
- if (!stepData?.PayloadAtEnd)
841
+ const stepData = item.data;
842
+ const rawPayload = stepData?.[field];
843
+ if (!rawPayload)
791
844
  return null;
792
845
  try {
793
- // First, check if PayloadAtEnd is a JSON string that needs to be parsed
794
- let payloadData = stepData.PayloadAtEnd;
846
+ let payloadData = rawPayload;
795
847
  try {
796
- // If PayloadAtEnd is a JSON string, parse it first
797
- payloadData = JSON.parse(stepData.PayloadAtEnd);
848
+ payloadData = JSON.parse(rawPayload);
798
849
  }
799
850
  catch {
800
- // If it's not valid JSON, use it as-is
801
- payloadData = stepData.PayloadAtEnd;
851
+ payloadData = rawPayload;
802
852
  }
803
853
  const parseOptions = {
804
854
  extractInlineJson: true,
@@ -807,14 +857,10 @@ export class AIAgentRunStepDetailComponent {
807
857
  };
808
858
  return ParseJSONRecursive(payloadData, parseOptions);
809
859
  }
810
- catch (e) {
860
+ catch {
811
861
  return null;
812
862
  }
813
863
  }
814
- /** @deprecated Use {@link StepPayloadAtEndObject}. */
815
- get stepPayloadAtEndObject() {
816
- return this.StepPayloadAtEndObject;
817
- }
818
864
  OnClosePanel() {
819
865
  this.ClosePanel.emit();
820
866
  }
@@ -853,18 +899,24 @@ export class AIAgentRunStepDetailComponent {
853
899
  * JSON column. Empty array when the step has no skill linkage or the JSON is malformed.
854
900
  */
855
901
  get StepSkillInvocations() {
856
- if (!this.SelectedTimelineItem || this.SelectedTimelineItem.type !== 'step') {
857
- return [];
902
+ this.ensureStepDetailCache();
903
+ return this._stepSkillInvocationsCache;
904
+ }
905
+ computeStepSkillInvocations(item) {
906
+ if (!item || item.type !== 'step') {
907
+ return AIAgentRunStepDetailComponent.EMPTY_SKILL_INVOCATIONS;
858
908
  }
859
- const raw = this.SelectedTimelineItem.data?.Skills;
909
+ const raw = item.data?.Skills;
860
910
  if (!raw)
861
- return [];
911
+ return AIAgentRunStepDetailComponent.EMPTY_SKILL_INVOCATIONS;
862
912
  try {
863
913
  const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
864
- return Array.isArray(parsed) ? parsed : [];
914
+ return Array.isArray(parsed)
915
+ ? parsed
916
+ : AIAgentRunStepDetailComponent.EMPTY_SKILL_INVOCATIONS;
865
917
  }
866
918
  catch {
867
- return [];
919
+ return AIAgentRunStepDetailComponent.EMPTY_SKILL_INVOCATIONS;
868
920
  }
869
921
  }
870
922
  /** @deprecated Use {@link StepSkillInvocations}. */
@@ -896,11 +948,15 @@ export class AIAgentRunStepDetailComponent {
896
948
  * a malformed or absent payload simply means no tab rather than a broken panel.
897
949
  */
898
950
  get StepTaskGraph() {
899
- if (!this.SelectedTimelineItem || this.SelectedTimelineItem.type !== 'step')
951
+ this.ensureStepDetailCache();
952
+ return this._stepTaskGraphCache;
953
+ }
954
+ computeStepTaskGraph(item) {
955
+ if (!item || item.type !== 'step')
900
956
  return null;
901
- if (this.SelectedTimelineItem.data?.StepType !== 'TaskGraph')
957
+ if (item.data?.StepType !== 'TaskGraph')
902
958
  return null;
903
- for (const raw of [this.SelectedTimelineItem.data?.InputData, this.SelectedTimelineItem.data?.OutputData]) {
959
+ for (const raw of [item.data?.InputData, item.data?.OutputData]) {
904
960
  if (!raw)
905
961
  continue;
906
962
  try {
@@ -930,10 +986,8 @@ export class AIAgentRunStepDetailComponent {
930
986
  * carries the arrangement its author dragged into place, on every node.
931
987
  */
932
988
  get StepTaskGraphPositions() {
933
- const spec = this.StepTaskGraph;
934
- if (!spec)
935
- return null;
936
- return ResolveTaskGraphPositions(spec);
989
+ this.ensureStepDetailCache();
990
+ return this._stepTaskGraphPositionsCache;
937
991
  }
938
992
  /** @deprecated Use {@link StepTaskGraphPositions}. */
939
993
  get stepTaskGraphPositions() {
@@ -953,7 +1007,11 @@ export class AIAgentRunStepDetailComponent {
953
1007
  * drawn as one.
954
1008
  */
955
1009
  get StepTaskGraphParentTaskID() {
956
- const raw = this.SelectedTimelineItem?.data?.OutputData;
1010
+ this.ensureStepDetailCache();
1011
+ return this._stepTaskGraphParentTaskIdCache;
1012
+ }
1013
+ computeStepTaskGraphParentTaskID(item) {
1014
+ const raw = item?.data?.OutputData;
957
1015
  if (!raw)
958
1016
  return null;
959
1017
  try {
@@ -971,9 +1029,13 @@ export class AIAgentRunStepDetailComponent {
971
1029
  }
972
1030
  /** True when the recorded graph was constant-folded rather than dispatched (D9). */
973
1031
  get StepTaskGraphWasFolded() {
974
- if (!this.SelectedTimelineItem)
1032
+ this.ensureStepDetailCache();
1033
+ return this._stepTaskGraphWasFoldedCache;
1034
+ }
1035
+ computeStepTaskGraphWasFolded(item) {
1036
+ if (!item)
975
1037
  return false;
976
- const raw = this.SelectedTimelineItem.data?.InputData;
1038
+ const raw = item.data?.InputData;
977
1039
  if (!raw)
978
1040
  return false;
979
1041
  try {
@@ -990,7 +1052,7 @@ export class AIAgentRunStepDetailComponent {
990
1052
  }
991
1053
  /** Asks the host to save this run's graph as a reusable workflow. */
992
1054
  RequestSaveAsWorkflow() {
993
- const spec = this.StepTaskGraph;
1055
+ const spec = this.StepTaskGraph ?? this.TaskGraphRunView?.Spec ?? null;
994
1056
  if (spec)
995
1057
  this.SaveAsWorkflowRequested.emit(spec);
996
1058
  }
@@ -1011,7 +1073,8 @@ export class AIAgentRunStepDetailComponent {
1011
1073
  * Parse scratchpad snapshot from InputData JSON
1012
1074
  */
1013
1075
  get StepScratchpadInput() {
1014
- return this.extractScratchpadFromField('InputData');
1076
+ this.ensureStepDetailCache();
1077
+ return this._stepScratchpadInputCache;
1015
1078
  }
1016
1079
  /** @deprecated Use {@link StepScratchpadInput}. */
1017
1080
  get stepScratchpadInput() {
@@ -1021,7 +1084,8 @@ export class AIAgentRunStepDetailComponent {
1021
1084
  * Parse scratchpad snapshot from OutputData JSON
1022
1085
  */
1023
1086
  get StepScratchpadOutput() {
1024
- return this.extractScratchpadFromField('OutputData');
1087
+ this.ensureStepDetailCache();
1088
+ return this._stepScratchpadOutputCache;
1025
1089
  }
1026
1090
  /** @deprecated Use {@link StepScratchpadOutput}. */
1027
1091
  get stepScratchpadOutput() {
@@ -1031,8 +1095,10 @@ export class AIAgentRunStepDetailComponent {
1031
1095
  * Number of tasks that changed between input and output scratchpad snapshots
1032
1096
  */
1033
1097
  get ScratchpadTaskChangeCount() {
1034
- const input = this.StepScratchpadInput;
1035
- const output = this.StepScratchpadOutput;
1098
+ this.ensureStepDetailCache();
1099
+ return this._scratchpadTaskChangeCountCache;
1100
+ }
1101
+ computeScratchpadTaskChangeCount(input, output) {
1036
1102
  if (!input && !output)
1037
1103
  return 0;
1038
1104
  if (!input || !output)
@@ -1084,7 +1150,12 @@ export class AIAgentRunStepDetailComponent {
1084
1150
  }
1085
1151
  }
1086
1152
  static { this.ɵfac = function AIAgentRunStepDetailComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AIAgentRunStepDetailComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
1087
- static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AIAgentRunStepDetailComponent, selectors: [["mj-ai-agent-run-step-detail"]], inputs: { SelectedTimelineItem: "SelectedTimelineItem", selectedTimelineItem: "selectedTimelineItem", Provider: "Provider" }, outputs: { ClosePanel: "ClosePanel", closePanel: "closePanel", NavigateToActionLog: "NavigateToActionLog", navigateToActionLog: "navigateToActionLog", CopyToClipboard: "CopyToClipboard", copyToClipboard: "copyToClipboard", SaveAsWorkflowRequested: "SaveAsWorkflowRequested", saveAsWorkflowRequested: "saveAsWorkflowRequested" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 1, consts: [["scratchpadSnapshotTpl", ""], [1, "json-detail-pane"], [1, "json-pane-header"], [1, "fas", 3, "ngClass"], ["title", "Close", 1, "btn-icon", 3, "click"], [1, "fas", "fa-times"], [1, "json-pane-content"], [1, "json-meta"], [1, "meta-item"], [1, "status-badge", "small"], [1, "step-notice"], [1, "action-link"], [1, "detail-pane-tabs"], [1, "detail-pane-content"], [1, "detail-graph"], [1, "skills-content"], [1, "step-payload-diff"], [1, "scratchpad-content"], [1, "json-viewer"], ["Size", "sm", 3, "Variant", "Message"], [1, "btn-link", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "detail-tab", 3, "active"], [1, "detail-tab", 3, "click"], [1, "fa-solid", "fa-code"], [1, "fa-solid", "fa-diagram-project"], [1, "fa-solid", "fa-code-branch"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "tab-badge"], [1, "fa-solid", "fa-clipboard-list"], [1, "detail-graph__actions"], ["Variant", "info", "Size", "sm", "Message", "This ran inline as a single step \u2014 a workflow of one needs no scheduling. You can still save it."], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-floppy-disk"], ["Height", "100%", 1, "detail-graph__canvas", 3, "Provider", "ParentTaskID", "WorkflowName"], [1, "detail-graph__canvas", 3, "Spec", "NodePositions", "ReadOnly", "ShowPalette", "ShowLegend"], [1, "skill-invocation-card"], [1, "skill-invocation-header"], [1, "skill-invocation-name"], [1, "skill-invocation-type"], [1, "skill-invocation-reason"], [1, "skill-invocation-provenance"], [1, "provenance-title"], [1, "provenance-row"], [1, "provenance-label"], [1, "provenance-value"], [1, "provenance-value", "provenance-id"], [1, "fa-solid", "fa-quote-left"], [3, "oldValue", "newValue", "title", "showSummary", "showUnchanged", "expandAll", "maxDepth", "maxStringLength", "treatNullAsUndefined"], [1, "scratchpad-sub-tabs"], [1, "scratchpad-sub-tab", 3, "click"], [1, "fa-solid", "fa-code-compare"], [1, "fa-solid", "fa-arrow-right-to-bracket"], [1, "fa-solid", "fa-arrow-right-from-bracket"], [1, "scratchpad-diff-view"], [1, "scratchpad-snapshot-view"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad changes in this step"], ["Size", "compact", "Icon", "fa-solid fa-circle-plus", "Title", "Scratchpad created in this step (no prior state)"], [1, "scratchpad-snapshot"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad input for this step"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad output for this step"], [1, "json-toolbar"], ["title", "Copy JSON", 1, "btn-icon", 3, "click"], [1, "fas", "fa-copy"], [2, "width", "100%", 3, "ngModelChange", "ngModel", "language", "readonly"], [1, "scratchpad-notes-section"], [1, "scratchpad-tasks-section"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "Empty scratchpad"], [1, "fa-solid", "fa-note-sticky"], [1, "scratchpad-notes-content"], [1, "fa-solid", "fa-list-check"], [1, "scratchpad-task-list"], [1, "scratchpad-task-item"], [1, "task-status-indicator"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-spinner"], [1, "fa-solid", "fa-circle-xmark"], [1, "fa-regular", "fa-circle"], [1, "task-content"], [1, "task-id"], [1, "task-title"], [1, "task-notes"], [1, "task-status-badge"]], template: function AIAgentRunStepDetailComponent_Template(rf, ctx) { if (rf & 1) {
1153
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AIAgentRunStepDetailComponent, selectors: [["mj-ai-agent-run-step-detail"]], viewQuery: function AIAgentRunStepDetailComponent_Query(rf, ctx) { if (rf & 1) {
1154
+ i0.ɵɵviewQuery(TaskGraphRunViewComponent, 5);
1155
+ } if (rf & 2) {
1156
+ let _t;
1157
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.TaskGraphRunView = _t.first);
1158
+ } }, inputs: { SelectedTimelineItem: "SelectedTimelineItem", selectedTimelineItem: "selectedTimelineItem", Provider: "Provider", SavingWorkflow: "SavingWorkflow" }, outputs: { ClosePanel: "ClosePanel", closePanel: "closePanel", NavigateToActionLog: "NavigateToActionLog", navigateToActionLog: "navigateToActionLog", CopyToClipboard: "CopyToClipboard", copyToClipboard: "copyToClipboard", SaveAsWorkflowRequested: "SaveAsWorkflowRequested", saveAsWorkflowRequested: "saveAsWorkflowRequested" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 3, vars: 1, consts: [["scratchpadSnapshotTpl", ""], [1, "json-detail-pane"], [1, "json-pane-header"], [1, "fas", 3, "ngClass"], ["title", "Close", 1, "btn-icon", 3, "click"], [1, "fas", "fa-times"], [1, "json-pane-content"], [1, "json-meta"], [1, "meta-item"], [1, "status-badge", "small"], [1, "step-notice"], [1, "action-link"], [1, "detail-pane-tabs"], [1, "detail-pane-content"], [1, "detail-graph"], [1, "skills-content"], [1, "step-payload-diff"], [1, "scratchpad-content"], [1, "json-viewer"], ["Size", "sm", 3, "Variant", "Message"], [1, "btn-link", 3, "click"], [1, "fas", "fa-external-link-alt"], [1, "detail-tab", 3, "active"], [1, "detail-tab", 3, "click"], [1, "fa-solid", "fa-code"], [1, "fa-solid", "fa-diagram-project"], [1, "fa-solid", "fa-code-branch"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "tab-badge"], [1, "fa-solid", "fa-clipboard-list"], [1, "detail-graph__actions"], ["Variant", "info", "Size", "sm", "Message", "This ran inline as a single step \u2014 a workflow of one needs no scheduling. You can still save it."], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], [1, "fa-solid", 3, "ngClass"], ["Height", "100%", 1, "detail-graph__canvas", 3, "Provider", "ParentTaskID", "WorkflowName"], [1, "detail-graph__canvas", 3, "Spec", "NodePositions", "ReadOnly", "ShowPalette", "ShowLegend"], [1, "skill-invocation-card"], [1, "skill-invocation-header"], [1, "skill-invocation-name"], [1, "skill-invocation-type"], [1, "skill-invocation-reason"], [1, "skill-invocation-provenance"], [1, "provenance-title"], [1, "provenance-row"], [1, "provenance-label"], [1, "provenance-value"], [1, "provenance-value", "provenance-id"], [1, "fa-solid", "fa-quote-left"], [3, "oldValue", "newValue", "title", "showSummary", "showUnchanged", "expandAll", "maxDepth", "maxStringLength", "treatNullAsUndefined"], [1, "scratchpad-sub-tabs"], [1, "scratchpad-sub-tab", 3, "click"], [1, "fa-solid", "fa-code-compare"], [1, "fa-solid", "fa-arrow-right-to-bracket"], [1, "fa-solid", "fa-arrow-right-from-bracket"], [1, "scratchpad-diff-view"], [1, "scratchpad-snapshot-view"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad changes in this step"], ["Size", "compact", "Icon", "fa-solid fa-circle-plus", "Title", "Scratchpad created in this step (no prior state)"], [1, "scratchpad-snapshot"], [4, "ngTemplateOutlet", "ngTemplateOutletContext"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad input for this step"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "No scratchpad output for this step"], [1, "json-toolbar"], ["title", "Copy JSON", 1, "btn-icon", 3, "click"], [1, "fas", "fa-copy"], [2, "width", "100%", 3, "ngModelChange", "ngModel", "language", "readonly"], [1, "scratchpad-notes-section"], [1, "scratchpad-tasks-section"], ["Size", "compact", "Icon", "fa-solid fa-circle-info", "Title", "Empty scratchpad"], [1, "fa-solid", "fa-note-sticky"], [1, "scratchpad-notes-content"], [1, "fa-solid", "fa-list-check"], [1, "scratchpad-task-list"], [1, "scratchpad-task-item"], [1, "task-status-indicator"], [1, "fa-solid", "fa-circle-check"], [1, "fa-solid", "fa-spinner"], [1, "fa-solid", "fa-circle-xmark"], [1, "fa-regular", "fa-circle"], [1, "task-content"], [1, "task-id"], [1, "task-title"], [1, "task-notes"], [1, "task-status-badge"]], template: function AIAgentRunStepDetailComponent_Template(rf, ctx) { if (rf & 1) {
1088
1159
  i0.ɵɵconditionalCreate(0, AIAgentRunStepDetailComponent_Conditional_0_Template, 33, 16, "div", 1);
1089
1160
  i0.ɵɵtemplate(1, AIAgentRunStepDetailComponent_ng_template_1_Template, 4, 3, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
1090
1161
  } if (rf & 2) {
@@ -1093,13 +1164,15 @@ export class AIAgentRunStepDetailComponent {
1093
1164
  }
1094
1165
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AIAgentRunStepDetailComponent, [{
1095
1166
  type: Component,
1096
- args: [{ standalone: false, selector: 'mj-ai-agent-run-step-detail', template: "@if (selectedTimelineItem) {\n <div class=\"json-detail-pane\">\n <div class=\"json-pane-header\">\n <h3>\n <i class=\"fas\" [ngClass]=\"selectedTimelineItem.icon\"></i>\n {{ selectedTimelineItem.title }}\n </h3>\n <button class=\"btn-icon\" (click)=\"onClosePanel()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"json-pane-content\">\n <div class=\"json-meta\">\n <div class=\"meta-item\">\n <label>Type</label>\n <span>{{ selectedTimelineItem.type }}</span>\n </div>\n <div class=\"meta-item\">\n <label>Status</label>\n <span class=\"status-badge small\" [attr.data-status]=\"selectedTimelineItem.status\">\n {{ selectedTimelineItem.status }}\n </span>\n </div>\n <div class=\"meta-item\">\n <label>Duration</label>\n <span>{{ selectedTimelineItem.duration || 'N/A' }}</span>\n </div>\n </div>\n <!-- Why a workflow step has nothing to show: it never ran. Above the tabs, because it explains\n the emptiness the reader is about to look at. -->\n @if (StepNotice) {\n <div class=\"step-notice\">\n <mj-alert [Variant]=\"StepNotice.Variant\" Size=\"sm\" [Message]=\"StepNotice.Message\"></mj-alert>\n </div>\n }\n <!-- Action-specific link -->\n @if (selectedTimelineItem.type === 'action' && selectedTimelineItem.data?.ID) {\n <div class=\"action-link\">\n <button class=\"btn-link\" (click)=\"onNavigateToActionLog(selectedTimelineItem.data.ID)\">\n <i class=\"fas fa-external-link-alt\"></i> View Action Execution Log\n </button>\n </div>\n }\n <!-- Tab Navigation - Show when we have payload diff or scratchpad data -->\n @if (showStepPayloadDiff || showScratchpadTab || showSkillsTab || showGraphTab) {\n <div class=\"detail-pane-tabs\">\n @if (showGraphTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'graph'\"\n (click)=\"detailPaneTab = 'graph'\">\n <i class=\"fa-solid fa-diagram-project\"></i>\n Workflow\n </button>\n }\n @if (showStepPayloadDiff) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'diff'\"\n (click)=\"detailPaneTab = 'diff'\">\n <i class=\"fa-solid fa-code-branch\"></i>\n Payload Changes\n </button>\n }\n @if (showSkillsTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'skills'\"\n (click)=\"detailPaneTab = 'skills'\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n Skills\n <span class=\"tab-badge\">{{ stepSkillInvocations.length }}</span>\n </button>\n }\n @if (showScratchpadTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'scratchpad'\"\n (click)=\"detailPaneTab = 'scratchpad'\">\n <i class=\"fa-solid fa-clipboard-list\"></i>\n Scratchpad\n @if (scratchpadTaskChangeCount > 0) {\n <span class=\"tab-badge\">{{ scratchpadTaskChangeCount }}</span>\n }\n </button>\n }\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'json'\"\n (click)=\"detailPaneTab = 'json'\">\n <i class=\"fa-solid fa-code\"></i>\n Full JSON\n </button>\n </div>\n }\n <!-- Tab Content -->\n <div class=\"detail-pane-content\" [class.with-tabs]=\"showStepPayloadDiff || showScratchpadTab || showSkillsTab || showGraphTab\">\n <!-- Skills Tab \u2014 skill-invocation observability (activation type, provenance of authority, reason) -->\n @if (detailPaneTab === 'graph' && showGraphTab) {\n <div class=\"detail-graph\">\n <div class=\"detail-graph__actions\">\n @if (stepTaskGraphWasFolded) {\n <!-- The fold is recorded rather than silent (D9), so the reader can see WHY this\n graph never reached the dispatcher instead of inferring it. -->\n <mj-alert Variant=\"info\" Size=\"sm\"\n Message=\"This ran inline as a single step \u2014 a workflow of one needs no scheduling. You can still save it.\">\n </mj-alert>\n }\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"RequestSaveAsWorkflow()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save as Workflow\n </button>\n </div>\n @if (stepTaskGraphParentTaskID; as parentTaskID) {\n <!-- The RUN, not the plan. Same component the Workflows app uses, so a workflow is\n drawn identically wherever it is opened from \u2014 declined branches hatched,\n untravelled edges absent, toolbar on, legend off. -->\n <mj-task-graph-run-view\n class=\"detail-graph__canvas\"\n [Provider]=\"Provider\"\n [ParentTaskID]=\"parentTaskID\"\n [WorkflowName]=\"stepTaskGraph?.workflowName || 'Workflow'\"\n Height=\"100%\">\n </mj-task-graph-run-view>\n } @else {\n <!-- No dispatcher rows to read: a folded graph never ran. Drawing the spec is the\n honest rendering \u2014 it is a plan, and it is drawn as one. -->\n <mj-task-graph-editor\n class=\"detail-graph__canvas\"\n [Spec]=\"stepTaskGraph\"\n [NodePositions]=\"stepTaskGraphPositions\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowLegend]=\"false\">\n </mj-task-graph-editor>\n }\n </div>\n }\n @if (detailPaneTab === 'skills' && showSkillsTab) {\n <div class=\"skills-content\">\n @for (inv of stepSkillInvocations; track inv.SkillID) {\n <div class=\"skill-invocation-card\" [attr.data-activation]=\"inv.ActivationType\">\n <div class=\"skill-invocation-header\">\n <span class=\"skill-invocation-name\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n {{ inv.SkillName }}\n </span>\n <span class=\"skill-invocation-type\" [attr.data-activation]=\"inv.ActivationType\">\n {{ GetActivationTypeLabel(inv) }}\n </span>\n </div>\n @if (inv.Reason) {\n <div class=\"skill-invocation-reason\">\n <i class=\"fa-solid fa-quote-left\"></i>\n {{ inv.Reason }}\n </div>\n }\n <div class=\"skill-invocation-provenance\">\n <div class=\"provenance-title\">Provenance of Authority</div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Requested By</span>\n <span class=\"provenance-value\">{{ inv.Provenance.RequestedBy === 'user-request' ? 'User (/skill mention)' : 'Agent decision' }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Agent AcceptsSkills</span>\n <span class=\"provenance-value\">{{ inv.Provenance.AgentAcceptsSkills }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Skill ActivationMode</span>\n <span class=\"provenance-value\">{{ inv.Provenance.SkillActivationMode }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Agent SkillActivationMode</span>\n <span class=\"provenance-value\">{{ inv.Provenance.AgentSkillActivationMode }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Skill ID</span>\n <span class=\"provenance-value provenance-id\">{{ inv.SkillID }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n <!-- Payload Diff Tab -->\n @if (detailPaneTab === 'diff' && showStepPayloadDiff) {\n <div class=\"step-payload-diff\">\n <mj-deep-diff\n [oldValue]=\"stepPayloadAtStartObject\"\n [newValue]=\"stepPayloadAtEndObject\"\n [title]=\"''\"\n [showSummary]=\"true\"\n [showUnchanged]=\"false\"\n [expandAll]=\"false\"\n [maxDepth]=\"8\"\n [maxStringLength]=\"150\"\n [treatNullAsUndefined]=\"true\">\n </mj-deep-diff>\n </div>\n }\n <!-- Scratchpad Tab -->\n @if (detailPaneTab === 'scratchpad' && showScratchpadTab) {\n <div class=\"scratchpad-content\">\n <div class=\"scratchpad-sub-tabs\">\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'diff'\"\n (click)=\"scratchpadSubTab = 'diff'\">\n <i class=\"fa-solid fa-code-compare\"></i> Diff\n </button>\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'input'\"\n (click)=\"scratchpadSubTab = 'input'\">\n <i class=\"fa-solid fa-arrow-right-to-bracket\"></i> Input\n </button>\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'output'\"\n (click)=\"scratchpadSubTab = 'output'\">\n <i class=\"fa-solid fa-arrow-right-from-bracket\"></i> Output\n </button>\n </div>\n <!-- Scratchpad Diff Sub-Tab -->\n @if (scratchpadSubTab === 'diff') {\n <div class=\"scratchpad-diff-view\">\n @if (stepScratchpadInput && stepScratchpadOutput) {\n <mj-deep-diff\n [oldValue]=\"stepScratchpadInput\"\n [newValue]=\"stepScratchpadOutput\"\n [title]=\"''\"\n [showSummary]=\"true\"\n [showUnchanged]=\"false\"\n [expandAll]=\"true\"\n [maxDepth]=\"6\"\n [maxStringLength]=\"300\"\n [treatNullAsUndefined]=\"true\">\n </mj-deep-diff>\n } @else if (stepScratchpadOutput) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-plus\"\n Title=\"Scratchpad created in this step (no prior state)\" />\n <div class=\"scratchpad-snapshot\">\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadOutput }\"></ng-container>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad changes in this step\" />\n }\n </div>\n }\n <!-- Scratchpad Input Sub-Tab -->\n @if (scratchpadSubTab === 'input') {\n <div class=\"scratchpad-snapshot-view\">\n @if (stepScratchpadInput) {\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadInput }\"></ng-container>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad input for this step\" />\n }\n </div>\n }\n <!-- Scratchpad Output Sub-Tab -->\n @if (scratchpadSubTab === 'output') {\n <div class=\"scratchpad-snapshot-view\">\n @if (stepScratchpadOutput) {\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadOutput }\"></ng-container>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad output for this step\" />\n }\n </div>\n }\n </div>\n }\n <!-- Full JSON Tab -->\n @if (detailPaneTab === 'json' || (!showStepPayloadDiff && !showScratchpadTab)) {\n <div class=\"json-viewer\">\n <div class=\"json-toolbar\">\n <button class=\"btn-icon\" title=\"Copy JSON\" (click)=\"onCopyToClipboard()\">\n <i class=\"fas fa-copy\"></i>\n </button>\n </div>\n <mj-code-editor\n [(ngModel)]=\"selectedItemJsonString\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n style=\"width: 100%;\">\n </mj-code-editor>\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Reusable scratchpad snapshot template -->\n<ng-template #scratchpadSnapshotTpl let-snapshot>\n <div class=\"scratchpad-snapshot\">\n @if (snapshot.notes) {\n <div class=\"scratchpad-notes-section\">\n <h4><i class=\"fa-solid fa-note-sticky\"></i> Notes</h4>\n <div class=\"scratchpad-notes-content\">{{ snapshot.notes }}</div>\n </div>\n }\n @if (snapshot.tasks?.length > 0) {\n <div class=\"scratchpad-tasks-section\">\n <h4><i class=\"fa-solid fa-list-check\"></i> Tasks ({{ snapshot.tasks.length }})</h4>\n <div class=\"scratchpad-task-list\">\n @for (task of snapshot.tasks; track task.id) {\n <div class=\"scratchpad-task-item\" [attr.data-status]=\"task.status\">\n <span class=\"task-status-indicator\" [attr.data-status]=\"task.status\">\n @switch (task.status) {\n @case ('completed') { <i class=\"fa-solid fa-circle-check\"></i> }\n @case ('in_progress') { <i class=\"fa-solid fa-spinner\"></i> }\n @case ('blocked') { <i class=\"fa-solid fa-circle-xmark\"></i> }\n @default { <i class=\"fa-regular fa-circle\"></i> }\n }\n </span>\n <div class=\"task-content\">\n <span class=\"task-id\">{{ task.id }}</span>\n <span class=\"task-title\">{{ task.title }}</span>\n @if (task.notes) {\n <span class=\"task-notes\">{{ task.notes }}</span>\n }\n </div>\n <span class=\"task-status-badge\" [attr.data-status]=\"task.status\">{{ task.status }}</span>\n </div>\n }\n </div>\n </div>\n }\n @if (!snapshot.notes && (!snapshot.tasks || snapshot.tasks.length === 0)) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"Empty scratchpad\" />\n }\n </div>\n</ng-template>", styles: [".json-detail-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n}\n\n.json-pane-header {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: var(--mj-bg-surface-sunken);\n}\n\n.json-pane-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.btn-icon {\n background: none;\n border: none;\n padding: 4px 8px;\n cursor: pointer;\n color: var(--mj-text-muted);\n border-radius: 4px;\n transition: all 0.2s;\n}\n\n.btn-icon:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n color: var(--mj-text-secondary);\n}\n\n.json-pane-content {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n}\n\n.json-meta {\n padding: 16px;\n display: flex;\n gap: 24px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.meta-item label {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n font-weight: 600;\n}\n\n.meta-item span {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.status-badge {\n display: inline-flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.status-badge.small {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.status-badge[data-status=\"Running\"] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.status-badge[data-status=\"Completed\"] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.status-badge[data-status=\"Failed\"] {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.status-badge[data-status=\"Cancelled\"] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.action-link {\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.btn-link {\n background: none;\n border: none;\n color: var(--mj-brand-primary);\n cursor: pointer;\n padding: 6px 12px;\n border-radius: 4px;\n transition: all 0.2s;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n\n.btn-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.detail-pane-tabs {\n display: flex;\n gap: 0;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 0 16px;\n}\n\n.detail-tab {\n padding: 12px 16px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-muted);\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.detail-tab:hover {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-text-primary) 2%, transparent);\n}\n\n.detail-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.detail-pane-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0; /* Allow flexbox to properly size */\n}\n\n/* \n.detail-pane-content.with-tabs {\n // No explicit height needed with flexbox \n} \n*/\n\n.json-viewer {\n flex: 1;\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: hidden;\n min-height: 0; /* Important for flexbox children to respect overflow */\n}\n\n.json-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n display: flex;\n gap: 4px;\n}\n\n.step-payload-diff {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n min-height: 0; /* Allow proper scrolling */\n}\n\n/* Specific styling for mj-code-editor container */\n.json-viewer mj-code-editor {\n flex: 1;\n overflow: hidden;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* Tab badge for change count */\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 9px;\n font-size: 10px;\n font-weight: 700;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n/* Scratchpad content area */\n.scratchpad-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0;\n}\n\n/* Scratchpad sub-tabs */\n.scratchpad-sub-tabs {\n display: flex;\n gap: 0;\n padding: 0 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.scratchpad-sub-tab {\n padding: 8px 12px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n font-size: 12px;\n color: var(--mj-text-muted);\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n\n.scratchpad-sub-tab:hover {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-text-primary) 2%, transparent);\n}\n\n.scratchpad-sub-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* Scratchpad diff and snapshot views */\n.scratchpad-diff-view,\n.scratchpad-snapshot-view {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n min-height: 0;\n}\n\n/* Scratchpad snapshot rendering */\n.scratchpad-snapshot {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.scratchpad-notes-section h4,\n.scratchpad-tasks-section h4 {\n margin: 0 0 8px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.scratchpad-notes-content {\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n border: 1px solid var(--mj-border-subtle);\n font-size: 13px;\n line-height: 1.5;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* Task list */\n.scratchpad-task-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scratchpad-task-item {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n transition: background 0.15s;\n}\n\n.scratchpad-task-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Task status indicator icon */\n.task-status-indicator {\n flex-shrink: 0;\n width: 16px;\n margin-top: 2px;\n font-size: 14px;\n}\n\n.task-status-indicator[data-status=\"completed\"] {\n color: var(--mj-status-success);\n}\n\n.task-status-indicator[data-status=\"in_progress\"] {\n color: var(--mj-brand-primary);\n}\n\n.task-status-indicator[data-status=\"blocked\"] {\n color: var(--mj-status-error);\n}\n\n.task-status-indicator[data-status=\"pending\"] {\n color: var(--mj-text-muted);\n}\n\n/* Task content */\n.task-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.task-id {\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n}\n\n.task-title {\n font-size: 13px;\n color: var(--mj-text-primary);\n line-height: 1.4;\n}\n\n.task-notes {\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n font-style: italic;\n}\n\n/* Task status badge */\n.task-status-badge {\n flex-shrink: 0;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n white-space: nowrap;\n}\n\n.task-status-badge[data-status=\"completed\"] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.task-status-badge[data-status=\"in_progress\"] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.task-status-badge[data-status=\"blocked\"] {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.task-status-badge[data-status=\"pending\"] {\n background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n}\n\n/* ============================================================\n Skills tab \u2014 skill-invocation observability cards\n ============================================================ */\n.skills-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n\n.skill-invocation-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.skill-invocation-card[data-activation=\"auto\"] {\n border-left-color: var(--mj-status-warning);\n}\n\n.skill-invocation-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.skill-invocation-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n.skill-invocation-name i {\n color: var(--mj-brand-primary);\n}\n\n.skill-invocation-type {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 2px 10px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.skill-invocation-type[data-activation=\"auto\"] {\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.skill-invocation-reason {\n margin-top: 8px;\n font-size: 13px;\n font-style: italic;\n color: var(--mj-text-secondary);\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n\n.skill-invocation-reason i {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n\n.skill-invocation-provenance {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.provenance-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n}\n\n.provenance-row {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n padding: 2px 0;\n}\n\n.provenance-label {\n color: var(--mj-text-secondary);\n}\n\n.provenance-value {\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.provenance-id {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* Workflow tab \u2014 the recorded task graph, read-only, with the Save as Workflow action. */\n.detail-graph {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n\n.detail-graph__actions {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n padding: var(--mj-space-sm, 0.5rem);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n/* min-height:0 is what lets the canvas actually shrink inside the flex column. */\n.detail-graph__canvas {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* Explanation for a step that never executed \u2014 see StepNotice. Sits above the tabs because it\n accounts for the emptiness the reader is about to look at. */\n.step-notice {\n padding: 0 var(--mj-space-3, 12px) var(--mj-space-2, 8px);\n}\n\n/* The run view fills the tab rather than taking its intrinsic size \u2014 a canvas that stops halfway\n down its pane with dead space beneath reads as a broken layout. */\n.detail-graph {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.detail-graph__canvas {\n flex: 1 1 auto;\n min-height: 0;\n}\n"] }]
1167
+ args: [{ standalone: false, selector: 'mj-ai-agent-run-step-detail', template: "@if (selectedTimelineItem) {\n <div class=\"json-detail-pane\">\n <div class=\"json-pane-header\">\n <h3>\n <i class=\"fas\" [ngClass]=\"selectedTimelineItem.icon\"></i>\n {{ selectedTimelineItem.title }}\n </h3>\n <button class=\"btn-icon\" (click)=\"onClosePanel()\" title=\"Close\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"json-pane-content\">\n <div class=\"json-meta\">\n <div class=\"meta-item\">\n <label>Type</label>\n <span>{{ selectedTimelineItem.type }}</span>\n </div>\n <div class=\"meta-item\">\n <label>Status</label>\n <span class=\"status-badge small\" [attr.data-status]=\"selectedTimelineItem.status\">\n {{ selectedTimelineItem.status }}\n </span>\n </div>\n <div class=\"meta-item\">\n <label>Duration</label>\n <span>{{ selectedTimelineItem.duration || 'N/A' }}</span>\n </div>\n </div>\n <!-- Why a workflow step has nothing to show: it never ran. Above the tabs, because it explains\n the emptiness the reader is about to look at. -->\n @if (StepNotice) {\n <div class=\"step-notice\">\n <mj-alert [Variant]=\"StepNotice.Variant\" Size=\"sm\" [Message]=\"StepNotice.Message\"></mj-alert>\n </div>\n }\n <!-- Action-specific link -->\n @if (selectedTimelineItem.type === 'action' && selectedTimelineItem.data?.ID) {\n <div class=\"action-link\">\n <button class=\"btn-link\" (click)=\"onNavigateToActionLog(selectedTimelineItem.data.ID)\">\n <i class=\"fas fa-external-link-alt\"></i> View Action Execution Log\n </button>\n </div>\n }\n <!-- Tab Navigation - Show when we have payload diff or scratchpad data -->\n @if (showStepPayloadDiff || showScratchpadTab || showSkillsTab || showGraphTab) {\n <div class=\"detail-pane-tabs\">\n @if (showGraphTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'graph'\"\n (click)=\"detailPaneTab = 'graph'\">\n <i class=\"fa-solid fa-diagram-project\"></i>\n Workflow\n </button>\n }\n @if (showStepPayloadDiff) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'diff'\"\n (click)=\"detailPaneTab = 'diff'\">\n <i class=\"fa-solid fa-code-branch\"></i>\n Payload Changes\n </button>\n }\n @if (showSkillsTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'skills'\"\n (click)=\"detailPaneTab = 'skills'\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n Skills\n <span class=\"tab-badge\">{{ stepSkillInvocations.length }}</span>\n </button>\n }\n @if (showScratchpadTab) {\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'scratchpad'\"\n (click)=\"detailPaneTab = 'scratchpad'\">\n <i class=\"fa-solid fa-clipboard-list\"></i>\n Scratchpad\n @if (scratchpadTaskChangeCount > 0) {\n <span class=\"tab-badge\">{{ scratchpadTaskChangeCount }}</span>\n }\n </button>\n }\n <button\n class=\"detail-tab\"\n [class.active]=\"detailPaneTab === 'json'\"\n (click)=\"detailPaneTab = 'json'\">\n <i class=\"fa-solid fa-code\"></i>\n Full JSON\n </button>\n </div>\n }\n <!-- Tab Content -->\n <div class=\"detail-pane-content\" [class.with-tabs]=\"showStepPayloadDiff || showScratchpadTab || showSkillsTab || showGraphTab\">\n <!-- Skills Tab \u2014 skill-invocation observability (activation type, provenance of authority, reason) -->\n @if (detailPaneTab === 'graph' && showGraphTab) {\n <div class=\"detail-graph\">\n <div class=\"detail-graph__actions\">\n @if (stepTaskGraphWasFolded) {\n <!-- The fold is recorded rather than silent (D9), so the reader can see WHY this\n graph never reached the dispatcher instead of inferring it. -->\n <mj-alert Variant=\"info\" Size=\"sm\"\n Message=\"This ran inline as a single step \u2014 a workflow of one needs no scheduling. You can still save it.\">\n </mj-alert>\n }\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"SavingWorkflow\" (click)=\"RequestSaveAsWorkflow()\">\n <i class=\"fa-solid\" [ngClass]=\"SavingWorkflow ? 'fa-spinner fa-spin' : 'fa-floppy-disk'\"></i>\n {{ SavingWorkflow ? 'Saving Workflow\u2026' : 'Save as Workflow' }}\n </button>\n </div>\n @if (stepTaskGraphParentTaskID; as parentTaskID) {\n <!-- The RUN, not the plan. Same component the Workflows app uses, so a workflow is\n drawn identically wherever it is opened from \u2014 declined branches hatched,\n untravelled edges absent, toolbar on, legend off. -->\n <mj-task-graph-run-view\n class=\"detail-graph__canvas\"\n [Provider]=\"Provider\"\n [ParentTaskID]=\"parentTaskID\"\n [WorkflowName]=\"stepTaskGraph?.workflowName || 'Workflow'\"\n Height=\"100%\">\n </mj-task-graph-run-view>\n } @else {\n <!-- No dispatcher rows to read: a folded graph never ran. Drawing the spec is the\n honest rendering \u2014 it is a plan, and it is drawn as one. -->\n <mj-task-graph-editor\n class=\"detail-graph__canvas\"\n [Spec]=\"stepTaskGraph\"\n [NodePositions]=\"stepTaskGraphPositions\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowLegend]=\"false\">\n </mj-task-graph-editor>\n }\n </div>\n }\n @if (detailPaneTab === 'skills' && showSkillsTab) {\n <div class=\"skills-content\">\n @for (inv of stepSkillInvocations; track inv.SkillID) {\n <div class=\"skill-invocation-card\" [attr.data-activation]=\"inv.ActivationType\">\n <div class=\"skill-invocation-header\">\n <span class=\"skill-invocation-name\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n {{ inv.SkillName }}\n </span>\n <span class=\"skill-invocation-type\" [attr.data-activation]=\"inv.ActivationType\">\n {{ GetActivationTypeLabel(inv) }}\n </span>\n </div>\n @if (inv.Reason) {\n <div class=\"skill-invocation-reason\">\n <i class=\"fa-solid fa-quote-left\"></i>\n {{ inv.Reason }}\n </div>\n }\n <div class=\"skill-invocation-provenance\">\n <div class=\"provenance-title\">Provenance of Authority</div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Requested By</span>\n <span class=\"provenance-value\">{{ inv.Provenance.RequestedBy === 'user-request' ? 'User (/skill mention)' : 'Agent decision' }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Agent AcceptsSkills</span>\n <span class=\"provenance-value\">{{ inv.Provenance.AgentAcceptsSkills }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Skill ActivationMode</span>\n <span class=\"provenance-value\">{{ inv.Provenance.SkillActivationMode }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Agent SkillActivationMode</span>\n <span class=\"provenance-value\">{{ inv.Provenance.AgentSkillActivationMode }}</span>\n </div>\n <div class=\"provenance-row\">\n <span class=\"provenance-label\">Skill ID</span>\n <span class=\"provenance-value provenance-id\">{{ inv.SkillID }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n <!-- Payload Diff Tab -->\n @if (detailPaneTab === 'diff' && showStepPayloadDiff) {\n <div class=\"step-payload-diff\">\n <mj-deep-diff\n [oldValue]=\"stepPayloadAtStartObject\"\n [newValue]=\"stepPayloadAtEndObject\"\n [title]=\"''\"\n [showSummary]=\"true\"\n [showUnchanged]=\"false\"\n [expandAll]=\"false\"\n [maxDepth]=\"8\"\n [maxStringLength]=\"150\"\n [treatNullAsUndefined]=\"true\">\n </mj-deep-diff>\n </div>\n }\n <!-- Scratchpad Tab -->\n @if (detailPaneTab === 'scratchpad' && showScratchpadTab) {\n <div class=\"scratchpad-content\">\n <div class=\"scratchpad-sub-tabs\">\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'diff'\"\n (click)=\"scratchpadSubTab = 'diff'\">\n <i class=\"fa-solid fa-code-compare\"></i> Diff\n </button>\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'input'\"\n (click)=\"scratchpadSubTab = 'input'\">\n <i class=\"fa-solid fa-arrow-right-to-bracket\"></i> Input\n </button>\n <button\n class=\"scratchpad-sub-tab\"\n [class.active]=\"scratchpadSubTab === 'output'\"\n (click)=\"scratchpadSubTab = 'output'\">\n <i class=\"fa-solid fa-arrow-right-from-bracket\"></i> Output\n </button>\n </div>\n <!-- Scratchpad Diff Sub-Tab -->\n @if (scratchpadSubTab === 'diff') {\n <div class=\"scratchpad-diff-view\">\n @if (stepScratchpadInput && stepScratchpadOutput) {\n <mj-deep-diff\n [oldValue]=\"stepScratchpadInput\"\n [newValue]=\"stepScratchpadOutput\"\n [title]=\"''\"\n [showSummary]=\"true\"\n [showUnchanged]=\"false\"\n [expandAll]=\"true\"\n [maxDepth]=\"6\"\n [maxStringLength]=\"300\"\n [treatNullAsUndefined]=\"true\">\n </mj-deep-diff>\n } @else if (stepScratchpadOutput) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-plus\"\n Title=\"Scratchpad created in this step (no prior state)\" />\n <div class=\"scratchpad-snapshot\">\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadOutput }\"></ng-container>\n </div>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad changes in this step\" />\n }\n </div>\n }\n <!-- Scratchpad Input Sub-Tab -->\n @if (scratchpadSubTab === 'input') {\n <div class=\"scratchpad-snapshot-view\">\n @if (stepScratchpadInput) {\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadInput }\"></ng-container>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad input for this step\" />\n }\n </div>\n }\n <!-- Scratchpad Output Sub-Tab -->\n @if (scratchpadSubTab === 'output') {\n <div class=\"scratchpad-snapshot-view\">\n @if (stepScratchpadOutput) {\n <ng-container *ngTemplateOutlet=\"scratchpadSnapshotTpl; context: { $implicit: stepScratchpadOutput }\"></ng-container>\n } @else {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"No scratchpad output for this step\" />\n }\n </div>\n }\n </div>\n }\n <!-- Full JSON Tab -->\n @if (detailPaneTab === 'json' || (!showStepPayloadDiff && !showScratchpadTab)) {\n <div class=\"json-viewer\">\n <div class=\"json-toolbar\">\n <button class=\"btn-icon\" title=\"Copy JSON\" (click)=\"onCopyToClipboard()\">\n <i class=\"fas fa-copy\"></i>\n </button>\n </div>\n <mj-code-editor\n [(ngModel)]=\"selectedItemJsonString\"\n [language]=\"'json'\"\n [readonly]=\"true\"\n style=\"width: 100%;\">\n </mj-code-editor>\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- Reusable scratchpad snapshot template -->\n<ng-template #scratchpadSnapshotTpl let-snapshot>\n <div class=\"scratchpad-snapshot\">\n @if (snapshot.notes) {\n <div class=\"scratchpad-notes-section\">\n <h4><i class=\"fa-solid fa-note-sticky\"></i> Notes</h4>\n <div class=\"scratchpad-notes-content\">{{ snapshot.notes }}</div>\n </div>\n }\n @if (snapshot.tasks?.length > 0) {\n <div class=\"scratchpad-tasks-section\">\n <h4><i class=\"fa-solid fa-list-check\"></i> Tasks ({{ snapshot.tasks.length }})</h4>\n <div class=\"scratchpad-task-list\">\n @for (task of snapshot.tasks; track task.id) {\n <div class=\"scratchpad-task-item\" [attr.data-status]=\"task.status\">\n <span class=\"task-status-indicator\" [attr.data-status]=\"task.status\">\n @switch (task.status) {\n @case ('completed') { <i class=\"fa-solid fa-circle-check\"></i> }\n @case ('in_progress') { <i class=\"fa-solid fa-spinner\"></i> }\n @case ('blocked') { <i class=\"fa-solid fa-circle-xmark\"></i> }\n @default { <i class=\"fa-regular fa-circle\"></i> }\n }\n </span>\n <div class=\"task-content\">\n <span class=\"task-id\">{{ task.id }}</span>\n <span class=\"task-title\">{{ task.title }}</span>\n @if (task.notes) {\n <span class=\"task-notes\">{{ task.notes }}</span>\n }\n </div>\n <span class=\"task-status-badge\" [attr.data-status]=\"task.status\">{{ task.status }}</span>\n </div>\n }\n </div>\n </div>\n }\n @if (!snapshot.notes && (!snapshot.tasks || snapshot.tasks.length === 0)) {\n <mj-empty-state\n Size=\"compact\"\n Icon=\"fa-solid fa-circle-info\"\n Title=\"Empty scratchpad\" />\n }\n </div>\n</ng-template>", styles: [".json-detail-pane {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n}\n\n.json-pane-header {\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: var(--mj-bg-surface-sunken);\n}\n\n.json-pane-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.btn-icon {\n background: none;\n border: none;\n padding: 4px 8px;\n cursor: pointer;\n color: var(--mj-text-muted);\n border-radius: 4px;\n transition: all 0.2s;\n}\n\n.btn-icon:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 5%, transparent);\n color: var(--mj-text-secondary);\n}\n\n.json-pane-content {\n flex: 1;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n}\n\n.json-meta {\n padding: 16px;\n display: flex;\n gap: 24px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.meta-item label {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n font-weight: 600;\n}\n\n.meta-item span {\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.status-badge {\n display: inline-flex;\n align-items: center;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n}\n\n.status-badge.small {\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.status-badge[data-status=\"Running\"] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.status-badge[data-status=\"Completed\"] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.status-badge[data-status=\"Failed\"] {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.status-badge[data-status=\"Cancelled\"] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.action-link {\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.btn-link {\n background: none;\n border: none;\n color: var(--mj-brand-primary);\n cursor: pointer;\n padding: 6px 12px;\n border-radius: 4px;\n transition: all 0.2s;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n}\n\n.btn-link:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.detail-pane-tabs {\n display: flex;\n gap: 0;\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 0 16px;\n}\n\n.detail-tab {\n padding: 12px 16px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-muted);\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.detail-tab:hover {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-text-primary) 2%, transparent);\n}\n\n.detail-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.detail-pane-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0; /* Allow flexbox to properly size */\n}\n\n/* \n.detail-pane-content.with-tabs {\n // No explicit height needed with flexbox \n} \n*/\n\n.json-viewer {\n flex: 1;\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: hidden;\n min-height: 0; /* Important for flexbox children to respect overflow */\n}\n\n.json-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n z-index: 10;\n display: flex;\n gap: 4px;\n}\n\n.step-payload-diff {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n min-height: 0; /* Allow proper scrolling */\n}\n\n/* Specific styling for mj-code-editor container */\n.json-viewer mj-code-editor {\n flex: 1;\n overflow: hidden;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* Tab badge for change count */\n.tab-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 9px;\n font-size: 10px;\n font-weight: 700;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n/* Scratchpad content area */\n.scratchpad-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0;\n}\n\n/* Scratchpad sub-tabs */\n.scratchpad-sub-tabs {\n display: flex;\n gap: 0;\n padding: 0 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface);\n}\n\n.scratchpad-sub-tab {\n padding: 8px 12px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n cursor: pointer;\n font-size: 12px;\n color: var(--mj-text-muted);\n transition: all 0.2s;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n\n.scratchpad-sub-tab:hover {\n color: var(--mj-text-secondary);\n background: color-mix(in srgb, var(--mj-text-primary) 2%, transparent);\n}\n\n.scratchpad-sub-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n/* Scratchpad diff and snapshot views */\n.scratchpad-diff-view,\n.scratchpad-snapshot-view {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n min-height: 0;\n}\n\n/* Scratchpad snapshot rendering */\n.scratchpad-snapshot {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.scratchpad-notes-section h4,\n.scratchpad-tasks-section h4 {\n margin: 0 0 8px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.scratchpad-notes-content {\n padding: 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n border: 1px solid var(--mj-border-subtle);\n font-size: 13px;\n line-height: 1.5;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* Task list */\n.scratchpad-task-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scratchpad-task-item {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 12px;\n border-radius: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n transition: background 0.15s;\n}\n\n.scratchpad-task-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* Task status indicator icon */\n.task-status-indicator {\n flex-shrink: 0;\n width: 16px;\n margin-top: 2px;\n font-size: 14px;\n}\n\n.task-status-indicator[data-status=\"completed\"] {\n color: var(--mj-status-success);\n}\n\n.task-status-indicator[data-status=\"in_progress\"] {\n color: var(--mj-brand-primary);\n}\n\n.task-status-indicator[data-status=\"blocked\"] {\n color: var(--mj-status-error);\n}\n\n.task-status-indicator[data-status=\"pending\"] {\n color: var(--mj-text-muted);\n}\n\n/* Task content */\n.task-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.task-id {\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n}\n\n.task-title {\n font-size: 13px;\n color: var(--mj-text-primary);\n line-height: 1.4;\n}\n\n.task-notes {\n font-size: 12px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n font-style: italic;\n}\n\n/* Task status badge */\n.task-status-badge {\n flex-shrink: 0;\n padding: 2px 6px;\n border-radius: 4px;\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n white-space: nowrap;\n}\n\n.task-status-badge[data-status=\"completed\"] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.task-status-badge[data-status=\"in_progress\"] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.task-status-badge[data-status=\"blocked\"] {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.task-status-badge[data-status=\"pending\"] {\n background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n}\n\n/* ============================================================\n Skills tab \u2014 skill-invocation observability cards\n ============================================================ */\n.skills-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n\n.skill-invocation-card {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n padding: 12px 14px;\n background: var(--mj-bg-surface);\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.skill-invocation-card[data-activation=\"auto\"] {\n border-left-color: var(--mj-status-warning);\n}\n\n.skill-invocation-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.skill-invocation-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--mj-text-primary);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n.skill-invocation-name i {\n color: var(--mj-brand-primary);\n}\n\n.skill-invocation-type {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 2px 10px;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.skill-invocation-type[data-activation=\"auto\"] {\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.skill-invocation-reason {\n margin-top: 8px;\n font-size: 13px;\n font-style: italic;\n color: var(--mj-text-secondary);\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n\n.skill-invocation-reason i {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n\n.skill-invocation-provenance {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.provenance-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n}\n\n.provenance-row {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n padding: 2px 0;\n}\n\n.provenance-label {\n color: var(--mj-text-secondary);\n}\n\n.provenance-value {\n color: var(--mj-text-primary);\n font-weight: 500;\n}\n\n.provenance-id {\n font-family: monospace;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* Workflow tab \u2014 the recorded task graph, read-only, with the Save as Workflow action. */\n.detail-graph {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n\n.detail-graph__actions {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n padding: var(--mj-space-sm, 0.5rem);\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n/* min-height:0 is what lets the canvas actually shrink inside the flex column. */\n.detail-graph__canvas {\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/* Explanation for a step that never executed \u2014 see StepNotice. Sits above the tabs because it\n accounts for the emptiness the reader is about to look at. */\n.step-notice {\n padding: 0 var(--mj-space-3, 12px) var(--mj-space-2, 8px);\n}\n\n/* The run view fills the tab rather than taking its intrinsic size \u2014 a canvas that stops halfway\n down its pane with dead space beneath reads as a broken layout. */\n.detail-graph {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n\n.detail-graph__canvas {\n flex: 1 1 auto;\n min-height: 0;\n}\n"] }]
1097
1168
  }], () => [{ type: i0.ChangeDetectorRef }], { SelectedTimelineItem: [{
1098
1169
  type: Input
1099
1170
  }], selectedTimelineItem: [{
1100
1171
  type: Input
1101
1172
  }], Provider: [{
1102
1173
  type: Input
1174
+ }], SavingWorkflow: [{
1175
+ type: Input
1103
1176
  }], ClosePanel: [{
1104
1177
  type: Output
1105
1178
  }], closePanel: [{
@@ -1116,6 +1189,9 @@ export class AIAgentRunStepDetailComponent {
1116
1189
  type: Output
1117
1190
  }], saveAsWorkflowRequested: [{
1118
1191
  type: Output
1192
+ }], TaskGraphRunView: [{
1193
+ type: ViewChild,
1194
+ args: [TaskGraphRunViewComponent]
1119
1195
  }] }); })();
1120
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AIAgentRunStepDetailComponent, { className: "AIAgentRunStepDetailComponent", filePath: "src/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.ts", lineNumber: 25 }); })();
1196
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AIAgentRunStepDetailComponent, { className: "AIAgentRunStepDetailComponent", filePath: "src/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.ts", lineNumber: 26 }); })();
1121
1197
  //# sourceMappingURL=ai-agent-run-step-detail.component.js.map