diff --git a/cli/src/registry/default/artifacts.ts b/cli/src/registry/default/artifacts.ts index c5fa5ff..08144f6 100644 --- a/cli/src/registry/default/artifacts.ts +++ b/cli/src/registry/default/artifacts.ts @@ -352,4 +352,53 @@ export const artifacts: RegistryItem[] = [ }, ], }, + { + name: "contextual-explanation", + type: "registry:artifacts", + dependencies: [ + "react", + "clsx", + "tailwind-merge", + "zod", + "@copilotkit/react-core", + ], + registryDependencies: ["button"], + files: [ + { + path: "hax/artifacts/contextual-explanation/contextual-explanation.tsx", + type: "registry:component", + content: readComponentFile( + "hax/artifacts/contextual-explanation/contextual-explanation.tsx", + ), + }, + { + path: "hax/artifacts/contextual-explanation/action.ts", + type: "registry:hook", + content: readComponentFile( + "hax/artifacts/contextual-explanation/action.ts", + ), + }, + { + path: "hax/artifacts/contextual-explanation/types.ts", + type: "registry:types", + content: readComponentFile( + "hax/artifacts/contextual-explanation/types.ts", + ), + }, + { + path: "hax/artifacts/contextual-explanation/index.ts", + type: "registry:index", + content: readComponentFile( + "hax/artifacts/contextual-explanation/index.ts", + ), + }, + { + path: "hax/artifacts/contextual-explanation/description.ts", + type: "registry:description", + content: readComponentFile( + "hax/artifacts/contextual-explanation/description.ts", + ), + }, + ], + }, ] diff --git a/cli/src/registry/github-registry/artifacts.json b/cli/src/registry/github-registry/artifacts.json index f3362df..b07f33c 100644 --- a/cli/src/registry/github-registry/artifacts.json +++ b/cli/src/registry/github-registry/artifacts.json @@ -154,5 +154,23 @@ { "name": "index.ts", "type": "registry:index" }, { "name": "description.ts", "type": "registry:description" } ] + }, + "contextual-explanation": { + "type": "registry:artifacts", + "dependencies": [ + "react", + "clsx", + "tailwind-merge", + "zod", + "@copilotkit/react-core" + ], + "registryDependencies": ["button"], + "files": [ + { "name": "contextual-explanation.tsx", "type": "registry:component" }, + { "name": "action.ts", "type": "registry:hook" }, + { "name": "types.ts", "type": "registry:types" }, + { "name": "index.ts", "type": "registry:index" }, + { "name": "description.ts", "type": "registry:description" } + ] } } diff --git a/hax/artifacts/contextual-explanation/README.md b/hax/artifacts/contextual-explanation/README.md new file mode 100644 index 0000000..68732c7 --- /dev/null +++ b/hax/artifacts/contextual-explanation/README.md @@ -0,0 +1,232 @@ +# Contextual Explanation Component + +A card component for displaying contextual explanations about system changes, agent decisions, or automated actions with supporting details and action buttons. + +## Installation + +Install the component using the HAX SDK CLI: + +```bash +hax init +hax add artifact contextual-explanation +``` + +This will install the component along with its required dependencies: +- `@copilotkit/react-core` +- `zod` + +## Usage + +### Basic Usage + +```tsx +import { + HAXContextualExplanation, + ExplanationDetail, +} from "@/artifacts/contextual-explanation" + +const details: ExplanationDetail[] = [ + { + label: "Previous Configuration", + value: "Load Balancer A", + isBoldLabel: true, + }, + { + label: "New Configuration", + value: "Load Balancer B", + isBoldLabel: true, + }, + { + label: "Region", + value: "us-west-2", + }, + { + label: "Timestamp", + value: "2024-01-15", + additionalInfo: "10:30 AM UTC", + }, +] + +function App() { + const handleApprove = () => { + console.log("Change approved") + } + + const handleDismiss = () => { + console.log("Change dismissed") + } + + return ( + + ) +} +``` + +### Using the Card Component Directly + +```tsx +import { ContextualExplanationCard } from "@/artifacts/contextual-explanation" + + console.log("Acknowledged")} +/> +``` + +### CopilotKit Integration + +Use the `useContextualExplanationAction` hook to enable AI agents to create contextual explanations: + +```tsx +import { useContextualExplanationAction } from "@/artifacts/contextual-explanation" + +function MyComponent() { + const addOrUpdateArtifact = (type, data) => { + // Handle the artifact creation/update + console.log("Artifact:", type, data) + } + + useContextualExplanationAction({ addOrUpdateArtifact }) + + return
...
+} +``` + +## Schema + +### ExplanationDetail + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `label` | `string` | Yes | Label for the detail row | +| `value` | `string` | Yes | Value/description for the detail row | +| `additionalInfo` | `string` | No | Optional additional info (e.g., timestamp) | +| `isSubItem` | `boolean` | No | Whether this is a sub-item (indented) | +| `isBoldLabel` | `boolean` | No | Whether the label should be bold | + +### ContextualExplanationArtifact + +```typescript +{ + id: string + type: "contextual-explanation" + data: { + title?: string + alertTitle?: string + alertDescription?: string + details: ExplanationDetail[] + secondaryButtonLabel?: string + primaryButtonLabel?: string + } +} +``` + +## Actions + +### create_contextual_explanation + +CopilotKit action for AI agents to create contextual explanations. + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `title` | `string` | No | Title for the contextual explanation card | +| `alertTitle` | `string` | No | Title for the alert section | +| `alertDescription` | `string` | No | Detailed description of why the change occurred | +| `detailsJson` | `string` | Yes | JSON string of detail items array | +| `secondaryButtonLabel` | `string` | No | Label for the secondary action button | +| `primaryButtonLabel` | `string` | No | Label for the primary action button | + +**Example Action Call:** + +```json +{ + "name": "create_contextual_explanation", + "parameters": { + "title": "Configuration Change", + "alertTitle": "Automatic optimization", + "alertDescription": "System detected suboptimal settings and applied corrections", + "detailsJson": "[{\"label\":\"Setting\",\"value\":\"Cache TTL\"},{\"label\":\"Previous\",\"value\":\"300s\"},{\"label\":\"New\",\"value\":\"600s\",\"isBoldLabel\":true}]", + "primaryButtonLabel": "Approve", + "secondaryButtonLabel": "Revert" + } +} +``` + +## Component Props + +### HAXContextualExplanation / ContextualExplanationCard + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `title` | `string` | `"Contextual Explanation"` | Card header title | +| `alertTitle` | `string` | `"Why this happened"` | Alert section title | +| `alertDescription` | `string` | - | Alert section description | +| `details` | `ExplanationDetail[]` | - | Array of detail items | +| `secondaryButtonLabel` | `string` | `"Dismiss"` | Secondary button text | +| `primaryButtonLabel` | `string` | `"Approve"` | Primary button text | +| `onSecondaryClick` | `() => void` | - | Secondary button callback | +| `onPrimaryClick` | `() => void` | - | Primary button callback | +| `className` | `string` | - | Additional CSS classes | + +## Exports + +```typescript +// Components +export { HAXContextualExplanation } +export { ContextualExplanationCard } + +// Hook +export { useContextualExplanationAction } + +// Types +export type { ContextualExplanationCardProps } +export type { ExplanationDetail } +export type { ContextualExplanationArtifact } + +// Zod Schema +export { ContextualExplanationArtifactZod } +``` + +## Best Practices + +- **Clear alert titles**: Summarize why something happened in the alert title +- **Detailed descriptions**: Use the alert description for additional context +- **Consistent labels**: Keep detail labels descriptive and consistent +- **Hierarchical info**: Use `isSubItem` for nested or related information +- **Emphasis**: Use `isBoldLabel` to highlight important details +- **Limit details**: Keep to 5-8 detail items for readability +- **Action-oriented buttons**: Make button labels clear and actionable + +## When to Use + +- Explaining automated system changes +- Displaying agent decision rationale +- Configuration change notifications +- Routing or traffic modifications +- Any situation requiring user acknowledgment with context + +## When NOT to Use + +- Simple notifications without details +- Single-line status messages +- Information that doesn't require user action +- Vague explanations without supporting details diff --git a/hax/artifacts/contextual-explanation/action.ts b/hax/artifacts/contextual-explanation/action.ts new file mode 100644 index 0000000..7d4fdb7 --- /dev/null +++ b/hax/artifacts/contextual-explanation/action.ts @@ -0,0 +1,107 @@ +/* + * Copyright 2025 Cisco Systems, Inc. and its affiliates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { useCopilotAction } from "@copilotkit/react-core" +import { ArtifactTab } from "./types" +import { CONTEXTUAL_EXPLANATION_DESCRIPTION } from "./description" + +interface UseContextualExplanationActionProps { + addOrUpdateArtifact: ( + type: "contextual-explanation", + data: Extract["data"], + ) => void +} + +export const useContextualExplanationAction = ({ + addOrUpdateArtifact, +}: UseContextualExplanationActionProps) => { + useCopilotAction({ + name: "create_contextual_explanation", + description: CONTEXTUAL_EXPLANATION_DESCRIPTION, + parameters: [ + { + name: "title", + type: "string", + description: "Title for the contextual explanation card", + required: false, + }, + { + name: "alertTitle", + type: "string", + description: + "Title for the alert section explaining why something happened", + required: false, + }, + { + name: "alertDescription", + type: "string", + description: "Detailed description of why the change or action occurred", + required: false, + }, + { + name: "detailsJson", + type: "string", + description: + "JSON string of detail items array. Each item must have: label (string), value (string), and optionally: additionalInfo (string), isSubItem (boolean), isBoldLabel (boolean)", + required: true, + }, + { + name: "secondaryButtonLabel", + type: "string", + description: "Label for the secondary action button", + required: false, + }, + { + name: "primaryButtonLabel", + type: "string", + description: "Label for the primary action button", + required: false, + }, + ], + handler: async (args) => { + try { + const { + title, + alertTitle, + alertDescription, + detailsJson, + secondaryButtonLabel, + primaryButtonLabel, + } = args + + const details = JSON.parse(detailsJson) + + addOrUpdateArtifact("contextual-explanation", { + title, + alertTitle, + alertDescription, + details, + secondaryButtonLabel, + primaryButtonLabel, + }) + + return `Created contextual explanation "${title || "Contextual Explanation"}" with ${details.length} details` + } catch (error) { + console.error("Error in create_contextual_explanation handler:", error) + const errorMessage = + error instanceof Error ? error.message : "Unknown error" + return `Failed to create contextual explanation: ${errorMessage}` + } + }, + }) +} diff --git a/hax/artifacts/contextual-explanation/contextual-explanation.tsx b/hax/artifacts/contextual-explanation/contextual-explanation.tsx new file mode 100644 index 0000000..6eb5116 --- /dev/null +++ b/hax/artifacts/contextual-explanation/contextual-explanation.tsx @@ -0,0 +1,215 @@ +/* + * Copyright 2025 Cisco Systems, Inc. and its affiliates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +"use client" + +import * as React from "react" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +export interface ExplanationDetail { + /** Label for the detail row */ + label: string + /** Value/description for the detail row */ + value: string + /** Optional additional info (e.g., timestamp) */ + additionalInfo?: string + /** Whether this is a sub-item (indented) */ + isSubItem?: boolean + /** Whether the label should be bold */ + isBoldLabel?: boolean +} + +export interface ContextualExplanationCardProps + extends React.HTMLAttributes { + /** Title displayed in the card header */ + title?: string + /** Alert title explaining why the change happened */ + alertTitle?: string + /** Alert description with more details */ + alertDescription?: string + /** Array of detail rows to display */ + details: ExplanationDetail[] + /** Text for the secondary button */ + secondaryButtonLabel?: string + /** Text for the primary button */ + primaryButtonLabel?: string + /** Callback when secondary button is clicked */ + onSecondaryClick?: () => void + /** Callback when primary button is clicked */ + onPrimaryClick?: () => void +} + +/** + * ContextualExplanationCard - Card component for displaying contextual explanations + * + * Uses Figma design system variables: + * - Background: card/card (#ffffff) + * - Border: general/border (#e2e8f0) + * - Border radius: semantic/rounded-lg (8px) + * - Padding: semantic/xl (24px) + * - Shadow: shadow-sm + */ +export function ContextualExplanationCard({ + title = "Contextual Explanation", + alertTitle = "Why this happened", + alertDescription = "Agent based on observation modified behaviour of the system", + details, + secondaryButtonLabel = "Dismiss", + primaryButtonLabel = "Approve", + onSecondaryClick, + onPrimaryClick, + className, + ...props +}: ContextualExplanationCardProps) { + return ( +
+ {/* Card Header */} +
+

