Tighten Claude partial tool input parsing
This commit is contained in:
@@ -441,7 +441,7 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("routes input_json_delta through partial parsing before buffered JSON is complete", async () => {
|
test("waits for complete JSON values before updating tool input from input_json_delta", async () => {
|
||||||
const session = await createSession();
|
const session = await createSession();
|
||||||
const internal = session as unknown as {
|
const internal = session as unknown as {
|
||||||
mapPartialEvent: (event: Record<string, unknown>) => AgentTimelineItem[];
|
mapPartialEvent: (event: Record<string, unknown>) => AgentTimelineItem[];
|
||||||
@@ -479,7 +479,7 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
partial_json: '{"command":"echo ',
|
partial_json: '{"command":"echo ',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedCommand: "echo ",
|
expectedCommand: "echo seed",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
event: {
|
event: {
|
||||||
@@ -520,7 +520,7 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("surfaces canonical partial tool input from input_json_delta before JSON is complete", async () => {
|
test("does not surface incomplete string values from input_json_delta", async () => {
|
||||||
const session = await createSession();
|
const session = await createSession();
|
||||||
const internal = session as unknown as {
|
const internal = session as unknown as {
|
||||||
mapPartialEvent: (event: Record<string, unknown>) => AgentTimelineItem[];
|
mapPartialEvent: (event: Record<string, unknown>) => AgentTimelineItem[];
|
||||||
@@ -551,7 +551,6 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
|
|
||||||
expect(internal.toolUseCache.get(toolUseId)?.input).toEqual({
|
expect(internal.toolUseCache.get(toolUseId)?.input).toEqual({
|
||||||
file_path: "src/message.tsx",
|
file_path: "src/message.tsx",
|
||||||
old_string: "before",
|
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
await session.close();
|
await session.close();
|
||||||
|
|||||||
@@ -13,33 +13,25 @@ describe("parsePartialJsonObject", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps partial string values without field-specific logic", () => {
|
it("does not emit incomplete string values", () => {
|
||||||
expect(parsePartialJsonObject('{"command":"echo ')).toEqual({
|
expect(parsePartialJsonObject('{"command":"echo ')).toEqual({
|
||||||
value: {
|
value: {},
|
||||||
command: "echo ",
|
|
||||||
},
|
|
||||||
complete: false,
|
complete: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns parsed prefix fields from incomplete objects", () => {
|
it("returns only complete prefix fields from incomplete objects", () => {
|
||||||
expect(parsePartialJsonObject('{"file_path":"src/message.tsx","old_string":"before')).toEqual({
|
expect(parsePartialJsonObject('{"file_path":"src/message.tsx","old_string":"before')).toEqual({
|
||||||
value: {
|
value: {
|
||||||
file_path: "src/message.tsx",
|
file_path: "src/message.tsx",
|
||||||
old_string: "before",
|
|
||||||
},
|
},
|
||||||
complete: false,
|
complete: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("parses nested partial values generically", () => {
|
it("does not emit incomplete nested values", () => {
|
||||||
expect(parsePartialJsonObject('{"payload":{"path":"src/index.ts","content":"hello')).toEqual({
|
expect(parsePartialJsonObject('{"payload":{"path":"src/index.ts","content":"hello')).toEqual({
|
||||||
value: {
|
value: {},
|
||||||
payload: {
|
|
||||||
path: "src/index.ts",
|
|
||||||
content: "hello",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
complete: false,
|
complete: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -271,17 +271,17 @@ function parsePartialObject(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
value[parsedKey.value] = parsedMemberValue.value;
|
|
||||||
currentIndex = skipWhitespace(input, parsedMemberValue.nextIndex);
|
|
||||||
|
|
||||||
if (!parsedMemberValue.complete) {
|
if (!parsedMemberValue.complete) {
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
nextIndex: currentIndex,
|
nextIndex: skipWhitespace(input, parsedMemberValue.nextIndex),
|
||||||
complete: false,
|
complete: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value[parsedKey.value] = parsedMemberValue.value;
|
||||||
|
currentIndex = skipWhitespace(input, parsedMemberValue.nextIndex);
|
||||||
|
|
||||||
const delimiter = input[currentIndex];
|
const delimiter = input[currentIndex];
|
||||||
if (delimiter === ",") {
|
if (delimiter === ",") {
|
||||||
currentIndex += 1;
|
currentIndex += 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user