fix(consult): BUG-950~953 Pass 4 按句放行,无分钟按句丢弃

正文不再整段 hold:闭合句立刻过 Pass 4,无 reject 即发(950)。
无出生分钟改为按句丢弃,全丢才用兜底句(951);该模式日期记 observe(952)。
校正流 token 级 thinking 死链按 P2 删除,测试翻转成否定合同(953)。
This commit is contained in:
jesse-ux
2026-09-18 14:08:45 +08:00
parent 24a6fc9873
commit cd4775aec6
14 changed files with 407 additions and 171 deletions
@@ -1,4 +1,5 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import {
@@ -7,8 +8,7 @@ import {
flushStepAnswerOnStreamFinish,
shouldPublishStepText,
} from "../src/lib/rectification-agentic/v9/step-answer.ts";
import { mapStreamChunkToPhase, mapStreamChunkToThinking } from "../src/lib/rectification-agentic/v9/stream-mapping.ts";
import { createThinkingFragmentAssembler } from "../src/lib/think-step-gate.ts";
import { mapStreamChunkToPhase } 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 {
@@ -40,16 +40,29 @@ test("does not publish intermediate tool-step text as answer.delta", () => {
});
test("never publishes reasoning-delta to the browser", () => {
// 原值:mapStreamChunkToThinking 把中文 reasoning 变成 thinking.delta
// 新值:校正流对 reasoning-delta 必须丢弃;源码不得把它映射成对外事件
// 原因:BUG-953token 级 thinking 是死链且违反 P2provider reasoning 永不外发)
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));
const state = createStepAnswerState();
assert.equal(
applyStepAnswerChunk(state, chunk("reasoning-delta", { text: "先核对经历。" }), isPublicTool).kind,
"none",
);
const mapping = readFileSync(new URL("../src/lib/rectification-agentic/v9/stream-mapping.ts", import.meta.url), "utf8");
assert.doesNotMatch(mapping, /function mapStreamChunkToThinking|function toPublicThinkingDelta|InternalThinkingDeltaEvent/);
});
test("consecutive Chinese reasoning fragments assemble into a visible thinking line", () => {
const assembler = createThinkingFragmentAssembler();
assert.equal(assembler.push("The proposedKind value was rejected"), null);
assert.equal(assembler.push("先核"), null);
assert.equal(assembler.push("对经历。"), "先核对经历。");
// 原值:createThinkingFragmentAssembler 把「先核」+「对经历。」拼成可见思考行
// 新值:分片组装器删除;连续 reasoning-delta 仍全部丢弃
// 原因:BUG-953,翻转成否定合同,测试总数不降
const state = createStepAnswerState();
assert.equal(applyStepAnswerChunk(state, chunk("reasoning-delta", { text: "先核" }), isPublicTool).kind, "none");
assert.equal(applyStepAnswerChunk(state, chunk("reasoning-delta", { text: "对经历。" }), isPublicTool).kind, "none");
const gate = readFileSync(new URL("../src/lib/think-step-gate.ts", import.meta.url), "utf8");
assert.doesNotMatch(gate, /acceptThinkingFragment|createThinkingFragmentAssembler/);
});
test("publishes only the terminal no-tool step as assistant text", () => {