+ {title} +

+
+ + {/* Why This Happened Alert Section */} +
+
+
+

+ {alertTitle} +

+

+ {alertDescription} +

+
+
+
+ + {/* Details Section */} +
+

Details

+
+ {details.map((detail, index) => ( +
+

+ {detail.label} +

+ {detail.value && ( +

+ {detail.value} + {detail.additionalInfo && ( + + {" "} + {detail.additionalInfo} + + )} +

+ )} +
+ ))} +
+
+ + {/* Button Group Section */} +
+ + +
+
+ ) +} + +interface HAXContextualExplanationProps { + title?: string + alertTitle?: string + alertDescription?: string + details: ExplanationDetail[] + secondaryButtonLabel?: string + primaryButtonLabel?: string + onSecondaryClick?: () => void + onPrimaryClick?: () => void +} + +export function HAXContextualExplanation({ + title, + alertTitle, + alertDescription, + details, + secondaryButtonLabel, + primaryButtonLabel, + onSecondaryClick, + onPrimaryClick, +}: HAXContextualExplanationProps) { + return ( +
+ +
+ ) +} + +export default ContextualExplanationCard diff --git a/hax/artifacts/contextual-explanation/description.ts b/hax/artifacts/contextual-explanation/description.ts new file mode 100644 index 0000000..0af9636 --- /dev/null +++ b/hax/artifacts/contextual-explanation/description.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2025 Cisco Systems, Inc. and its affiliates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export const CONTEXTUAL_EXPLANATION_DESCRIPTION = + `Use contextual explanation artifacts to present structured explanations for system changes, agent decisions, or automated actions. Best for explaining why something happened, displaying configuration changes, routing modifications, and any situation where users need to understand the reasoning behind an action with supporting details. + +Structure each explanation with an alert section (title and description explaining why something happened) and a details section with label-value pairs. Include action buttons for user response. + +Best practices: +- Provide a clear, concise alert title that summarizes why something happened +- Use the alert description to give more context about the reasoning +- Keep detail labels consistent and descriptive +- Use isSubItem for hierarchical or nested information +- Use isBoldLabel to emphasize important details +- Limit to 5-8 detail items for readability +- Make button labels action-oriented and clear + +Don't use contextual explanations for simple notifications or single-line messages. Avoid vague explanations. Don't include details without clear labels.` as const diff --git a/hax/artifacts/contextual-explanation/index.ts b/hax/artifacts/contextual-explanation/index.ts new file mode 100644 index 0000000..2545a52 --- /dev/null +++ b/hax/artifacts/contextual-explanation/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Cisco Systems, Inc. and its affiliates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + HAXContextualExplanation, + ContextualExplanationCard, +} from "./contextual-explanation" +export type { + ContextualExplanationCardProps, + ExplanationDetail, +} from "./contextual-explanation" +export { useContextualExplanationAction } from "./action" +export type { ContextualExplanationArtifact } from "./types" +export { ContextualExplanationArtifactZod } from "./types" diff --git a/hax/artifacts/contextual-explanation/types.ts b/hax/artifacts/contextual-explanation/types.ts new file mode 100644 index 0000000..e55ba75 --- /dev/null +++ b/hax/artifacts/contextual-explanation/types.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2025 Cisco Systems, Inc. and its affiliates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import z from "zod" + +const ExplanationDetailZod = z.object({ + label: z.string(), + value: z.string(), + additionalInfo: z.string().optional(), + isSubItem: z.boolean().optional(), + isBoldLabel: z.boolean().optional(), +}) + +export const ContextualExplanationArtifactZod = z.object({ + id: z.string(), + type: z.literal("contextual-explanation"), + data: z.object({ + title: z.string().optional(), + alertTitle: z.string().optional(), + alertDescription: z.string().optional(), + details: z.array(ExplanationDetailZod), + secondaryButtonLabel: z.string().optional(), + primaryButtonLabel: z.string().optional(), + }), +}) + +export type ContextualExplanationArtifact = z.infer< + typeof ContextualExplanationArtifactZod +> + +export const ArtifactTabZod = z.discriminatedUnion("type", [ + ContextualExplanationArtifactZod, +]) +export type ArtifactTab = z.infer