Skip to content

Add unfencing operation for markdown text elements in safe outputs#13718

Merged
pelikhan merged 3 commits intomainfrom
copilot/add-unfencing-operation-markdown
Feb 4, 2026
Merged

Add unfencing operation for markdown text elements in safe outputs#13718
pelikhan merged 3 commits intomainfrom
copilot/add-unfencing-operation-markdown

Conversation

Copy link
Contributor

Copilot AI commented Feb 4, 2026

Agents occasionally wrap entire markdown responses in code fences (\``markdown\n...\n````), causing the fence delimiters to appear in collected safe outputs. This adds detection and removal of outer fences when the entire text element is wrapped.

Implementation

Go (pkg/workflow/markdown_unfencing.go)

  • UnfenceMarkdown() detects and strips outer fences starting with markdown/md language tags
  • Supports both backtick and tilde fences of any length (3+)
  • Validates fence length matching: closing fence must have at least as many characters as opening fence
  • Case-insensitive language tag matching

JavaScript (actions/setup/js/markdown_unfencing.cjs)

  • Runtime equivalent for log parsing
  • Integrated into log_parser_shared.cjs at all text content extraction points
  • Integrated into safe_output_type_validator.cjs for all sanitized markdown fields
  • Applied during safe output collection from agent responses

Behavior

// Before: Agent output wrapped in fence
const text = "```markdown\n# Actual Content\n```";

// After: Unwrapped automatically
unfenceMarkdown(text); // Returns: "# Actual Content"

// Non-wrapped content passes through unchanged
unfenceMarkdown("# Content"); // Returns: "# Content"

Detection criteria:

  • First line must be \``(markdown|md)or~~~(markdown|md)` (case-insensitive)
  • Last line must be matching fence type (\``or~~~`) with length >= opening fence length
  • Only removes outermost fence; nested code blocks preserved

Testing

  • 89 comprehensive tests (45 Go + 44 JavaScript)
  • Fence length matching tests (3, 4, 5, 10+ character fences)
  • Real-world agent response scenarios
  • Edge cases: empty content, whitespace, nested blocks, mismatched fence types and lengths
Original prompt

Add a unfencing operation to top level mardown text elements when the agent accidentally wrapped the entire markdown body in a code fence.

See #12235

  • separate helper file
  • extensive testing
  • must starting with markdown/md fence
  • must finish with fence

Applied to any markdown body when ,
Collecting safe outputs.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

- Created helper file pkg/workflow/markdown_unfencing.go with UnfenceMarkdown function
- Added comprehensive Go tests in pkg/workflow/markdown_unfencing_test.go (26 test cases)
- Created JavaScript implementation actions/setup/js/markdown_unfencing.cjs
- Added comprehensive JavaScript tests in markdown_unfencing.test.cjs (31 test cases)
- Integrated unfencing into log_parser_shared.cjs to handle text elements
- All tests passing and code formatted

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add unfencing operation for top-level markdown text elements Add unfencing operation for markdown text elements in safe outputs Feb 4, 2026
Copilot AI requested a review from pelikhan February 4, 2026 13:23
@pelikhan
Copy link
Contributor

pelikhan commented Feb 4, 2026

@copilot

  • support more than 3 backticks (start end must match)
  • apply to all safe output markdown strings in collector
  • add more tests

- Integrated unfenceMarkdown into safe_output_type_validator.cjs for all markdown fields
- Added 13 new Go test cases for fence length matching and real-world examples
- Added 13 new JavaScript test cases for fence length matching and real-world examples
- Total tests: 45 Go tests, 44 JavaScript tests (89 total)
- All tests passing

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 4, 2026

@copilot

  • support more than 3 backticks (start end must match)...

Done in commit 4df0437:

More than 3 backticks support - Already implemented and now extensively tested. The implementation dynamically counts fence characters and validates that the closing fence has at least as many characters as the opening (e.g., 4 backticks must close with 4+, but 4 backticks won't match 3 backtick closing).

Applied to all safe output markdown strings - Added unfenceMarkdown to safe_output_type_validator.cjs at line 347-348, which processes all sanitized string fields in safe outputs before sanitization. This complements the existing integration in log_parser_shared.cjs.

Added more tests - Added 26 new test cases:

  • 13 new Go tests (fence length matching + real-world examples)
  • 13 new JavaScript tests (fence length matching + real-world examples)
  • Total: 89 tests (45 Go + 44 JS) covering fence lengths from 3 to 10+ characters

All tests passing ✓

@pelikhan pelikhan marked this pull request as ready for review February 4, 2026 13:39
Copilot AI review requested due to automatic review settings February 4, 2026 13:39
Copy link
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 pull request adds automatic detection and removal of outer markdown code fences from agent responses to address cases where agents accidentally wrap entire markdown content in code fence delimiters (```markdown\n...\n```).

Changes:

  • Implements UnfenceMarkdown() function in both Go and JavaScript with identical behavior
  • Integrates unfencing into safe output validation before sanitization
  • Integrates unfencing into log parsing at all text content extraction points

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/workflow/markdown_unfencing.go Go implementation with fence detection, validation, and removal logic
pkg/workflow/markdown_unfencing_test.go Comprehensive Go test suite with 45 test cases covering edge cases and real-world scenarios
actions/setup/js/markdown_unfencing.cjs JavaScript implementation equivalent to Go version
actions/setup/js/markdown_unfencing.test.cjs JavaScript test suite with 44 test cases mirroring Go tests
actions/setup/js/safe_output_type_validator.cjs Integrates unfencing before sanitization for markdown fields
actions/setup/js/log_parser_shared.cjs Integrates unfencing at three text extraction points in log parsing

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

@@ -0,0 +1,246 @@
import { describe, it, expect } from "vitest";
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The beforeEach function is used but not imported from 'vitest'. This will cause a runtime error when the tests are executed. The import statement on line 1 should include beforeEach in the destructured imports.

Suggested change
import { describe, it, expect } from "vitest";
import { describe, it, expect, beforeEach } from "vitest";

Copilot uses AI. Check for mistakes.
Comment on lines +348 to 353
// Apply unfencing to remove accidental outer markdown fences before sanitization
let processedValue = unfenceMarkdown(value);
const sanitized = sanitizeContent(processedValue, {
maxLength: validation.maxLength || MAX_BODY_LENGTH,
allowedAliases: options?.allowedAliases || [],
});
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

While the unfenceMarkdown function itself has comprehensive test coverage, there are no integration tests verifying that it's correctly applied before sanitization in the safe output validation flow. Consider adding a test case that validates an item with a markdown field wrapped in fences to ensure the integration works as expected.

Copilot uses AI. Check for mistakes.
@pelikhan pelikhan merged commit d07394e into main Feb 4, 2026
131 of 132 checks passed
@pelikhan pelikhan deleted the copilot/add-unfencing-operation-markdown branch February 4, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants