fix(web): isolate rectification answers from tool-step planning text
Mastra intermediate text-delta was published as answer.delta, then set-focus domain errors reset the attempt and replayed evidence. Publish only the terminal no-tool step, persist the next probe on the server, and ground batch quotes in the source turn. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
applyStepAnswerChunk,
|
||||
createStepAnswerState,
|
||||
flushStepAnswerOnStreamFinish,
|
||||
shouldPublishStepText,
|
||||
} from "../src/lib/rectification-agentic/v9/step-answer.ts";
|
||||
import { mapStreamChunkToPhase, mapStreamChunkToThinking } from "../src/lib/rectification-agentic/v9/stream-mapping.ts";
|
||||
import { PUBLIC_RECTIFICATION_TOOLS } from "../src/lib/rectification-agentic/v9/public-receipt.ts";
|
||||
|
||||
function isPublicTool(name: string): boolean {
|
||||
return (PUBLIC_RECTIFICATION_TOOLS as readonly string[]).includes(name);
|
||||
}
|
||||
|
||||
function chunk(type: string, payload?: Record<string, unknown>) {
|
||||
return { type, payload };
|
||||
}
|
||||
|
||||
test("does not publish intermediate tool-step text as answer.delta", () => {
|
||||
const state = createStepAnswerState();
|
||||
applyStepAnswerChunk(state, chunk("text-delta", { text: "Let me set" }), isPublicTool);
|
||||
applyStepAnswerChunk(state, chunk("tool-call", { toolName: "rectification-record-evidence-batch" }), isPublicTool);
|
||||
const afterTool = applyStepAnswerChunk(
|
||||
state,
|
||||
chunk("tool-result", { toolName: "rectification-record-evidence-batch" }),
|
||||
isPublicTool,
|
||||
);
|
||||
assert.equal(afterTool.kind, "discard");
|
||||
applyStepAnswerChunk(state, chunk("text-delta", { text: "记下了,2020年4月开始实习。" }), isPublicTool);
|
||||
const finish = applyStepAnswerChunk(state, chunk("step-finish", { reason: "stop" }), isPublicTool);
|
||||
assert.deepEqual(finish, { kind: "publish", pieces: ["记下了,2020年4月开始实习。"] });
|
||||
});
|
||||
|
||||
test("never publishes reasoning-delta to the browser", () => {
|
||||
assert.equal(mapStreamChunkToPhase(chunk("reasoning-delta", { text: "Let me" }) as never), null);
|
||||
assert.equal(mapStreamChunkToPhase(chunk("text-delta", { text: "Let me" }) as never), null);
|
||||
assert.ok(mapStreamChunkToThinking(chunk("reasoning-delta", { text: "先核对经历。" }) as never));
|
||||
});
|
||||
|
||||
test("publishes only the terminal no-tool step as assistant text", () => {
|
||||
const state = createStepAnswerState();
|
||||
applyStepAnswerChunk(state, chunk("step-start"), isPublicTool);
|
||||
applyStepAnswerChunk(state, chunk("text-delta", { text: "_probe" }), isPublicTool);
|
||||
applyStepAnswerChunk(state, chunk("tool-call", { toolName: "rectification-compare-candidates" }), isPublicTool);
|
||||
applyStepAnswerChunk(state, chunk("step-finish", { stepResult: { reason: "tool-calls" } }), isPublicTool);
|
||||
assert.equal(shouldPublishStepText({ calledTool: true, text: "_probe" }, "tool-calls"), false);
|
||||
applyStepAnswerChunk(state, chunk("step-start"), isPublicTool);
|
||||
applyStepAnswerChunk(state, chunk("text-delta", { text: "已经记下实习。" }), isPublicTool);
|
||||
const finish = flushStepAnswerOnStreamFinish(state, "stop");
|
||||
assert.deepEqual(finish, { kind: "publish", pieces: ["已经记下实习。"] });
|
||||
});
|
||||
Reference in New Issue
Block a user