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.
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:
detectSubagentmarks the request as a subagent request and extracts a model hint, whilestripSubagentTagFromMessagesremoves the malformed text from the user message.Code path
src/router/scenario/subagentDetector.ts:3definesSUBAGENT_TAG_PATTERNwith independent alternations for the opening and closing tag families.src/router/scenario/subagentDetector.ts:17uses the regex indetectSubagentand setstaggedInUserMessage,modelHint, andisSubagent.src/router/scenario/subagentDetector.ts:53uses the same regex instripSubagentTagFromMessagesand removes the matched text.src/router/scenario/decideScenario.ts:26consumes the detection result for router scenario selection.src/router/RouterRuntime.ts:510enables subagent tag stripping when the decision is considered a subagent request.Steps to reproduce
Tested against local HEAD
dbb2b416.From the repository root:
Current output:
Expected behavior
Only same-family tag pairs should be accepted as router control tags, for example:
A cross-family pair should be ignored by
detectSubagentand preserved bystripSubagentTagFromMessages:Actual behavior
The cross-family pair is accepted as valid. This causes:
detectSubagentto returnisSubagent: trueandtaggedInUserMessage: truefor malformed user text;modelHintto be extracted from the malformed tag body;stripSubagentTagFromMessagesto delete text that was not a valid control tag.Existing coverage
I searched existing issues for
subagentDetector,subagent-model,cross-family, andmalformed 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:
With that shape, the model hint capture would move from
match[1]tomatch[2].Suggested tests
pilotdecktags are detected and stripped;ccrtags are detected and stripped;stripSubagentTagFromMessages;Submitted with Codex.