mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(sse): handle space-separated arg name/value in Composer tool calls (port from 9router#1811) (#7116)
parseInnerCall only split arg segments on a newline between the arg name and its value. Cursor's live Composer/Auto output has been observed using a single space instead, so those segments were treated as one long (space-containing) arg name with an empty value, silently no-opping Write/tool calls for Composer/Auto models. Fall back to splitting on the first whitespace boundary when no newline is present in the segment. Reported-by: way-art (https://github.com/decolua/9router/issues/1811)
This commit is contained in:
committed by
GitHub
parent
a62141210b
commit
0130a4bbb2
1
changelog.d/fixes/1811-composer-space-sep.md
Normal file
1
changelog.d/fixes/1811-composer-space-sep.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** Cursor Composer/Auto tool calls that separate the arg name and value with a space instead of a newline (e.g. `path /Users/.../test`) no longer produce empty-valued, malformed argument keys, fixing silent no-op Write/tool calls. (thanks @way-art)
|
||||
@@ -126,15 +126,28 @@ function parseInnerCall(body: string): { name: string; arguments: string } | nul
|
||||
const args: Record<string, unknown> = {};
|
||||
for (const seg of segments) {
|
||||
if (!seg) continue;
|
||||
// Each segment is `arg_name\nvalue\n...`. The arg name is the first
|
||||
// line; everything after the first newline is the value (verbatim,
|
||||
// including additional newlines).
|
||||
// Each segment is normally `arg_name\nvalue\n...`: the arg name is the
|
||||
// first line, everything after the first newline is the value
|
||||
// (verbatim, including additional newlines). Some live Composer/Auto
|
||||
// captures instead separate the arg name and value with a single space
|
||||
// on the same line (no newline at all in the segment) — fall back to
|
||||
// splitting on the first whitespace boundary in that case so the value
|
||||
// isn't swallowed into an empty-valued, space-containing "arg name".
|
||||
const idxNl = seg.indexOf("\n");
|
||||
let argName: string;
|
||||
let argValue: string;
|
||||
if (idxNl < 0) {
|
||||
argName = seg.trim();
|
||||
argValue = "";
|
||||
const idxSp = seg.search(/\s/);
|
||||
if (idxSp < 0) {
|
||||
argName = seg.trim();
|
||||
argValue = "";
|
||||
} else {
|
||||
argName = seg.slice(0, idxSp).trim();
|
||||
// Unlike the newline-delimited form, a space-delimited value has no
|
||||
// multi-line content to preserve — trim the trailing whitespace left
|
||||
// over from the boundary with the next `<|tool▁sep|>` marker.
|
||||
argValue = seg.slice(idxSp + 1).trim();
|
||||
}
|
||||
} else {
|
||||
argName = seg.slice(0, idxNl).trim();
|
||||
argValue = seg.slice(idxNl + 1);
|
||||
|
||||
@@ -214,3 +214,27 @@ test("feedStreamingChunk: noop after done state", () => {
|
||||
assert.equal(out.safeDelta, "");
|
||||
assert.equal(out.ready, false);
|
||||
});
|
||||
|
||||
// ─── Regression: space-separated arg name/value (9router#1811) ───────────────
|
||||
// Cursor's real Composer/Auto output has been observed using a single space
|
||||
// (instead of a newline) between the arg name and its value inside a
|
||||
// <|tool▁sep|> segment, e.g. "<|tool▁sep|>path /Users/.../test". The parser
|
||||
// must still extract {path: "/Users/.../test"} rather than treating the whole
|
||||
// segment as the (empty-valued) arg name.
|
||||
test("parseComposerToolCalls: parses args separated by a space instead of a newline (Cursor Composer live capture)", () => {
|
||||
const text =
|
||||
"<|tool▁calls▁begin|><|tool▁call▁begin|> Write " +
|
||||
"<|tool▁sep|>path /Users/kabawagang/Desktop/Code/iOS_Review/test " +
|
||||
"<|tool▁sep|>contents 22\n\n<|tool▁call▁end|><|tool▁calls▁end|>";
|
||||
|
||||
const result = parseComposerToolCalls(text);
|
||||
|
||||
assert.equal(result.toolCalls.length, 1);
|
||||
const tc = result.toolCalls[0];
|
||||
assert.equal(tc.function.name, "Write");
|
||||
const args = JSON.parse(tc.function.arguments);
|
||||
assert.deepEqual(args, {
|
||||
path: "/Users/kabawagang/Desktop/Code/iOS_Review/test",
|
||||
contents: 22,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user