Skip to content

AI assistant plugin addition with aks-agent support#419

Merged
illume merged 18 commits into
mainfrom
ai-assistant-addition
Mar 16, 2026
Merged

AI assistant plugin addition with aks-agent support#419
illume merged 18 commits into
mainfrom
ai-assistant-addition

Conversation

@ashu8912
Copy link
Copy Markdown
Member

@ashu8912 ashu8912 commented Mar 12, 2026

Description

Adds AKS agent version of the ai-assistant plugin.

It's preview state, and off by default. Turn it on in the plugin settings.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring
  • CI/CD changes
  • Other: **___**

Related Issues

Related to #156

Changes Made

On top of the AI assistant it adds AKS agent support.

Testing

  • Unit tests pass
  • Integration tests pass
  • Manual testing completed
  • Performance tested (if applicable)
  • Accessibility tested (if applicable)

Test Cases

Describe the test cases that were run:

You need to install AKS agent on a cluster.

Ask Ashu / Rene for cluster with it already installed.

Screenshots/Videos

If applicable, add screenshots or videos to demonstrate the changes.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Breaking Changes

If this is a breaking change, describe:

  • What breaks
  • How to migrate
  • Timeline for deprecation

Performance Impact

  • No performance impact
  • Performance improved
  • Performance degraded (explain why this is acceptable)

Documentation Updates

  • README.md updated
  • API documentation updated
  • Code comments updated
  • User guide updated
  • No documentation updates needed

Reviewer Notes

Any specific areas you'd like reviewers to focus on or questions you have.

Copilot AI review requested due to automatic review settings March 12, 2026 12:21
Comment thread plugins/ai-assistant/src/agent/aksAgentManager.ts Fixed
Comment thread plugins/ai-assistant/src/agent/aksAgentManager.ts Fixed
Comment thread plugins/ai-assistant/src/helper/apihelper.tsx Dismissed
Comment thread plugins/ai-assistant/src/components/assistant/AIInputSection.tsx Fixed
Comment thread plugins/ai-assistant/src/components/common/LogsDialog.tsx Fixed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new Headlamp AI Assistant plugin that adds an in-UI chat/agent experience for Kubernetes help, including provider configuration, prompt/context generation, and a LangChain-based tool system for Kubernetes API access (with confirmation UI for write operations).

Changes:

  • Adds a new plugins/ai-assistant package with build/test configuration and plugin metadata.
  • Implements provider configuration storage, UI settings (including terms), and core chat panel UI/UX (rendering YAML/logs, suggestions, agent thinking steps).
  • Adds a LangChain tool framework (registry/manager/base) plus a Kubernetes API tool and UI plumbing for confirmation + execution.

Reviewed changes

Copilot reviewed 57 out of 60 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
plugins/ai-assistant/tsconfig.json Adds TypeScript config for the plugin package.
plugins/ai-assistant/src/utils/ToolConfigManager.ts Tool enable/disable settings management.
plugins/ai-assistant/src/utils/SampleYamlLibrary.ts Sample YAML library plus YAML parsing/extraction helpers.
plugins/ai-assistant/src/utils/ProviderConfigManager.ts Provider config persistence helpers (save/delete/default/terms).
plugins/ai-assistant/src/utils/promptLinkHelper.ts Headlamp link instruction template + parsing/normalization of Headlamp links.
plugins/ai-assistant/src/utils/promptGenerator.ts Generates dynamic prompt suggestions from current UI context.
plugins/ai-assistant/src/utils/modalUtils.ts Markdown-to-plain-text and suggestion parsing; provider model helpers.
plugins/ai-assistant/src/utils/LogsHelper.ts Pod/container log fetching and pod info extraction helpers.
plugins/ai-assistant/src/utils/icons.ts Bundles Iconify collections for offline provider/logo icons.
plugins/ai-assistant/src/utils/contextGenerator.ts Generates human-readable AI context strings from Headlamp events/resources/warnings.
plugins/ai-assistant/src/utils.tsx Shared plugin constants, ConfigStore wiring, and global state hook.
plugins/ai-assistant/src/textstream.tsx Chat message stream UI with auto-scroll logic and YAML/editor integration.
plugins/ai-assistant/src/storybook.test.tsx Storyshots test bootstrap for plugin stories.
plugins/ai-assistant/src/langchain/tools/ToolManager.ts Loads enabled tools, binds them to the model, and executes tool calls.
plugins/ai-assistant/src/langchain/tools/ToolBase.ts Base class for defining LangChain tools and a standard tool response shape.
plugins/ai-assistant/src/langchain/tools/registry.ts Central tool registry (currently Kubernetes tool only).
plugins/ai-assistant/src/langchain/tools/kubernetes/types.ts Type definitions for Kubernetes tool UI state/callback context.
plugins/ai-assistant/src/langchain/tools/kubernetes/KubernetesTool.ts Implements kubernetes_api_request tool with confirmation flow.
plugins/ai-assistant/src/langchain/tools/kubernetes/index.ts Kubernetes tool barrel exports.
plugins/ai-assistant/src/langchain/tools/index.ts Tools barrel exports.
plugins/ai-assistant/src/langchain/PromptTemplates.ts Reusable prompt templates for structured tasks and error reporting.
plugins/ai-assistant/src/langchain/OutputParsers.ts Zod-based structured output parsers and safe parsing utility.
plugins/ai-assistant/src/index.tsx Registers UI panel + app bar action + plugin settings page (providers/tools).
plugins/ai-assistant/src/hooks/useKubernetesToolUI.ts Hook for Kubernetes tool UI state and API execution callback wrapper.
plugins/ai-assistant/src/hooks/useClusterWarnings.ts Hook to fetch per-cluster warnings/errors for context.
plugins/ai-assistant/src/helper/index.tsx Misc helpers (formatString, log URL detection, test-mode flag).
plugins/ai-assistant/src/helper/apihelper.tsx Executes confirmed Kubernetes API operations; formats tables/log responses into chat history.
plugins/ai-assistant/src/headlamp-plugin.d.ts Headlamp plugin type reference shim for TS.
plugins/ai-assistant/src/editordialog.tsx Monaco-based YAML editor dialog to apply resources to cluster.
plugins/ai-assistant/src/ContentRenderer.tsx Renders assistant markdown, YAML previews, logs button, and Headlamp links.
plugins/ai-assistant/src/config/modelConfig.ts Provider definitions (fields/models/icons) and defaults.
plugins/ai-assistant/src/components/settings/TermsDialog.tsx Terms/important info dialog for AI assistant usage.
plugins/ai-assistant/src/components/settings/index.ts Settings component barrel exports.
plugins/ai-assistant/src/components/index.ts Components barrel exports.
plugins/ai-assistant/src/components/common/YamlDisplay.tsx Read-only YAML preview with “Open in Editor” action and resize handling.
plugins/ai-assistant/src/components/common/SafeIcon.tsx Safe wrapper around Iconify <Icon> to avoid frozen-style mutation crash.
plugins/ai-assistant/src/components/common/LogsDialog.tsx Monaco-based log viewer dialog with copy/download and basic formatting.
plugins/ai-assistant/src/components/common/LogsButton.tsx UI card/button that opens the log viewer dialog.
plugins/ai-assistant/src/components/common/index.ts Common component barrel exports.
plugins/ai-assistant/src/components/common/ApiConfirmationDialog.tsx Confirmation/edit flow for API write operations (PUT/POST/DELETE).
plugins/ai-assistant/src/components/assistant/TestModeInput.tsx UI for injecting test messages/responses in “test mode”.
plugins/ai-assistant/src/components/assistant/PromptSuggestions.tsx Renders clickable suggestion chips (including content-filter recovery).
plugins/ai-assistant/src/components/assistant/index.ts Assistant component barrel exports.
plugins/ai-assistant/src/components/assistant/AIInputSection.tsx Input box, send/stop/clear, provider+model dropdown, agent mode selector.
plugins/ai-assistant/src/components/assistant/AIChatContent.tsx Wraps chat history stream and shows top-level API error banner.
plugins/ai-assistant/src/components/assistant/AIAssistantHeader.tsx Header (settings/close) and test-mode badge.
plugins/ai-assistant/src/components/agent/AgentThinkingSteps.tsx Displays streamed agent “thinking steps” grouped by phase.
plugins/ai-assistant/src/components/agent/AgentModeSelector.tsx Toggles chat vs agent mode and selects AKS-agent cluster.
plugins/ai-assistant/src/ai/prompts.ts Base system prompt (tool usage rules, YAML formatting, link rules, suggestions format).
plugins/ai-assistant/src/ai/manager.ts Abstract AI manager + prompt history model used by UI.
plugins/ai-assistant/README.md Plugin readme describing features and supported providers.
plugins/ai-assistant/package.json Plugin package definition, scripts, and dependencies.
plugins/ai-assistant/artifacthub-pkg.yml ArtifactHub metadata for distributing the plugin.
plugins/ai-assistant/.gitignore Plugin-specific ignores (dist/build/storybook/etc.).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/ai-assistant/tsconfig.json
Comment thread plugins/ai-assistant/src/utils/ToolConfigManager.ts
Comment thread plugins/ai-assistant/src/helper/apihelper.tsx
Comment thread plugins/ai-assistant/src/hooks/useClusterWarnings.ts
Comment thread plugins/ai-assistant/src/utils/ToolConfigManager.ts
@illume illume marked this pull request as draft March 12, 2026 12:35
@illume illume requested a review from Copilot March 12, 2026 13:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 57 out of 60 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread plugins/ai-assistant/src/utils/SampleYamlLibrary.ts
Comment thread plugins/ai-assistant/src/utils/modalUtils.ts Outdated
Comment thread plugins/ai-assistant/src/index.tsx
Comment thread plugins/ai-assistant/src/index.tsx
Comment thread plugins/ai-assistant/src/index.tsx
Comment thread plugins/ai-assistant/src/utils/LogsHelper.ts
Comment thread plugins/ai-assistant/src/components/common/YamlDisplay.tsx
@illume illume added the p0 Highest priority label Mar 12, 2026
@illume illume self-assigned this Mar 12, 2026
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 12, 2026

I'll work on hooking it up to preview toggle.

@illume illume force-pushed the ai-assistant-addition branch from 97d6a04 to e5847ae Compare March 12, 2026 16:35
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 12, 2026

Rebased against main.

@illume illume force-pushed the ai-assistant-addition branch from 2d0a432 to 2227345 Compare March 12, 2026 21:20
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 12, 2026

@ashu8912 worked on finding response parser related bugs.


Fixes 7 parsing bugs in agent response handling and adds comprehensive test coverage for all parsing and markdown utility functions in the ai-assistant plugin, including extensive malformed AI output tests, code block preservation tests for TypeScript, Mermaid, Bash, and JSON fenced blocks, and Kubernetes YAML markdown block tests with real-world manifests and parser edge cases.

Changes Made

Bug fixes

  • wrapBareYamlBlocks apiVersion detection — Regex required \S after apiVersion:, so apiVersion: with no inline value (value on next line) was missed. Relaxed to apiVersion:\s*.
  • wrapBareYamlBlocks premature early-exitif (/```ya?ml/i.test(text)) return text skipped the entire function when any yaml fence existed, leaving other bare YAML blocks in the same response unwrapped.
  • parseLogsButtonData brace counting — Character-by-character JSON extraction counted {/} inside string values, causing incorrect object boundary detection on content like "error: {unexpected}". Now tracks inString/escaped state.
  • parseSuggestionsFromResponse incomplete stripping — Used non-global regex so only the first SUGGESTIONS: line was removed from cleanContent. Changed to global replace to strip all occurrences.
  • looksLikeYaml missing --- document separator — Did not recognize --- (YAML document separator) or ... (document end marker), so wrapBareYamlBlocks left multi-document YAML with bare --- lines that rendered as markdown horizontal rules instead of staying inside a single fenced block.
  • markdownToPlainText regex ordering — Whitespace collapse (\s+' ') ran before line-start-anchored patterns (list markers, blockquotes, horizontal rules), removing all newlines first and making the ^-anchored regexes useless. Moved whitespace collapse after line-anchored patterns.
  • ThinkingStepTracker partial-row abandon regex — Did not match = sign table borders (+====+====+), inconsistent with AGENT_NOISE_PATTERNS. Added = to the character class, and also added = sign border matching to AGENT_NOISE_PATTERNS itself.

Test coverage (369 tests)

  • src/agent/parsing.test.ts — 230+ tests: extractAIAnswer, stripAnsi, normalizeBullets, looksLikeYaml, wrapBareYamlBlocks, cleanTerminalFormatting, isAgentNoiseLine, stripAgentNoise, ThinkingStepTracker, extractTaskRow, friendlyToolLabel, plus malformed output, code block preservation, and Kubernetes YAML suites
  • src/utils/modalUtils.test.ts — 60+ tests: markdownToPlainText, parseSuggestionsFromResponse, plus malformed markdown and malformed suggestions suites
  • src/ContentRenderer.test.ts — 60+ tests: parseLogsButtonData, parseJsonContent, isJsonKubernetesResource, convertJsonToYaml, plus malformed JSON and code block handling suites

Malformed AI output tests

Tests validate graceful handling of:

  • Truncated/garbled ANSI sequences, null bytes, interleaved noise in agent responses
  • Invalid YAML (trailing garbage, no kind/metadata, deeply indented, unclosed fences)
  • Incomplete/mismatched Rich terminal borders, nested and empty panels
  • Truncated JSON, trailing commas, single quotes, deeply nested structures, unicode content
  • Unclosed markdown formatting (bold, italic, code spans, links, nested markers)
  • Broken SUGGESTIONS lines (no pipes, only pipes, unicode, embedded in markdown)

Code block preservation tests

Tests verify that fenced code blocks for TypeScript (ts/typescript), Mermaid (mermaid), Bash (bash), and JSON (json) pass through all parsing stages unmodified:

  • wrapBareYamlBlocks does not wrap non-YAML fenced blocks
  • cleanTerminalFormatting preserves code block content
  • stripAgentNoise does not strip lines inside code fences
  • extractAIAnswer end-to-end pipeline preserves all code block types

Kubernetes YAML markdown block tests

Tests cover real-world Kubernetes manifests and YAML edge cases:

  • Full Deployment, Service, ConfigMap, Ingress, HPA manifests in fenced yaml blocks preserved through all parsing stages
  • Multi-document YAML with --- separators wrapped correctly in a single fenced block
  • YAML with | (literal block scalar) and > (folded block scalar) multiline string indicators
  • YAML anchors (&anchor) and aliases (*anchor) with merge keys (<<:)
  • Bare Kubernetes YAML (Deployment, Service) auto-wrapped even alongside already-fenced blocks
  • Deeply nested YAML specs (6+ indent levels) and empty-value keys (apiVersion: with value on next line)
  • ... document end markers recognized by looksLikeYaml
  • End-to-end extractAIAnswer pipeline correctly wraps multi-document bare YAML

Internal parsing functions exported via _testing namespace for test access.

Test Cases

  1. wrapBareYamlBlocks wraps bare YAML even when another block is already fenced
  2. parseLogsButtonData correctly handles braces inside JSON string values
  3. parseSuggestionsFromResponse strips all SUGGESTIONS: lines from cleanContent, not just the first
  4. parseSuggestionsFromResponse handles non-string inputs (arrays, objects, null, undefined)
  5. ThinkingStepTracker handles wrapped task table rows spanning multiple terminal lines
  6. extractAIAnswer end-to-end: strips ANSI, removes noise, normalizes bullets, wraps YAML, cleans Rich formatting
  7. TypeScript, Mermaid, Bash, JSON code blocks preserved through full extractAIAnswer pipeline
  8. Truncated/malformed JSON in parseLogsButtonData returns null gracefully
  9. Unclosed markdown formatting in markdownToPlainText produces reasonable plain text output
  10. Garbled agent noise patterns are stripped without affecting real content
  11. Fenced Kubernetes Deployment/Service/ConfigMap/Ingress/HPA YAML preserved through all stages
  12. Multi-document YAML with --- separators wrapped in single fenced block
  13. YAML with |/> multiline scalars and anchors/aliases handled correctly
  14. looksLikeYaml recognizes --- document separator and ... end marker
  15. = sign table borders stripped by ThinkingStepTracker and AGENT_NOISE_PATTERNS

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 12, 2026

@ashu8912 some contrast fixes.


Code blocks in the agent chat used hardcoded grey[100]/grey[900] regardless of theme mode, and success icons used hardcoded #4caf50. Both break contrast in dark mode. This PR makes these colors theme-aware, fixes the user message bubble to use the real theme.palette.sidebar.selectedBackground instead of a hardcoded color, and adds Storybook stories (co-located next to their components) to verify contrast visually across light and dark themes.

Changes Made

  • ContentRenderer.tsx: Swap grey[100]grey[900] for code block bg/text based on theme.palette.mode
  • AgentThinkingSteps.tsx: Replace 3× hardcoded #4caf50 with theme.palette.success.main
  • ChatContrast stories: Use theme.palette.sidebar.selectedBackground (with fallback) for user message bubble instead of hardcoded #c2c2c2, matching production textstream.tsx behavior
  • Edge case stories added:
    • ContentRenderer: empty content, very long content, nested code blocks, YAML blocks, inline code heavy, multiple code block languages
    • ChatContrast: short messages, error/success sequences, long unbroken strings, deeply nested markdown, YAML + multi-language code blocks
    • AgentThinkingSteps: single step, many steps (15+), long step labels, all-pending state

Screenshots/Videos

Light Theme

Dark Theme

Agent Thinking Steps (Dark, Completed)

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 13, 2026

@ashu8912 I pushed a commit for viewport scrolling.


Fix chat viewport scrolling to show top of new agent responses

New agent responses scrolled the viewport to the bottom of the response instead of the top. Users couldn't see the beginning of the answer without scrolling back up. Additionally, when the last message was a user message (during loading), the viewport would incorrectly jump back to a previous assistant message, and tall new responses could cause auto-scroll to be skipped entirely.

Changes Made

  • textstream.tsxscrollToShowNewMessage: Replaced branching logic with a simple approach: find the latest assistant message element and scroll its top to the top of the viewport. For short preceding user questions (less than 30% of viewport height), scrolls to show the question above the response for context. For long user questions (≥30% of viewport), scrolls to the bottom of the question so the tail of the question and the start of the response are both visible — preventing a full-screen question from hiding the response entirely. When the newest message is a user message (e.g. during loading before the assistant response arrives), scrolls to bottom to keep the user message and loading indicator visible instead of jumping back to a previous assistant message. Uses a direct querySelector with a specific data-message-index attribute selector instead of querySelectorAll + Array.from + find to avoid O(n) DOM scans on every auto-scroll.
  • textstream.tsxwasNearBottomRef: Added a ref updated in handleScroll to track whether the user was near the bottom before the history update. The history-change useEffect now reads this ref instead of calling isNearBottom() post-render, so tall new messages no longer cause auto-scroll to be skipped when the user was following the conversation.
  • textstream.tsxlastUserMessageCountRef reset: Added a guard to reset the ref when currentUserMessageCount decreases (e.g. when history is cleared via setPromptHistory([]) without unmounting), so subsequent user messages are still properly detected as "new" and trigger the always-scroll-to-bottom behavior.
  • textstream.tsx — scroll trigger useEffect: When user has scrolled away from bottom, only show the scroll-to-bottom FAB — don't force-scroll to the new message.
  • textstream.tsxisLoading useEffect: Calls scrollToBottom directly when loading starts instead of routing through scrollToShowNewMessage, since the intent is always to keep the user message and loading indicator visible.
  • textstream.stories.tsx: Added 5 interactive stories (ScrollToTopOfResponse, ScrollToTopOfResponseDark, MultiStepConversation, LongUserQuestionsWithYAML, SlowResponsesWithLoader) with "Add Agent Response" buttons to demonstrate and verify scroll behavior. Stories simulate the real flow by adding a user question before each agent response, so the scroll-trigger useEffect (which keys on new user messages) fires correctly on every click. Each story uses a useRef + useEffect cleanup to clear setTimeout timers on unmount, preventing state-update-on-unmounted-component warnings when navigating away mid-timer. The LongUserQuestionsWithYAML story includes multi-line YAML-containing user questions to verify the scroll-to-bottom-of-long-question behavior. The SlowResponsesWithLoader story uses realistic 2–5 second delays with isLoading=true to verify that loading spinners are visible during the wait.

Test Cases

  1. All 383 existing tests pass unchanged
  2. Storybook stories verify scroll position after adding agent responses
  3. Multi-step conversation story verifies scroll across sequential exchanges
  4. Repeated clicks of "Add Agent Response" correctly scroll to each new response
  5. Long YAML user questions scroll to show the bottom of the question + top of response
  6. Slow response story shows loading indicator for 2–5 seconds before response appears

Screenshots/Videos

Before — conversation with pending user question:

After clicking "Add Agent Response" — viewport at the top of the new response:

Reviewer Notes

The old scrollToShowNewMessage had separate code paths for "one response after user" vs "multiple responses" with different scroll targets (user message top, user message bottom, half of last message). All paths now converge on one behavior: scroll to show the user's question for context followed by the top of the latest assistant message. Short questions (< 30% viewport) show entirely above the response; long questions (≥ 30% viewport) scroll to show their bottom portion so the user sees the tail of what they asked alongside the start of the answer, rather than the question consuming the entire viewport. There's an explicit fallback to scroll-to-bottom when the newest message is from the user (loading state). The wasNearBottomRef ensures that a tall new message doesn't trick the near-bottom check into thinking the user had scrolled away. The lastUserMessageCountRef is reset when history is cleared so new-message detection remains correct across conversation resets.

The Storybook stories simulate the real chat flow by adding both a user question and an agent response on each button click. This ensures the scroll-trigger useEffect (which keys on new user message count) fires correctly on every click, not just the first one. The SlowResponsesWithLoader story adds realistic 2–5 second delays with isLoading=true to verify that loading spinners are visible during the wait, matching real-world AI response latency.

Comment thread plugins/ai-assistant/src/agent/debugLog.ts Dismissed
Comment thread plugins/ai-assistant/src/agent/debugLog.ts Dismissed
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 13, 2026

@ashu8912 Improved parsing and rendering of AI responses.


Add levelled debug logging, robust code-line detection, real-world parsing tests and stories, normalize terminal-formatted responses, collapse terminal blank lines, fix code block boundary detection, fix GFM table/YAML rendering, wrap bare code command...

Screenshots/Videos

Storybook rendering of the Real World stories:

image

Description

Adds structured, levelled debug logging throughout the AKS agent response parsing pipeline and incoming data flow. Logging uses a simple const DEBUG level system at the top of debugLog.ts: 0/false = off, 1 = lifecycle events, 2 = per-transform detail, 3 = verbose per-line output (currently defaults to 3 for active development). Logging is gated behind import.meta.env.DEV so it is always off in production builds and off during tests (import.meta.env.MODE === 'test'). All debug logs output full untruncated data for maximum visibility during development. A dumpForTestCase helper logs raw→parsed pairs as JSON strings for direct copy-paste into new test cases.

This PR also adds real-world parsing test cases and ContentRenderer Storybook stories derived from dev console captures. Stories use raw exec output (with ANSI codes, bracketed paste mode, bash prompts, task tables, and terminal formatting) piped through extractAIAnswer, exercising the full parsing→rendering pipeline. During Storybook and Playwright verification, this work uncovered and fixed several rendering issues:

  • GFM markdown table separator rows were being stripped by the agent noise filter, breaking table rendering
  • Terminal-styled AI responses were being rendered as plain paragraphs instead of markdown-friendly ordered lists and fenced code blocks
  • Indented multi-document YAML blocks (Namespace + Deployment + Service) were absorbing subsequent prose because wrapBareYamlBlocks didn't track base indentation
  • Terminal-formatted code blocks (Rich panels with [40m background ANSI codes) produced blank lines between every content line, breaking Dockerfile and YAML rendering and splitting prose paragraphs into separate <p> elements
  • Dockerfile parser directives (# syntax=docker/dockerfile:1) were rendered as markdown <h4> headings instead of being included in the code block
  • Code blocks in normalizeTerminalMarkdown were absorbing centered terminal headings (like "Option B: ...") and subsequent prose sections because code block collection didn't stop at blank-line + non-code-line boundaries
  • Shell comments with URLs (like # then open http://localhost:8080) were not detected as code lines, causing them to break out of kubectl code blocks
  • Bare shell/kubectl/curl commands appearing outside fenced code blocks were rendered as plain paragraphs instead of code blocks
  • Terminal line wrapping at ~80 columns split YAML values mid-line, leaving orphaned ANSI code remnants (like [0m, [97;40m) that broke YAML parsing and caused content to render as <h5> headings
  • YAML values with long strings (e.g. OTEL_EXPORTER_OTLP_ENDPOINT: followed by a URL on the next line) were not being continued into the YAML block, causing the value to appear as prose outside the code fence
  • YAML values with unclosed flow-style braces (e.g. valueFrom: { secretKeyRef: { name: db-credentials, key: split across lines) were not being continued, causing the closing } } to appear as prose
  • YAML code blocks were no longer rendering as Monaco editors (YamlDisplay) — parseKubernetesYAML threw on multi-document YAML with --- separators, and CodeComponent was incorrectly treating all fenced code blocks as inline code
  • Single-line fenced code blocks were rendered as <em> (italic) instead of code blocks because CodeComponent treated them as inline
  • YAML comment lines like # Shared config/secrets before apiVersion: were excluded from the yaml fence, causing them to render as markdown headings
  • looksLikeShellOrDockerCodeLine had a false positive on # Shared config/secrets because the /\w path regex matched config/secrets without requiring a preceding whitespace
  • joinYamlContinuation consumed entire YAML blocks when { was left unclosed by terminal chunk boundaries (e.g. labels: { app.kubernetes.io/name: payments, app.kubernetes.io/part-of: shop split across chunks, with spec: on the next line being consumed as a "continuation")
  • kind: split from its value Kustomization at terminal chunk boundaries was not being rejoined

The code-line detection function looksLikeShellOrDockerCodeLine was substantially improved with a 3-tier approach: ~500 unambiguous command keywords (docker, kubectl, helm, terraform, npm, pip, cargo, etc.), ~50 ambiguous words that are also common English (cat, find, sort, kill, wait, test, set, etc.) that require shell syntax confirmation via the new hasShellSyntax helper, and structural pattern matching for paths, flags, pipes, redirects, and assignment operators. Dockerfile parser directives (# syntax=, # escape=, # check=) and shell comments with URLs (# then open http://...) are now recognized as code lines to prevent markdown heading misinterpretation and code block fragmentation. The path-detection regex was fixed to require whitespace before / to avoid false positives on comment text like # Shared config/secrets.

A new collapseTerminalBlankLines function was added to handle the characteristic pattern of Rich/terminal-formatted output where each content line is followed by a blank line due to chunk-by-chunk data arrival. This function:

  • Removes blank lines between terminal-formatted code lines (lines starting with a space from Rich panel padding)
  • Joins terminal-wrapped prose continuations (lines at ~75-80 chars followed by continuation text)
  • Collapses runs of 3+ blank lines to a single blank line
  • Preserves intentional paragraph breaks (lines ending with sentence-terminal punctuation)

A new wrapBareCodeBlocks function was added to detect and wrap bare shell/kubectl/curl/docker commands that appear outside of markdown fenced code blocks. These commands are common in AI agent responses but were being rendered as plain paragraphs. The function identifies consecutive lines that look like shell commands (using looksLikeShellOrDockerCodeLine) and wraps them in ```bash fences.

The ANSI stripping in cleanTerminalFormatting was enhanced with a second-pass regex (/\[\d[\d;]*m/g) to catch orphaned ANSI code fragments — partial escape sequences left behind when terminal line wrapping splits an ANSI escape sequence across chunk boundaries. These fragments (like [0m, [97;40m, [91;40m) were not caught by the standard \x1b[...m stripping because they lack the ESC (\x1b) prefix byte, which was in the previous chunk.

The wrapBareYamlBlocks function was enhanced with YAML value continuation detection: when a YAML key's value is split across terminal-wrapped lines (e.g. OTEL_EXPORTER_OTLP_ENDPOINT: on one line and "http://..." on the next), or when flow-style braces are unclosed (e.g. valueFrom: { secretKeyRef: { name: db-credentials, key: followed by POSTGRES_USER } }), these continuation lines are now absorbed into the YAML block. The looksLikeYaml helper was extended to recognize quoted scalar values ("...", '...') and flow-style closers (}, ] }) as valid YAML continuation content. Additionally, YAML comment lines (like # Namespaces, # -----) immediately before apiVersion: are now looked back and included inside the yaml fence to prevent them from being misinterpreted as markdown headings. The joinYamlContinuation function was also fixed to handle bare unquoted values after kind: (for terminal chunk splits like kind: / Kustomization) and to stop consuming lines on unclosed braces when the next line looks like a new YAML key (e.g. spec:) rather than a flow-style continuation.

The parseKubernetesYAML function in SampleYamlLibrary.ts was fixed to use YAML.parseAllDocuments() instead of YAML.parse(), which threw YAMLParseError on multi-document YAML containing --- separators. This was causing console errors and preventing YAML code blocks from rendering as Monaco editors.

The CodeComponent in ContentRenderer.tsx was fixed to only apply inline code styling when props.inline is explicitly true, rather than when className is undefined. This was causing single-line fenced code blocks to render as <em> (italic text) instead of proper code blocks.

All ContentRenderer Storybook stories now pass onYamlDetected so that YAML code blocks render as Monaco editors (YamlDisplay) with "Open In Editor" buttons, matching the real application behavior. A new YamlEditorDisplay story was added to specifically demonstrate YAML rendering as an interactive editor.

Before (from real-world Java deployment responses):

  • # syntax=docker/dockerfile:1 rendered as <h4> heading outside code block
  • Prose broken into separate paragraphs ("Here's a minimal..." / "app on Kubernetes..." / "Actuator.")
  • Extra blank lines between every Dockerfile instruction
  • Extra blank lines in YAML between apiVersion: and kind:
  • "Option B: build your own..." centered heading absorbed into kubectl code block
  • Java controller code and Dockerfile instructions merged into one giant code block
  • curl -s localhost:8080/api/users rendered as plain text paragraph
  • Large multi-document YAML with terminal-wrapped values rendered with orphaned [0m fragments as <h5> headings
  • YAML code blocks rendered as plain code instead of Monaco editors
  • parseKubernetesYAML throwing YAMLParseError on multi-document YAML
  • # Shared config/secrets YAML comments rendered as markdown headings
  • labels: { ... shop with unclosed { consuming entire subsequent YAML into one line
  • kind: / Kustomization split at chunk boundary not rejoined

Related Issues

Related to the ai-integration PR — addresses difficulty diagnosing response parsing bugs in development and verifying real-world agent response rendering.

Changes Made

  • src/agent/debugLog.ts — Levelled debug logging module with a single const DEBUG control variable:

    • DEBUG = 0 or false — all logging off (for clean console)
    • DEBUG = 1debugLog() emits lifecycle events (session start, question complete, final parse result)
    • DEBUG = 2detailLog() adds per-transform summaries (line counts after each step, noise lines dropped)
    • DEBUG = 3verboseLog() adds per-line detail (every individual noise line, chunk contents, full raw output)
    • dumpForTestCase(tag, raw, parsed) — logs both as JSON.stringify for copy-paste into tests, gated at level 3
    • All logging gated behind import.meta.env.DEV — completely silent in production builds and tests regardless of DEBUG level
  • aksAgentManager.ts parsing pipeline — Levelled logging at each transform step in extractAIAnswer: input size (level 1), normalised line count (level 2), lines after stripCommandEcho (level 2), AI line detection (level 2), noise stripping counts (level 2), terminal formatting stats (level 2), terminal blank line collapsing (level 2), terminal-markdown normalization (level 2), bullet normalization (level 2), bare YAML wrapping (level 2), bare code wrapping (level 2), and full final output (level 1). Per-line detail (individual noise lines, chunk contents) at level 3.

  • aksAgentManager.ts intermediate helpers — Levelled logging in:

    • stripCommandEcho — logs when command echo is detected and how many lines are stripped
    • stripAgentNoise — logs dropped noise lines (verbose) and totals (detail)
    • cleanTerminalFormatting — logs border/panel cleanup stats; enhanced with second-pass orphaned ANSI code stripping (/\[\d[\d;]*m/g) for terminal line-wrap fragments
    • collapseTerminalBlankLinesnew function that removes terminal-artifact blank lines between code lines, joins terminal-wrapped prose continuations, and collapses multi-blank runs. Logs removed/joined/collapsed counts.
    • normalizeBullets — logs when Unicode bullets are converted to markdown dashes
    • normalizeTerminalMarkdown — normalization pass that converts terminal-style numbered choice lines into markdown ordered-list items, trims centered heading lines, wraps indented code sections in fenced code blocks, and now correctly stops code blocks at blank-line + non-code-line boundaries to prevent absorbing centered headings and subsequent prose sections
    • looksLikeShellOrDockerCodeLine3-tier detection: ~500 unambiguous command keywords, ~50 ambiguous words with hasShellSyntax confirmation, structural pattern matching (paths, flags, pipes, redirects, assignments, shebangs, comments), Dockerfile parser directive detection (# syntax=, # escape=, # check=), shell comment with URL detection (# then open http://...), and fixed path regex to require whitespace before / to avoid false positives on # Shared config/secrets
    • hasShellSyntax — new helper that checks for shell-specific syntax (flags like -r, --force; paths like ./, /usr/; operators like |, >, &&, ||; variable references like $VAR, ${HOME}; command substitution; quotes with spaces) to disambiguate common English words from shell commands
    • wrapBareYamlBlocksfixed indent tracking: now records the base indentation of apiVersion: lines and stops the YAML block when a less-indented non-blank line is encountered, preventing prose like Apply: from being absorbed into YAML fences. Also dedents indented YAML to column 0 inside the fence. Added YAML value continuation detection for terminal-wrapped values (key on one line, value on next) and unclosed flow-style braces. Added comment look-back to include YAML comment lines (# Namespaces, # -----) before apiVersion: inside the yaml fence. Fixed joinYamlContinuation to handle bare unquoted values after kind: and to stop consuming on unclosed braces when the next line is a new YAML key.
    • wrapBareCodeBlocksnew function that detects bare shell/kubectl/curl/docker commands outside fenced code blocks and wraps consecutive command lines in ```bash fences. Skips lines already inside fenced blocks.
    • looksLikeYamlextended to recognize quoted scalar values ("...", '...') and flow-style closers (}, ] }) as valid YAML content
  • aksAgentManager.ts WebSocket data flow — Levelled logging in handleData / handleChannel / handleStdout for channel info, chunk sizes, full incoming data, and thinking step updates. handleStdout completion detection logs commandSent, hasAiMarker, and hasPrompt state.

  • aksAgentManager.ts session lifecycle — Levelled logging in:

    • resolveCurrentQuestion — logs output length, AI marker presence, and full output
    • handleStderr — logs stderr chunk details
    • handleConnectionFailure — logs stdout/stderr lengths and resolved state
  • aksAgentManager.ts GFM table fix — Narrowed the AGENT_NOISE_PATTERNS "table remnant" regex to require a + character, so GFM markdown table separators (|---|---|) are preserved while agent task-table borders (+---+---+) are still stripped.

  • ContentRenderer.tsx — Levelled logging at each content-type detection branch (JSON error/success, K8s resource, LOGS_BUTTON, unformatted YAML, markdown) with full content output. Additional logging in:

    • processUnformattedYaml — logs part count after --- split, per-part type detection decisions, and total sections rendered
    • CodeComponent — logs className, inline status, YAML/JSON-K8s detection, and content length. Fixed inline detection to only apply inline code styling when props.inline is explicitly true, preventing single-line fenced code blocks from rendering as <em>.
  • SampleYamlLibrary.tsFixed parseKubernetesYAML to use YAML.parseAllDocuments() instead of YAML.parse(), which threw YAMLParseError on multi-document YAML containing --- separators. This restores YAML code blocks rendering as Monaco editors.

  • parsing.test.ts — Added comprehensive test suites:

    • Real-world extractAIAnswer test cases covering bracketed paste mode, ANSI codes, Rich formatting, task tables, GFM table preservation, multi-document YAML, Python deployment advice with terminal formatting, terminal-formatted Java deployment with Rich panel code blocks (Dockerfile + multi-document YAML with Namespace/Deployment/Service/Ingress/HPA), Option A/B Java deployment with centered terminal headings, kubectl commands, Java controller code, multi-stage Dockerfile, and docker build/push commands, K8s deploy with curl commands verifying bare code wrapping, and real-world microservice YAML verifying comment inclusion, multi-document parsing, and terminal-wrapped value joining
    • collapseTerminalBlankLines — test cases covering blank lines between code lines, prose continuation joining, multi-blank collapse, mixed content, uppercase continuation words, and Kubernetes-specific patterns (kubectl commands between YAML blocks, mixed YAML + kubectl + prose, deployment rollout monitoring)
    • hasShellSyntax — test cases (flags, paths, pipes, variable refs, quotes, negatives for plain English)
    • looksLikeShellOrDockerCodeLine — 160+ test cases: Dockerfile instructions including parser directives (# syntax=, # escape=, # check=), common CLI tools (~30), cloud/container CLIs, ambiguous words with shell syntax (~15), ambiguous words without syntax as negatives (~15), prose/markdown negatives (~30), YAML/Kubernetes negatives (~10), headings/labels negatives, shell comments with URLs (# then open http://localhost:8080), and YAML comment false positive negatives (# Shared config/secrets)
    • normalizeTerminalMarkdown — test cases including empty lines in code blocks, Kubernetes YAML passthrough, mixed content with numbered lists, centered headings, indented code blocks, kubectl commands after YAML, multi-section K8s deployment, Dockerfile + YAML + prose, mixed K8s commands, YAML block followed by prose, centered heading breaking code blocks, and multi-option deployment with centered headings
    • wrapBareYamlBlocks — indent-tracking tests verifying YAML blocks stop at less-indented prose and multi-document YAML is properly dedented, plus YAML value continuation tests for terminal-wrapped values, unclosed flow braces, bare value joins (kind: + Kustomization), and unclosed brace stopping at new YAML keys (labels: { ... not consuming spec:)
    • wrapBareCodeBlocks — test cases for single commands, consecutive commands, commands already in fences (skipped), mixed prose and commands, and multi-line curl with backslash continuation
    • Kubernetes-focused extractAIAnswer tests covering kubectl create/apply, multi-document YAML with explanations, HPA with kubectl top, RBAC with ServiceAccount/Role/RoleBinding, Ingress with annotations, PersistentVolume + StatefulSet, CronJob + ConfigMap, mixed Helm + kubectl workflows, and bare curl/kubectl commands wrapping
  • ContentRenderer.stories.tsx — New stories using raw exec output through extractAIAnswer: RealWorldPodStatus, RealWorldCrashDiagnosis, RealWorldCrashDiagnosisDark, RealWorldBestPractices, RealWorldMultiResource, RealWorldMultiResourceDark, RealWorldBareYamlService, RealWorldPythonDeploymentAdvice, RealWorldJavaDeployTerminal, RealWorldJavaDeployTerminalDark, RealWorldJavaDeployOptionAB, RealWorldJavaDeployOptionABDark, RealWorldK8sDeployWithCurl, RealWorldK8sDeployWithCurlDark, RealWorldMicroserviceYaml, and YamlEditorDisplay. All stories pass onYamlDetected so YAML code blocks render as Monaco editors matching real application behavior. Each exercises the full parsing→rendering pipeline and was verified with Playwright.

Test Cases

  1. All 592 tests pass with zero debug noise (completely clean test output)
  2. No console.warn or console.debug output during tests — all logging is gated behind isEnabled() which checks import.meta.env.DEV
  3. Real-world extractAIAnswer tests verify correct parsing of raw exec output with ANSI codes, bracketed paste, Rich formatting, bash prompts, terminal-styled numbered lists, Rich panel code blocks, centered terminal headings with Option A/B sections, and bare curl/kubectl commands
  4. collapseTerminalBlankLines tests verify blank line removal between code lines, prose continuation joining (including uppercase continuation words like "Actuator."), multi-blank collapse, mixed content, and Kubernetes-specific patterns (kubectl between YAML, mixed YAML + kubectl + prose)
  5. hasShellSyntax tests verify shell syntax detection (flags, paths, pipes, variable refs) and rejection of plain English
  6. 160+ looksLikeShellOrDockerCodeLine tests verify 3-tier detection with extensive positive cases (Dockerfile including parser directives, CLI tools, cloud CLIs, shell comments with URLs) and negative cases (prose, markdown, YAML, headings, labels, YAML comments like # Shared config/secrets)
  7. normalizeTerminalMarkdown tests cover numbered lists, centered headings, code blocks with empty lines, K8s YAML passthrough, mixed content, kubectl commands after YAML, multi-section K8s deployments, Dockerfile + YAML + prose, centered heading breaking code blocks, and multi-option deployment with centered headings
  8. wrapBareYamlBlocks indent-tracking tests verify YAML blocks stop at less-indented prose, multi-document YAML is dedented, YAML value continuations (terminal-wrapped values, unclosed flow braces) are absorbed, bare values after kind: are joined, and unclosed braces stop at new YAML keys
  9. wrapBareCodeBlocks tests verify single commands, consecutive commands, already-fenced commands (skipped), mixed prose/commands, and multi-line curl
  10. GFM table separator test verifies |---|---| rows are not stripped as noise
  11. Terminal-formatted Java deployment e2e test verifies prose joining, Dockerfile code block inclusion of # syntax directive, YAML block continuity, and bullet list rendering
  12. Option A/B Java deployment e2e test verifies centered headings are NOT absorbed into code blocks, kubectl commands stay in their own code block, Java controller code renders as separate code block, Dockerfile renders as separate code block, and docker build/push commands render correctly
  13. K8s deploy with curl test verifies bare kubectl apply and curl commands are wrapped in code fences
  14. Real-world microservice YAML tests verify single YAML block output, comments included inside fence, all 4 multi-document sections valid, per-document content correctness, terminal-wrapped value joining, and parseKubernetesYAML multi-doc support
  15. All Storybook stories (existing + new) verified rendering correctly via Playwright with YAML displayed as Monaco editors
  16. tsc, lint, format all pass clean

Breaking Changes

No breaking changes. All existing parsing behavior is preserved — the new collapseTerminalBlankLines pass only activates on content with terminal-artifact blank lines (alternating content/blank patterns from Rich panel output). The normalizeTerminalMarkdown pass only activates on terminal-formatted content (numbered choice lines, centered headings, indented code blocks). The wrapBareYamlBlocks indent-tracking fix is backward compatible — it only changes behavior for indented YAML blocks followed by less-indented prose. The wrapBareCodeBlocks pass only wraps lines that are detected as shell commands and are not already inside fenced code blocks. The orphaned ANSI code stripping only removes patterns that match [digits;digitsm which are never valid markdown content. The parseKubernetesYAML fix to use parseAllDocuments is strictly more capable — it handles both single and multi-document YAML. The CodeComponent inline fix only changes behavior for fenced code blocks that were incorrectly treated as inline.

Performance Impact

  • No performance impact

The debug logging is completely eliminated in production builds via import.meta.env.DEV gating. The looksLikeShellOrDockerCodeLine function uses Set lookups (O(1)) for the ~550 command keywords and simple regex checks for structural patterns. The collapseTerminalBlankLines and wrapBareCodeBlocks functions are single-pass O(n) scans over lines.

Reviewer Notes

  • The const DEBUG = 3 in debugLog.ts is intentionally set to verbose (level 3) for active development. Change to 0 when debugging is no longer needed.
  • The looksLikeShellOrDockerCodeLine 3-tier approach (unambiguous commands → ambiguous + shell syntax → structural patterns) was designed to avoid false positives on English prose while still catching the wide variety of CLI tools that appear in AI agent responses. The path-detection regex now requires whitespace before / to prevent # Shared config/secrets from being detected as a code line (the old regex /(^|\s)\// matched /secrets as a path).
  • The hasShellSyntax helper is key to preventing false positives — words like "cat", "find", "sort", "kill", "test" only match when accompanied by shell-specific syntax (flags, paths, pipes, etc.).
  • The wrapBareYamlBlocks indent-tracking fix ensures that indented multi-document YAML (common in agent responses showing Namespace + Deployment + Service) doesn't absorb subsequent prose into the YAML fence. The new YAML value continuation detection handles terminal line wrapping that splits key: value across two lines and unclosed flow-style braces ({ name: ..., key: on one line, VALUE } } on the next). The comment look-back ensures YAML comments like # Namespaces are included inside the fence rather than being rendered as markdown headings.
  • The joinYamlContinuation fix for unclosed braces is critical for real-world terminal output: when labels: { app.kubernetes.io/name: payments, app.kubernetes.io/part-of: shop is split at a chunk boundary with no closing }, the old code would consume everything that followed (including spec:, replicas:, etc.) into one massive line. The fix now only continues on quoted values or flow-style closers (}, ]), stopping when it encounters what looks like a new YAML key (e.g. spec:).
  • The joinYamlContinuation bare value fix handles kind: / Kustomization being split across terminal chunks — when a line ends with : and the next line is a single capitalized word (common YAML values like Namespace, Deployment, Kustomization), it's joined.
  • The collapseTerminalBlankLines function addresses a fundamental issue with Rich/terminal-formatted output: each content line arrives as a separate chunk followed by \r\n, producing blank lines between every line. The function uses heuristics (leading-space detection for code lines, line-length + punctuation for prose continuations) to distinguish terminal artifacts from intentional paragraph breaks.
  • Dockerfile parser directives (# syntax=, # escape=, # check=) are now detected as code lines to prevent markdown's # heading interpretation from turning # syntax=docker/dockerfile:1 into an <h4> heading.
  • Shell comments with URLs (# then open http://localhost:8080) are now detected as code lines so they don't break kubectl code blocks.
  • The normalizeTerminalMarkdown code block collection now correctly stops when it encounters a blank line followed by a non-code line, preventing centered terminal headings and prose from being absorbed into preceding code blocks. This was the root cause of "Option B: ..." being swallowed into the kubectl code block in the Option A/B Java deployment response.
  • The wrapBareCodeBlocks function addresses a common pattern where AI responses include bare kubectl apply -f ... or curl -s localhost:8080/... commands outside of fenced code blocks. These were being rendered as plain <p> paragraphs instead of code blocks.
  • The orphaned ANSI code stripping (/\[\d[\d;]*m/g) in cleanTerminalFormatting addresses a fundamental issue with terminal line wrapping: when an ANSI escape sequence like \x1b[97;40m is split across two 80-column terminal chunks, the first chunk gets \x1b (caught by standard stripping) but the second chunk starts with [97;40m (no ESC prefix), which was not caught. These orphaned fragments caused YAML content to be misinterpreted as markdown headings (e.g. [0m: postgres-pvc rendered as an <h5>).
  • The parseKubernetesYAML fix using YAML.parseAllDocuments() was necessary because real-world AI responses frequently include multi-document YAML with --- separators. The old YAML.parse() threw on these, causing console errors and preventing YAML from rendering as Monaco editors.
  • The CodeComponent inline fix ensures that fenced code blocks (even single-line ones) render as proper <pre><code> blocks rather than inline <code> with <em> styling. Only backtick-delimited inline code (where props.inline is true) gets inline treatment.
  • All Storybook stories now pass onYamlDetected so YAML renders as Monaco editors (YamlDisplay) with "Open In Editor" buttons, matching real application behavior. Previously stories omitted this prop, causing YAML to render as plain code blocks even though the real app showed editors.
  • Known remaining issues: The payments YAML block in the microservice example still has terminal chunk boundary issues where labels: { ... shop gets its } split across chunks. The joinYamlContinuation fix prevents the worst case (consuming the entire block) but the resulting YAML may still have formatting artifacts from the chunk split. Centered text like "How this scales..." may still render inside a code block rather than as a heading in some terminal-formatted responses.

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 13, 2026

rebased against main

@illume illume force-pushed the ai-assistant-addition branch from 5093388 to 9a69339 Compare March 13, 2026 19:03
illume and others added 6 commits March 13, 2026 20:11
…rsing/markdown

fix parsing bugs and add comprehensive tests for agent response/markdown parsing

- Fix wrapBareYamlBlocks: handle apiVersion with no value after colon
- Fix wrapBareYamlBlocks: remove early-exit when yaml fence exists (allows wrapping additional bare blocks)
- Fix parseLogsButtonData: track string context when counting braces to handle braces inside JSON string values
- Add 172 tests covering: stripAnsi, normalizeBullets, looksLikeYaml, wrapBareYamlBlocks, cleanTerminalFormatting, isAgentNoiseLine, stripAgentNoise, extractAIAnswer, ThinkingStepTracker, extractTaskRow, friendlyToolLabel, parseLogsButtonData, parseJsonContent, isJsonKubernetesResource, convertJsonToYaml, markdownToPlainText, parseSuggestionsFromResponse

fix parseSuggestionsFromResponse to strip all SUGGESTIONS: lines from cleanContent

add malformed AI output tests and code block preservation tests

Add 100+ new tests covering:
- Code block preservation (ts, mermaid, bash, json) across wrapBareYamlBlocks,
  cleanTerminalFormatting, stripAgentNoise, and extractAIAnswer pipeline
- Malformed extractAIAnswer inputs (truncated, garbled ANSI, multiple AI: prefixes,
  null bytes, interleaved noise)
- Malformed YAML (trailing garbage, no kind/metadata, deeply indented, unclosed fences)
- Malformed Rich terminal borders (incomplete, mismatched, nested, empty panels)
- Garbled agent noise patterns, edge cases in looksLikeYaml, normalizeBullets, stripAnsi
- Malformed ThinkingStepTracker inputs (empty models, unknown tool finish, partial rows)
- Malformed parseLogsButtonData JSON (truncated, trailing commas, single quotes, deeply
  nested, unicode, very long content, null logs)
- Malformed parseJsonContent (truncated, trailing commas, YAML/XML content)
- Edge cases in isJsonKubernetesResource and convertJsonToYaml
- Malformed markdown (unclosed bold/italic/code/links, nested unclosed markers)
- Malformed SUGGESTIONS (no pipes, only pipes, unicode, embedded in markdown)

fix: looksLikeYaml misses --- YAML doc separator, markdownToPlainText ordering bug, ThinkingStepTracker = sign border

- looksLikeYaml: recognize --- (YAML document separator) and ... (document end
  marker) so wrapBareYamlBlocks keeps multi-document YAML in a single fence
  instead of leaving bare --- as a markdown horizontal rule.
- markdownToPlainText: move whitespace collapse (\s+ → ' ') after the
  line-start-anchored patterns (list markers, blockquotes, horizontal rules).
  Previously the collapse removed all newlines first, making the ^ anchors
  useless for list/blockquote/hr removal.
- ThinkingStepTracker.processLine: partial-row abandon regex now includes =
  (consistent with the updated AGENT_NOISE_PATTERNS).

docs: improve comments on YAML separator and table border patterns
…lors in agent chat

Code blocks in the agent chat used hardcoded `grey[100]`/`grey[900]`
regardless of theme mode, and success icons used hardcoded `#4caf50`.
Both break contrast in dark mode. This PR makes these colors theme-aware,
fixes the user message bubble to use the real
`theme.palette.sidebar.selectedBackground` instead of a hardcoded color,
and adds Storybook stories (co-located next to their components) to
verify contrast visually across light and dark themes.

- **ContentRenderer.tsx**: Swap `grey[100]`↔`grey[900]` for code block bg/text based on `theme.palette.mode`
- **AgentThinkingSteps.tsx**: Replace 3× hardcoded `#4caf50` with `theme.palette.success.main`
- **ChatContrast stories**: Use `theme.palette.sidebar.selectedBackground` (with fallback) for user message bubble instead of hardcoded `#c2c2c2`, matching production `textstream.tsx` behavior
- **Story file co-location**: Stories are placed next to their components (`ContentRenderer.stories.tsx` next to `ContentRenderer.tsx`, `ChatContrast.stories.tsx` next to `textstream.tsx`, `AgentThinkingSteps.stories.tsx` next to `AgentThinkingSteps.tsx`)
- **Edge case stories added**:
  - ContentRenderer: empty content, very long content, nested code blocks, YAML blocks, inline code heavy, multiple code block languages
  - ChatContrast: short messages, error/success sequences, long unbroken strings, deeply nested markdown, YAML + multi-language code blocks
  - AgentThinkingSteps: single step, many steps (15+), long step labels, all-pending state
Add levelled debug logging, robust code-line detection,
real-world parsing tests and stories, normalize terminal-formatted
responses, collapse terminal blank lines, fix code block boundary
detection, fix GFM table/YAML rendering, wrap bare code command.
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 13, 2026

@ashu8912 Added a loader to when the "Agent is working..."

Description

Adds a small MUI CircularProgress spinner to the right of the "Agent working…" text in AgentThinkingSteps, providing clearer visual feedback that the agent is actively processing.

Changes Made

  • Added CircularProgress (12px, thickness 5) conditionally rendered when isRunning is true, positioned to the right of "Agent working…" via existing flex layout
  • Added aria-label="Agent processing" for screen reader accessibility

Screenshots/Videos

image

- dev script now runs ai-assistant:start alongside plugin:start via concurrently
- Added ai-assistant:{install,build,start,test,lint,format} convenience scripts mirroring the plugin:* scripts
- Updated install:all and build:all to include ai-assistant
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 15, 2026

@ashu8912 fixed npm run dev to start ai-assistant too.

npm run dev only started the aks-desktop plugin. The ai-assistant plugin was missing from the dev workflow, requiring manual startup.

  • dev script now runs ai-assistant:start alongside plugin:start via concurrently
  • Added ai-assistant:{install,build,start,test,lint,format} convenience scripts mirroring the plugin:* scripts
  • Updated install:all and build:all to include ai-assistant

…ndicator

**`AgentThinkingSteps.tsx`** (~20 net lines)
- Added `endRef` sentinel `<div>` at the bottom of the component content (used as DOM anchor for scroll parent lookup) and `scrollParentRef` to cache the nearest scrollable ancestor
- Scroll parent lookup uses `getComputedStyle(sp).overflowY` to find containers with `'auto'`/`'scroll'` — correctly skips MUI Collapse wrappers which use `overflow: hidden`
- Added `useEffect` that calls `scrollParent.scrollTo()` directly when `steps.length` changes — fires after a 350ms delay to let MUI Collapse animations settle, with cleanup to clear the timer on unmount/re-render
- Uses direct `scrollParent.scrollTo()` instead of `scrollIntoView()` which was targeting the wrong container through nested `overflow: hidden` MUI Collapse wrappers
- Gated by a `distanceFromBottom` check on the scroll parent (matching TextStreamContainer's "near bottom" pattern), skipping if the user has scrolled more than one viewport-height away from the bottom
- Respects `prefers-reduced-motion` via inline `window.matchMedia` check with SSR guard (`typeof window !== 'undefined' && typeof window.matchMedia === 'function'`), using `behavior: 'auto'` instead of `'smooth'` when reduced motion is preferred

**`AgentThinkingSteps.stories.tsx`**
- Added `SmartScroll` story with a 300px scrollable container, filler messages, a user question, and progressive thinking steps to demonstrate the scroll behavior when the user is near the bottom
- Added `ScrolledAway` story with a 200px container starting scrolled to the top, demonstrating that new steps do NOT yank the viewport back down
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 15, 2026

@ashu8912 fixed "Agent working…" not scrolling into viewport issue.

Description

When "Agent working…" appears, the viewport scrolled to the very bottom, hiding the user's question. Now AgentThinkingSteps uses direct scrollParent.scrollTo() to keep the expanding thinking steps visible as new steps arrive — but only when the user is near the bottom of the scroll container. If the user has intentionally scrolled away, the scroll is skipped to avoid yanking the viewport. The scroll fires after a 350ms delay to let MUI Collapse animations settle before measuring positions. It also respects prefers-reduced-motion for accessibility. This complements the existing scrollToBottom in textstream.tsx that fires when the user sends a message.

The scroll parent is found by checking getComputedStyle(sp).overflowY for 'auto'/'scroll', which correctly skips MUI Collapse wrappers (overflow: hidden) that previously caused scrollIntoView() to target the wrong container.

Changes Made

AgentThinkingSteps.tsx (~20 net lines)

  • Added endRef sentinel <div> at the bottom of the component content (used as DOM anchor for scroll parent lookup) and scrollParentRef to cache the nearest scrollable ancestor
  • Scroll parent lookup uses getComputedStyle(sp).overflowY to find containers with 'auto'/'scroll' — correctly skips MUI Collapse wrappers which use overflow: hidden
  • Added useEffect that calls scrollParent.scrollTo() directly when steps.length changes — fires after a 350ms delay to let MUI Collapse animations settle, with cleanup to clear the timer on unmount/re-render
  • Uses direct scrollParent.scrollTo() instead of scrollIntoView() which was targeting the wrong container through nested overflow: hidden MUI Collapse wrappers
  • Gated by a distanceFromBottom check on the scroll parent (matching TextStreamContainer's "near bottom" pattern), skipping if the user has scrolled more than one viewport-height away from the bottom
  • Respects prefers-reduced-motion via inline window.matchMedia check with SSR guard (typeof window !== 'undefined' && typeof window.matchMedia === 'function'), using behavior: 'auto' instead of 'smooth' when reduced motion is preferred

AgentThinkingSteps.stories.tsx

  • Added SmartScroll story with a 300px scrollable container, filler messages, a user question, and progressive thinking steps to demonstrate the scroll behavior when the user is near the bottom
  • Added ScrolledAway story with a 200px container starting scrolled to the top, demonstrating that new steps do NOT yank the viewport back down

Screenshots/Videos

SmartScroll with Collapse wrappers — scroll parent correctly found through nested overflow: hidden wrappers, all steps visible:

SmartScroll story — user question + "Agent working…" + all steps visible in viewport:

ScrolledAway story — user scrolled to top, steps arrive but viewport is NOT yanked back:

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 15, 2026

@ashu8912 Renamed the "alpha" label to "preview" in the AI Assistant header, and adds a disclaimer warning icon next to the tidy-up (broom) button.

Two small UI polish changes to the AI Assistant panel:

  • Header label: AI Assistant (alpha) → AI Assistant (preview)
  • Disclaimer warning icon button added next to the Clear History (broom) button; hovering shows a tooltip: "AI responses may be inaccurate or incomplete. Always review and verify suggestions before applying them, especially in production environments."
image image

Comment thread plugins/ai-assistant/src/components/assistant/AIInputSection.tsx Fixed
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 15, 2026

@ashu8912 Improved the AI response parsing.

It's not perfect, but now renders many code blocks better.

After parser improvements:

image

Before parser improvements:

See how there's errors with the python code, the requirements.txt code? It's like this for most code blocks before.
image

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 16, 2026

@ashu8912 added i18n t() all over the place

ai-assistant: i18n: Add t() translation for hardcoded strings

All user-visible hardcoded strings in the ai-assistant plugin
are now wrapped with t() using useTranslation enabling full
i18n support. All translation keys use {{placeholder}}
interpolation instead of string concatenation,
ensuring correct grammar and word order across locales.

@illume illume force-pushed the ai-assistant-addition branch from 57f5780 to e679e36 Compare March 16, 2026 02:23
illume added 2 commits March 16, 2026 03:27
All user-visible hardcoded strings in the ai-assistant plugin
are now wrapped with t() using useTranslation enabling full
i18n support. All translation keys use {{placeholder}}
interpolation instead of string concatenation,
ensuring correct grammar and word order across locales.
Comment thread plugins/ai-assistant/src/components/assistant/AIInputSection.tsx Dismissed
Comment thread plugins/ai-assistant/src/components/common/LogsDialog.tsx Dismissed
@illume illume force-pushed the ai-assistant-addition branch from e679e36 to 64aa8b4 Compare March 16, 2026 02:29
@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 16, 2026

@ashu8912 also upgraded to headlamp-plugin to 0.13.1, because that has fixed i18n support.

@illume
Copy link
Copy Markdown
Collaborator

illume commented Mar 16, 2026

Added files so the localization team can update the translations for the ai-assistant

@ashu8912 ashu8912 marked this pull request as ready for review March 16, 2026 09:18
Copilot AI review requested due to automatic review settings March 16, 2026 09:18
@ashu8912
Copy link
Copy Markdown
Member Author

Hii @illume the changes look good to me, i think we can merge this and continue the development from the base we have created here, in future we will have to think about using the upstream ai-assistant changes here, as right now it's just a copy of the upstream plugin here, but things will get more smoother once we have ag-ui support in aks agent.

Thanks

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 95 out of 101 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/ai-assistant/src/hooks/useClusterWarnings.ts
Comment thread plugins/ai-assistant/src/utils/ProviderConfigManager.ts
Comment thread plugins/ai-assistant/src/agent/debugLog.ts
Comment thread plugins/ai-assistant/src/components/assistant/AIChatContent.tsx
Comment thread plugins/ai-assistant/src/langchain/tools/ToolBase.ts
Comment thread plugins/ai-assistant/src/storybook.test.tsx
Comment thread plugins/ai-assistant/src/langchain/tools/ToolManager.ts
Comment thread plugins/ai-assistant/README.md
Comment thread plugins/README.md
Comment thread plugins/ai-assistant/src/helper/index.tsx
Copy link
Copy Markdown
Collaborator

@illume illume left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 thanks!

@illume illume merged commit e921064 into main Mar 16, 2026
22 checks passed
@illume illume deleted the ai-assistant-addition branch March 16, 2026 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p0 Highest priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants