diff --git a/README.md b/README.md index f21c071..48d3cb4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ OpsOrch Console is the operator-focused web UI for OpsOrch. It provides a unifie OpsOrch Console is available in two editions built from a single codebase: -- **OSS Edition** - Open source features including incidents, logs, metrics, tickets, services, and settings +- **OSS Edition** - Open source features including incidents, logs, metrics, tickets, services, orchestration, and settings - **Enterprise Edition** - All OSS features plus AI-powered Copilot assistance and chat history The edition is controlled at build time via the `OPSORCH_EDITION` environment variable. @@ -23,6 +23,7 @@ The edition is controlled at build time via the `OPSORCH_EDITION` environment va - **Metrics**: Visualize and query metrics data with customizable expressions and aggregations - **Services**: Explore service catalog and dependencies - **Tickets**: View and manage tickets from integrated ticketing systems +- **Orchestration**: Browse workflow plans, launch runs, monitor run status, and complete manual steps with progress tracking - **Chat**: AI-powered assistance via OpsOrch Copilot for incident investigation, log analysis, and operational queries. Copilot can generate smart references to filtered views with query parameters - **Settings**: Configure OpsOrch Core and Copilot endpoints @@ -35,6 +36,14 @@ All primary data views (Incidents, Alerts, Logs, Metrics) support: - **Scope filtering**: Filter by service, environment, and team - **Copilot integration**: AI can generate filtered views and include them as clickable references +### Copilot Answer Actions + +Copilot responses can include recommended actions to trigger orchestration workflows: + +- `actions` entries use the `orchestration` type and may include `id`, `name`, and `reason` +- When an `id` is present, the UI links directly to `/orchestration/plans/{id}` +- When no `id` is provided, the UI routes operators to the orchestration plan browser + ## Architecture @@ -202,7 +211,7 @@ The Docker images run as a non-root user and expose port 3000. Both editions sup ## Project Structure - `app/` - Next.js app router pages and layouts - - `(oss)/` - OSS Edition routes (incidents, logs, metrics, tickets, services, settings) + - `(oss)/` - OSS Edition routes (incidents, logs, metrics, tickets, services, orchestration, settings) - `(enterprise)/` - Enterprise Edition routes (Copilot home, chat history) - `components/` - Reusable React components - `(enterprise)/` - Enterprise-only components (CopilotPanel, etc.) @@ -312,4 +321,3 @@ Download release artifacts from the [Releases page](https://github.com/OpsOrch/o - [OpsOrch Core Documentation](../opsorch-core/README.md) - [OpsOrch Copilot Documentation](../opsorch-copilot/README.md) - [Next.js Documentation](https://nextjs.org/docs) - diff --git a/app/(oss)/deployments/[id]/page.tsx b/app/(oss)/deployments/[id]/page.tsx index 8b30489..e75df84 100644 --- a/app/(oss)/deployments/[id]/page.tsx +++ b/app/(oss)/deployments/[id]/page.tsx @@ -58,7 +58,7 @@ export default function DeploymentDetailPage() { .catch((err) => { deploymentState.fail(err); }); - }, [deploymentId]); // eslint-disable-line react-hooks/exhaustive-deps + }, [deploymentId, deploymentState]); diff --git a/app/components/(enterprise)/CopilotPanel.tsx b/app/components/(enterprise)/CopilotPanel.tsx index dce5281..26e3df7 100644 --- a/app/components/(enterprise)/CopilotPanel.tsx +++ b/app/components/(enterprise)/CopilotPanel.tsx @@ -48,14 +48,19 @@ function normalizeAnswer(payload: CopilotApiResponse): CopilotAnswer { // Extract executionTrace from the answer const derivedExecutionTrace = answer.executionTrace; + // Extract actions from the answer + const derivedActions = answer.actions; + console.log('[normalizeAnswer] answer.references:', answer.references); console.log('[normalizeAnswer] derivedReferences:', derivedReferences); console.log('[normalizeAnswer] executionTrace:', derivedExecutionTrace); + console.log('[normalizeAnswer] actions:', derivedActions); return { conclusion: derivedConclusion, missing: answer.missing, references: derivedReferences, + actions: derivedActions, confidence: answer.confidence, chatId: derivedChatId, executionTrace: derivedExecutionTrace, diff --git a/app/components/(enterprise)/copilot/ActionLinks.tsx b/app/components/(enterprise)/copilot/ActionLinks.tsx new file mode 100644 index 0000000..b8be910 --- /dev/null +++ b/app/components/(enterprise)/copilot/ActionLinks.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { CopilotAction } from "@/app/lib/types"; + +export function ActionLinks({ + actions, +}: { + actions?: CopilotAction[]; +}) { + if (!actions?.length) { + return null; + } + + return ( +
+ Recommended Actions ({actions.length}) +
++ {action.reason} +
+ )} +Services
{renderList(services.map((svc) => ( - + @@ -168,11 +169,11 @@ export function ReferenceLinks({Teams
{renderList(teams.map((team: string) => ( - + @@ -189,12 +190,12 @@ export function ReferenceLinks({ const label = `${t}`; const href = t ? `/tickets?ticketId=${encodeURIComponent(t)}` : "/tickets"; return ( - + @@ -218,12 +219,12 @@ export function ReferenceLinks({ : "(unnamed)"; const tooltip = `${m.start || "?"} → ${m.end || "?"}${m.scope ? ` • Scope: ${JSON.stringify(m.scope)}` : ""}`; return ( - + @@ -259,6 +260,23 @@ export function ReferenceLinks({ )}Orchestration Plans
+ {renderList(orchestrationPlans.map((planId: string) => ( + + + {planId} + + )))} +