diff --git a/plugins/provider-claude-code/src/event-translation.ts b/plugins/provider-claude-code/src/event-translation.ts index aa03d5d1a..d463e4232 100644 --- a/plugins/provider-claude-code/src/event-translation.ts +++ b/plugins/provider-claude-code/src/event-translation.ts @@ -1362,12 +1362,22 @@ export function createClaudeEventTranslator( }); } const message = parsedMessage.data; - const turnId = resolveProviderTerminalTurn({ - events, - registry: args.turnState, - state, - threadId, - }); + // A result whose loop the CLI opened for itself (a background-task + // notification) never answers accepted user input. On resume the CLI + // settles tasks orphaned by the previous process with such a result + // (num_turns 0) before it reads the prompt bb pushed; opening the + // pending turn here would close it empty and leave the real answer to + // start an unsolicited turn (#1718). Only an already-open turn (the + // model was reinvoked) may complete on it. + const turnId = + message.origin?.kind === "task-notification" + ? state.currentTurnId + : resolveProviderTerminalTurn({ + events, + registry: args.turnState, + state, + threadId, + }); if (turnId) { const contextWindowUsage = extractClaudeContextWindowUsage({ fallbackModelContextWindow: state.selectedModelContextWindow, diff --git a/plugins/provider-claude-code/src/schemas.ts b/plugins/provider-claude-code/src/schemas.ts index e565efa5e..4580bae00 100644 --- a/plugins/provider-claude-code/src/schemas.ts +++ b/plugins/provider-claude-code/src/schemas.ts @@ -406,6 +406,14 @@ export const claudeResultMessageSchema = z result: z.unknown().optional(), usage: z.unknown().optional(), modelUsage: z.unknown().optional(), + // Provenance of the loop this result settles. Absent or "human" means the + // user's prompt; "task-notification" means the CLI reinvoked itself for a + // background task (including the zero-work settlement it emits on resume + // for tasks orphaned by the previous process, #1718). + origin: z + .object({ kind: z.string().min(1) }) + .passthrough() + .optional(), }) .passthrough(); export type ClaudeResultMessage = z.infer;