修正无头应答器收 stdin 的时机
CLI 的 REPL 是「先打印提示符再读行」,所以第一个「你>」出现时本轮还没开始跑:它 就是用来读我们这条任务的。在那里收 stdin,等确认卡弹出来已经是 EOF,自动应答器 根本没机会回 approve,一轮真实跑就这样收在 pending-confirmation。改成按提示符计 数,投递之后的下一个提示符才收口。 同时补上原本缺的那条:总控可能判定直接回复而不起持久 Run,那条路径没有 turn 回 执,只等回执会一直干等到超时——实测白等了十分钟。提示符收口对两种情况都成立。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -946,6 +946,20 @@ export function nextSwarmAutoPilotReply(output) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// CLI 的 REPL 是「先打印提示符再读行」,所以第一个「你>」出现时本轮还没开始跑:
|
||||
// 它就是用来读我们这条任务的。收 stdin 必须等到投递之后的下一个提示符——那才是
|
||||
// 本轮结束、CLI 回到待输入状态。绝大多数情况下此前已经打印过 turn 回执,但总控也
|
||||
// 可能判定直接回复而不起持久 Run,那条路径没有回执,只等回执会一直干等到超时。
|
||||
const swarmChatPromptPattern = /(^|\n)你> $/u;
|
||||
|
||||
export function swarmAutoPilotSitsAtPrompt(output) {
|
||||
return swarmChatPromptPattern.test(output);
|
||||
}
|
||||
|
||||
export function swarmAutoPilotShouldCloseInput(output, promptsAfterSubmit) {
|
||||
return swarmAutoPilotSitsAtPrompt(output) && promptsAfterSubmit >= 1;
|
||||
}
|
||||
|
||||
async function runTaskCargo(
|
||||
cliArguments,
|
||||
task,
|
||||
@@ -960,6 +974,9 @@ async function runTaskCargo(
|
||||
const reportLines = [];
|
||||
let pendingLine = '';
|
||||
let settled = false;
|
||||
let taskSubmitted = false;
|
||||
let promptsSeen = 0;
|
||||
let sittingAtPrompt = false;
|
||||
child.stdout.setEncoding('utf8');
|
||||
child.stdout.on('data', (chunk) => {
|
||||
process.stdout.write(chunk);
|
||||
@@ -974,8 +991,15 @@ async function runTaskCargo(
|
||||
}
|
||||
}
|
||||
if (!autoPilot || child.stdin.writableEnded) return;
|
||||
// turn 已给出回执后不再应答,直接收 stdin 让 CLI 正常退出。
|
||||
if (settled) {
|
||||
const atPrompt = swarmAutoPilotSitsAtPrompt(pendingLine);
|
||||
if (atPrompt && !sittingAtPrompt) promptsSeen += 1;
|
||||
sittingAtPrompt = atPrompt;
|
||||
// turn 已给出回执、或 CLI 回到了投递之后的下一个提示符,都说明本轮结束。
|
||||
if (
|
||||
settled ||
|
||||
(taskSubmitted &&
|
||||
swarmAutoPilotShouldCloseInput(pendingLine, promptsSeen - 1))
|
||||
) {
|
||||
child.stdin.end();
|
||||
return;
|
||||
}
|
||||
@@ -987,6 +1011,7 @@ async function runTaskCargo(
|
||||
});
|
||||
if (autoPilot) {
|
||||
child.stdin.write(`${task}\n`);
|
||||
taskSubmitted = true;
|
||||
} else {
|
||||
child.stdin.end(`${task}\n`);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ import {
|
||||
resolveSwarmTestTimeoutMs,
|
||||
runnerEndpointFileName,
|
||||
shouldStartPersistentPreview,
|
||||
swarmAutoPilotShouldCloseInput,
|
||||
swarmAutoPilotSitsAtPrompt,
|
||||
terminateChildTree,
|
||||
testProjectPrefix,
|
||||
testProjectSentinelName,
|
||||
@@ -715,6 +717,28 @@ describe('Swarm test argument parsing', () => {
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('closes the driven stdin only at the prompt that follows submission', () => {
|
||||
// CLI 先打印提示符再读行:第一个「你>」是用来读我们这条任务的,此时本轮还没
|
||||
// 开始跑。在那里收 stdin,确认卡弹出来时已经是 EOF,自动应答器根本没机会回
|
||||
// approve——实测就这样把一轮跑成了 pending-confirmation。
|
||||
expect(swarmAutoPilotSitsAtPrompt('你> ')).toBe(true);
|
||||
expect(swarmAutoPilotShouldCloseInput('你> ', 0)).toBe(false);
|
||||
expect(swarmAutoPilotShouldCloseInput('你> ', 1)).toBe(true);
|
||||
// 总控判定直接回复那条路径没有 turn 回执,同样靠这个提示符收口。
|
||||
expect(swarmAutoPilotShouldCloseInput('[意图] reply\n\n你> ', 1)).toBe(
|
||||
true,
|
||||
);
|
||||
// 提示符不在缓冲末尾、或本轮还在推进时不得收 stdin。
|
||||
expect(
|
||||
swarmAutoPilotSitsAtPrompt(
|
||||
'你> [决策] Agent 正在判断直接回复或调用持久能力。',
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
swarmAutoPilotSitsAtPrompt('[状态] project-supervisor running/planning'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps persistent preview only for manual chat mode', () => {
|
||||
expect(shouldStartPersistentPreview(parseSwarmTestArguments([]))).toBe(
|
||||
true,
|
||||
|
||||
Reference in New Issue
Block a user