Short summary
Tool calls leak as plain text (stray prefix "course"/"call") instead of executing when narrative text precedes the call
Affected version or release
GitHub Copilot app v1.0.10 (shared engine @github/copilot 1.0.65) · Windows · model: Claude Opus
What happened?
When an assistant turn begins with prose before a tool-call block, the call is rendered as plain text and never executes — sub-agents, shell, SQL and file edits silently don't run. The leaked block is consistently prefixed by a stray word that is the tail of the preceding prose word, e.g. course (from "Of course"), call (from "the call"), sure. When the tool call is the first content of the turn, it executes reliably.
This is a streaming/parse desync, not model output: the model emits a valid <function_calls> block; the client just fails to parse it once prose precedes it.
Root cause
The shipped engine is closed-source, so this is from the minified bundle @github/copilot/app.js. The text-based tool-call fallback is start-anchored:
let n = /^<functions><([^>]+)>([^>]+)</.exec(e.content); if (n) return [{toolName:n[1],input:n[2],id:""}]
^ requires the tag at offset 0. All 5 <functions> parse sites are anchored — there is no anywhere-scan. Text deltas are flushed per chunk before any tool scan:
this.streamingMessageText += n, this.session.emitEphemeral("assistant.message_delta", {messageId:this.currentMessageId, deltaContent:n})
So prose+markup in one content string is never re-scanned → leaks. The stray prefix is just the chunk-boundary tail of the last prose word.
Steps to reproduce
- Have the model start a turn with prose then a tool call (e.g.
Of course\n<function_calls>...).
- Observe the markup printed as text; tool does not run.
Deterministic, parser-isolated repro (exact regex from bundle):
const p = c => /^<functions><([^>]+)>([^>]+)</.exec(c) ? 'EXECUTES' : 'LEAKS';
['Of course','make the call','sure'].forEach(s => console.log(p(s+'\n<functions><t>x</t></functions>'))); // 3x LEAKS
console.log(p('<functions><t>x</t></functions>')); // EXECUTES
Expected behavior
Parse the tool-call sentinel at any offset, buffering across stream chunks, so narrative-before-toolcall executes the same as toolcall-first. Fix: drop the ^ anchor / scan-anywhere.
Additional context
Related: github/copilot-cli#3765 (same symptom on CLI; shared engine).
Short summary
Tool calls leak as plain text (stray prefix "course"/"call") instead of executing when narrative text precedes the call
Affected version or release
GitHub Copilot app v1.0.10 (shared engine
@github/copilot1.0.65) · Windows · model: Claude OpusWhat happened?
When an assistant turn begins with prose before a tool-call block, the call is rendered as plain text and never executes — sub-agents, shell, SQL and file edits silently don't run. The leaked block is consistently prefixed by a stray word that is the tail of the preceding prose word, e.g.
course(from "Of course"),call(from "the call"),sure. When the tool call is the first content of the turn, it executes reliably.This is a streaming/parse desync, not model output: the model emits a valid
<function_calls>block; the client just fails to parse it once prose precedes it.Root cause
The shipped engine is closed-source, so this is from the minified bundle
@github/copilot/app.js. The text-based tool-call fallback is start-anchored:^requires the tag at offset 0. All 5<functions>parse sites are anchored — there is no anywhere-scan. Text deltas are flushed per chunk before any tool scan:So prose+markup in one content string is never re-scanned → leaks. The stray prefix is just the chunk-boundary tail of the last prose word.
Steps to reproduce
Of course\n<function_calls>...).Deterministic, parser-isolated repro (exact regex from bundle):
Expected behavior
Parse the tool-call sentinel at any offset, buffering across stream chunks, so narrative-before-toolcall executes the same as toolcall-first. Fix: drop the
^anchor / scan-anywhere.Additional context
Related: github/copilot-cli#3765 (same symptom on CLI; shared engine).