diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.css b/src/app/shared/task-execution-viewer/task-execution-viewer.css
index a898909..2ca77df 100644
--- a/src/app/shared/task-execution-viewer/task-execution-viewer.css
+++ b/src/app/shared/task-execution-viewer/task-execution-viewer.css
@@ -1013,3 +1013,66 @@
align-items: center;
gap: 4px;
}
+/* Credential setup uses the same neutral surface and compact controls as the execution view. */
+.execution-credential-banner,
+.execution-credential-card {
+ border: 1px solid #dbe3ee;
+ border-radius: .5rem;
+ background: #fff;
+}
+
+.execution-credential-banner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: .75rem;
+ margin: .25rem;
+ padding: .7rem .9rem;
+ border-left: 3px solid #3b82f6;
+}
+
+.execution-credential-banner__title {
+ display: flex;
+ align-items: center;
+ gap: .375rem;
+ color: #334155;
+ font-size: .8rem;
+ font-weight: 700;
+}
+.execution-credential-banner__message { margin-top: .125rem; font-size: .75rem; color: #64748b; }
+
+.execution-credential-card {
+ margin: .75rem;
+ padding: .8rem;
+}
+.execution-credential-card--provided {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: .75rem;
+ border-left: 3px solid #22c55e;
+}
+
+.execution-credential-card__title { color: #334155; font-size: .8125rem; font-weight: 700; }
+.execution-credential-button {
+ border: 1px solid transparent;
+ border-radius: .375rem;
+ padding: .45rem .7rem;
+ font-size: .75rem;
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.execution-credential-button--primary { background: #2563eb; color: #fff; }
+.execution-credential-button--secondary { border-color: #cbd5e1; background: #fff; color: #334155; }
+.execution-credential-button:disabled { opacity: .55; }
+
+.execution-credential-card__error {
+ margin-top: .6rem;
+ padding: .5rem .625rem;
+ border: 1px solid #fecaca;
+ border-radius: .375rem;
+ background: #fef2f2;
+ color: #b91c1c;
+ font-size: .75rem;
+}
diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.html b/src/app/shared/task-execution-viewer/task-execution-viewer.html
index a0a0698..b15e1bb 100644
--- a/src/app/shared/task-execution-viewer/task-execution-viewer.html
+++ b/src/app/shared/task-execution-viewer/task-execution-viewer.html
@@ -124,16 +124,24 @@
}
@if (authorizationGate().missingProviders.length || authorizationGate().runtime.length) {
-
No saved {{ entry.provider }} credentials. Add one to continue.
+ }
@if (llmCredentialErrorFor(entry); as credentialError) {
-
+
{{ credentialError }}
- Retry
+ Retry
}
@if (authorizationErrorFor(entry); as authorizationError) {
-
{{ authorizationError }}
+
{{ authorizationError }}
}
@if (credentialFormError(); as formError) {
-
{{ formError }}
+
{{ formError }}
}
-
+
@if (!llmCredentialLoadingFor(entry)) {
-
+
{{ credentialFormSaving() ? 'Saving...' : 'Add credential' }}
}
@if (editingAuthorizationKeys()[entry.requirement.key]) {
- Cancel
+ Cancel
}
diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.ts b/src/app/shared/task-execution-viewer/task-execution-viewer.ts
index 9eb4edc..1b1d718 100644
--- a/src/app/shared/task-execution-viewer/task-execution-viewer.ts
+++ b/src/app/shared/task-execution-viewer/task-execution-viewer.ts
@@ -10,8 +10,6 @@ import { NotificationService } from '@services/notifications/notification';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatSelectModule } from '@angular/material/select';
import {
areFlowValueKindsCompatible,
FlowBlock,
@@ -143,7 +141,7 @@ function uploadFailureMessage(failure: unknown): string {
@Component({
selector: 'app-task-execution-viewer',
- imports: [CommonModule, FormsModule, ReteEditor, TaskExecutionInputsPanelComponent, MatButtonModule, MatIconModule, MatTooltipModule, MatFormFieldModule, MatSelectModule, BiasImpactReportListComponent, JsonViewerComponent],
+ imports: [CommonModule, FormsModule, ReteEditor, TaskExecutionInputsPanelComponent, MatButtonModule, MatIconModule, MatTooltipModule, BiasImpactReportListComponent, JsonViewerComponent],
templateUrl: './task-execution-viewer.html',
styleUrl: './task-execution-viewer.css',
changeDetection: ChangeDetectionStrategy.OnPush
@@ -966,6 +964,29 @@ export class TaskExecutionViewerComponent implements OnDestroy {
this.activeAsideTab.set('inputs');
}
+ /** The banner creates the first credential directly: there is nothing to choose from yet. */
+ openAuthorizationAction() {
+ const pending = this.pendingVaultAuthorizations();
+ if (pending.length === 1
+ && !this.llmCredentialLoadingFor(pending[0])
+ && !this.llmCredentialOptionsFor(pending[0]).length) {
+ this.openAuthorizationPanel();
+ void this.openVaultCredentialForm(pending[0].provider);
+ return;
+ }
+ this.openAuthorizationPanel();
+ }
+
+ authorizationActionLabel(): string {
+ const pending = this.pendingVaultAuthorizations();
+ if (pending.length === 1
+ && !this.llmCredentialLoadingFor(pending[0])
+ && !this.llmCredentialOptionsFor(pending[0]).length) {
+ return 'Add credential';
+ }
+ return pending.length ? 'Choose credential' : 'Open authorization';
+ }
+
toggleContextAside() {
this.contextAsideOpen.update((open) => !open);
}