Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/addie-validation-id-diagnostics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
---

Surface validation result IDs in Addie's failed-validation diagnostics and
render sanitized validation strings without extra quote wrapping.
20 changes: 18 additions & 2 deletions server/src/addie/mcp/conformance-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const MAX_VALIDATION_STRING_CHARS = 200;
type PublicValidationValue = string | number | boolean | null | '[undefined]' | '[redacted]';

interface PublicValidationResult {
id?: string;
check: string;
passed: false;
description: string;
Expand Down Expand Up @@ -143,7 +144,21 @@ function optionalSanitizedString(value: unknown): string | undefined {
function safeAgentText(value: unknown): string | undefined {
if (typeof value !== 'string') return undefined;
const sanitized = cleanValidationText(value, 160);
return sanitized === '[redacted]' ? sanitized : `"${sanitized}"`;
return sanitized;
}

function safeValidationId(value: unknown): string | undefined {
if (typeof value !== 'string') return undefined;
const sanitized = value
.replace(/[\r\n`\u0000-\u001f\u007f\u0085\u2028\u2029]/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.slice(0, 160);
if (!sanitized) return undefined;
if (SENSITIVE_VALUE_PATTERN.test(sanitized) || PROMPT_INJECTION_PATTERN.test(sanitized)) {
return '[redacted]';
}
return sanitized;
}

function hasOwn(value: object, key: string): boolean {
Expand All @@ -153,6 +168,7 @@ function hasOwn(value: object, key: string): boolean {
function compactValidationForOutput(validation: ValidationResult): PublicValidationResult {
const remediation = optionalSanitizedString(validation.remediation);
return {
...(hasOwn(validation, 'id') && { id: safeValidationId(validation.id) ?? '[redacted]' }),
check: safeAgentText(validation.check) ?? 'validation',
passed: false,
description: safeAgentText(validation.description) ?? 'Validation failed',
Expand Down Expand Up @@ -254,7 +270,7 @@ export const CONFORMANCE_TOOLS: AddieTool[] = [
{
name: 'run_conformance_against_my_agent',
description:
'Run a compliance storyboard against the adopter MCP server connected to this Addie session via Socket Mode. The adopter must have started `@adcp/sdk/server` ConformanceClient with a token issued in this same chat session — the channel routes by WorkOS organization id. Returns a markdown report with phase/step pass/fail/skipped status and trimmed error text on failures. Use this after the user confirms their conformance client shows `status=connected`.',
'Run a compliance storyboard against the adopter MCP server connected to this Addie session via Socket Mode. The adopter must have started `@adcp/sdk/server` ConformanceClient with a token issued in this same chat session — the channel routes by WorkOS organization id. Returns a markdown report with phase/step pass/fail/skipped status, trimmed error text, and sanitized failed-validation details such as id, expected, and actual on failures. Use this after the user confirms their conformance client shows `status=connected`.',
usage_hints:
'use for "run conformance on my agent", "test my agent against media-buy storyboards", "check my creative agent compliance". Requires a live conformance connection — if there isn\'t one, the tool returns a hint pointing the user at issue_conformance_token first.',
input_schema: {
Expand Down
7 changes: 7 additions & 0 deletions server/tests/unit/conformance-addie-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ describe('run_conformance_against_my_agent Addie tool', () => {
description: 'Response matches schema',
},
{
id: 'check_extend_flight_mode',
check: 'field_value',
passed: false,
path: 'products[0].allowed_actions[1].mode',
Expand All @@ -224,6 +225,7 @@ describe('run_conformance_against_my_agent Addie tool', () => {
response: { payload: { secret_response: 'do-not-print' } },
},
{
id: 'authorization_header_missing',
check: 'field_value',
passed: false,
path: 'errors[0].details',
Expand All @@ -232,6 +234,7 @@ describe('run_conformance_against_my_agent Addie tool', () => {
description: 'Structured error details are present',
},
{
id: 'sk_live_1234567890abcdefghijkl',
check: 'field_value',
passed: false,
path: 'errors[0].message',
Expand All @@ -254,6 +257,10 @@ describe('run_conformance_against_my_agent Addie tool', () => {
expect(out).toMatch(/FAILED/);
expect(out).toMatch(/expected status 200, got 500/);
expect(out).toMatch(/failed validations/);
expect(out).toMatch(/"id": "check_extend_flight_mode"/);
expect(out).toMatch(/"id": "authorization_header_missing"/);
expect(out).toMatch(/"id": "\[redacted\]"/);
expect(out).not.toMatch(/\\"check_extend_flight_mode\\"/);
expect(out).toMatch(/products\[0\]\.allowed_actions\[1\]\.mode/);
expect(out).toMatch(/requires_approval/);
expect(out).toMatch(/\[undefined\]/);
Expand Down
Loading