Don't get out of sync when background task creates new init/result (#386)

Relates to #336.

This does NOT ensure that the results are immediately sent to the
client.

See
https://github.com/zed-industries/claude-agent-acp/issues/336#issuecomment-4011939471.

I guess some tests for this would be useful.
This commit is contained in:
Steffen Deusch
2026-03-09 14:19:49 +01:00
committed by GitHub
parent 9ecb222807
commit 3524606555
2 changed files with 120 additions and 15 deletions

View File

@@ -497,17 +497,23 @@ export class ClaudeAcpAgent implements Agent {
const userMessage = promptToClaude(params);
const promptUuid = randomUUID();
userMessage.uuid = promptUuid;
let promptReplayed = false;
if (session.promptRunning) {
const uuid = randomUUID();
userMessage.uuid = uuid;
session.input.push(userMessage);
const order = session.nextPendingOrder++;
const cancelled = await new Promise<boolean>((resolve) => {
session.pendingMessages.set(uuid, { resolve, order });
session.pendingMessages.set(promptUuid, { resolve, order });
});
if (cancelled) {
return { stopReason: "cancelled" };
}
// The replay resolved the promise, mark in this loop too,
// so we don't treat the next result as a background task's result.
promptReplayed = true;
} else {
session.input.push(userMessage);
}
@@ -582,10 +588,6 @@ export class ClaudeAcpAgent implements Agent {
}
break;
case "result": {
if (session.cancelled) {
return { stopReason: "cancelled" };
}
// Accumulate usage from this result
session.accumulatedUsage.inputTokens += message.usage.input_tokens;
session.accumulatedUsage.outputTokens += message.usage.output_tokens;
@@ -613,6 +615,18 @@ export class ClaudeAcpAgent implements Agent {
});
}
if (!promptReplayed) {
// This result is from a background task that finished after
// the previous prompt loop ended. Consume it and continue
// waiting for our own prompt's result.
this.logger.log(`Session ${params.sessionId}: consuming background task result`);
break;
}
if (session.cancelled) {
return { stopReason: "cancelled" };
}
// Build the usage response
const usage: PromptResponse["usage"] = {
inputTokens: session.accumulatedUsage.inputTokens,
@@ -688,8 +702,14 @@ export class ClaudeAcpAgent implements Agent {
break;
}
// Check for queued prompt replay
// Check for prompt replay
if (message.type === "user" && "uuid" in message && message.uuid) {
if (message.uuid === promptUuid) {
// Our own prompt was replayed back — we're now processing
// our prompt's response (not a background task's).
promptReplayed = true;
break;
}
const pending = session.pendingMessages.get(message.uuid as string);
if (pending) {
pending.resolve(false);