Skip to content

Router accepts mismatched subagent model tags and strips user text #430

Description

@MicroMilo

Summary

The router subagent tag parser accepts malformed tags whose opening and closing tag families do not match. For example, <ccr-subagent-model>...</pilotdeck-subagent-model> is currently treated as a valid control tag.

This can silently turn ordinary user-provided text into router control input: detectSubagent marks the request as a subagent request and extracts a model hint, while stripSubagentTagFromMessages removes the malformed text from the user message.

Code path

  • src/router/scenario/subagentDetector.ts:3 defines SUBAGENT_TAG_PATTERN with independent alternations for the opening and closing tag families.
  • src/router/scenario/subagentDetector.ts:17 uses the regex in detectSubagent and sets taggedInUserMessage, modelHint, and isSubagent.
  • src/router/scenario/subagentDetector.ts:53 uses the same regex in stripSubagentTagFromMessages and removes the matched text.
  • src/router/scenario/decideScenario.ts:26 consumes the detection result for router scenario selection.
  • src/router/RouterRuntime.ts:510 enables subagent tag stripping when the decision is considered a subagent request.

Steps to reproduce

Tested against local HEAD dbb2b416.

From the repository root:

node --import tsx --input-type=module <<'NODE'
import {
  detectSubagent,
  stripSubagentTagFromMessages,
} from './src/router/scenario/subagentDetector.ts';

const messages = [
  {
    role: 'user',
    content: [
      {
        type: 'text',
        text: '<ccr-subagent-model>openai/gpt-4o</pilotdeck-subagent-model> explain this markup',
      },
    ],
  },
];

const tools = [{ name: 'agent', inputSchema: {} }];

console.log(JSON.stringify(detectSubagent(messages, tools, true)));
console.log(JSON.stringify(stripSubagentTagFromMessages(messages)[0].content[0].text));
NODE

Current output:

{"isSubagent":true,"modelHint":"openai/gpt-4o","missingAgentTool":false,"taggedInUserMessage":true}
" explain this markup"

Expected behavior

Only same-family tag pairs should be accepted as router control tags, for example:

<pilotdeck-subagent-model>...</pilotdeck-subagent-model>
<ccr-subagent-model>...</ccr-subagent-model>

A cross-family pair should be ignored by detectSubagent and preserved by stripSubagentTagFromMessages:

<ccr-subagent-model>...</pilotdeck-subagent-model>

Actual behavior

The cross-family pair is accepted as valid. This causes:

  • detectSubagent to return isSubagent: true and taggedInUserMessage: true for malformed user text;
  • modelHint to be extracted from the malformed tag body;
  • stripSubagentTagFromMessages to delete text that was not a valid control tag.

Existing coverage

I searched existing issues for subagentDetector, subagent-model, cross-family, and malformed subagent. The only nearby matches were subagent configuration issues (#247 and #248), which do not cover this tag parser behavior.

Suggested fix

Bind the closing tag family to the opening tag family, for example with a backreference:

const SUBAGENT_TAG_PATTERN =
  /<(pilotdeck|ccr)-subagent-model>([\s\S]+?)<\/\1-subagent-model>/i;

With that shape, the model hint capture would move from match[1] to match[2].

Suggested tests

  • same-family pilotdeck tags are detected and stripped;
  • same-family ccr tags are detected and stripped;
  • cross-family tags are not detected as subagent control tags;
  • cross-family tags are preserved by stripSubagentTagFromMessages;
  • ordinary user text containing malformed XML-like snippets is not silently removed.

Submitted with Codex.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions