fix(discord): engine tests stop in finally; a turn pi never started stops pi instead of guessing (#1509)
Every engine test stops its engine in finally, and commands() tolerates a log that doesn't exist yet, so a failed assertion no longer leaks a fake pi and hangs the six-package union. A turn that failed client-side stays at the front of the queue and holds the next prompt. If pi has sent no agent_start ABORT_GRACE_MS (30 s) after the failure, the engine marks itself wedged, fails held prompts with engine-wedged, refuses new ones with engine-down, and stops pi. The exit reaches onExit, the connector exits 1, and the unit restarts it. Pi's events carry no prompt id, so R1's approach, dropping the turn and sending on, let a late run answer the next prompt. Rocko rejected R1 and approved R2. Tests: engine 17/17 (R1 fails 4, HEAD fails 5). Union 408/408 and the eight suites green on the committed index. Record: agents/darkwing/work/discord-engine-busy/. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -8,9 +8,13 @@
|
||||
// then a second turn that answers "read <n> file(s)"
|
||||
// "toolonly" a run whose only turn calls a tool and never answers
|
||||
// "retry" an agent_end with willRetry, then the real answer
|
||||
// "mute" accept the prompt and emit nothing, staying idle
|
||||
// "late <ms>" ignore abort; after <ms> emit a tool pair and a tool turn,
|
||||
// then answer "late reply", like a run that outlives its
|
||||
// client-side timeout
|
||||
// "stall <ms>" accept the prompt, then read nothing for <ms> (pi stuck
|
||||
// before agent_start); then run it, answering "echo:
|
||||
// stalled", and only then read what came in meanwhile
|
||||
// anything else answer "echo: <text>" immediately
|
||||
// A prompt received while busy without streamingBehavior is refused, as pi
|
||||
// does. A prompt with streamingBehavior followUp is folded into the running
|
||||
@@ -30,6 +34,7 @@ function assistant(text, stopReason = "stop") {
|
||||
}
|
||||
|
||||
function run(text) {
|
||||
if (text === "mute") return;
|
||||
busy = true;
|
||||
out({ type: "agent_start" });
|
||||
out({ type: "turn_start" });
|
||||
@@ -109,11 +114,16 @@ function run(text) {
|
||||
let current = null;
|
||||
|
||||
let buffer = "";
|
||||
let stalled = false;
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
buffer += chunk;
|
||||
drain();
|
||||
});
|
||||
|
||||
function drain() {
|
||||
let idx;
|
||||
while ((idx = buffer.indexOf("\n")) !== -1) {
|
||||
while (!stalled && (idx = buffer.indexOf("\n")) !== -1) {
|
||||
const line = buffer.slice(0, idx);
|
||||
buffer = buffer.slice(idx + 1);
|
||||
if (!line) continue;
|
||||
@@ -125,7 +135,15 @@ process.stdin.on("data", (chunk) => {
|
||||
continue;
|
||||
}
|
||||
out({ id: cmd.id, type: "response", command: "prompt", success: true });
|
||||
if (busy) queue.push(cmd.message);
|
||||
const sm = /^stall (\d+)$/.exec(cmd.message);
|
||||
if (sm) {
|
||||
stalled = true;
|
||||
setTimeout(() => {
|
||||
run("stalled");
|
||||
stalled = false;
|
||||
drain();
|
||||
}, Number(sm[1]));
|
||||
} else if (busy) queue.push(cmd.message);
|
||||
else run(cmd.message);
|
||||
} else if (cmd.type === "abort") {
|
||||
out({ id: cmd.id, type: "response", command: "abort", success: true });
|
||||
@@ -139,5 +157,5 @@ process.stdin.on("data", (chunk) => {
|
||||
out({ id: cmd.id, type: "response", command: cmd.type, success: true, data: {} });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
process.stdin.on("end", () => process.exit(0));
|
||||
|
||||
Reference in New Issue
Block a user