Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions plugins/provider-claude-code/src/delta-test-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,74 @@ export function loadSessionFixture(name: string): Record<string, unknown>[] {
});
}

/**
* The assistant `tool_use` block that spawns a background task. The CLI
* streams it before the task's `task_started`, and the translator only
* materializes a task whose spawning call it saw open.
*/
export function spawningToolUseMessage(args: {
toolUseId: string;
toolName: string;
input?: Record<string, unknown>;
parentToolUseId?: string;
}): Record<string, unknown> {
return {
type: "assistant",
message: {
role: "assistant",
content: [
{
type: "tool_use",
id: args.toolUseId,
name: args.toolName,
input: args.input ?? {},
},
],
},
parent_tool_use_id: args.parentToolUseId ?? null,
session_id: "sess-1",
};
}

/** The spawning `tool_use` for a `task_started` fixture or literal. */
export function spawningToolUseFor(
taskStarted: Record<string, unknown>,
): Record<string, unknown> {
const toolUseId = taskStarted.tool_use_id;
if (typeof toolUseId !== "string") {
throw new Error("task_started fixture has no tool_use_id");
}
const description =
typeof taskStarted.description === "string" ? taskStarted.description : "";
switch (taskStarted.task_type) {
case "local_workflow":
return spawningToolUseMessage({
toolUseId,
toolName: "Workflow",
input: { script: taskStarted.prompt ?? "" },
});
case "local_bash":
return spawningToolUseMessage({
toolUseId,
toolName: "Bash",
input: { command: description, run_in_background: true },
});
default:
return spawningToolUseMessage({
toolUseId,
toolName: "Agent",
input: {
description,
prompt: taskStarted.prompt ?? description,
run_in_background: true,
...(typeof taskStarted.subagent_type === "string"
? { subagent_type: taskStarted.subagent_type }
: {}),
},
});
}
}

const CLAUDE_TEST_ENTROPY = "cl-test";
export const TURN_1 = "cl-test-t1";
export const TURN_2 = "cl-test-t2";
Expand Down
9 changes: 9 additions & 0 deletions plugins/provider-claude-code/src/delta-translation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TURN_2,
createClaudeDeltaHarness,
loadFixture,
spawningToolUseFor,
} from "./delta-test-harness.js";

/**
Expand Down Expand Up @@ -435,6 +436,10 @@ describe("claude turn and checkpoint lifecycle", () => {
const harness = createClaudeDeltaHarness();
const context = { threadId: "bb-thread-rate-limited" };
harness.acceptInput("creq_23456789af", context.threadId);
harness.translate(
spawningToolUseFor(loadFixture("task-started-subagent.json")),
context,
);
harness.translate(loadFixture("task-started-subagent.json"), context);
harness.translate(
{
Expand Down Expand Up @@ -937,6 +942,10 @@ describe("claude synthetic no-response handling", () => {
it("keeps an open turn for synthetic no-response messages while an agent is running", () => {
const harness = createClaudeDeltaHarness();
const context = { threadId: "bb-thread-1" };
harness.translate(
spawningToolUseFor(loadFixture("task-started-subagent.json")),
context,
);
harness.translate(loadFixture("task-started-subagent.json"), context);

const events = harness.translate(
Expand Down
1 change: 1 addition & 0 deletions plugins/provider-claude-code/src/delta-translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ export function createClaudeDeltaTranslator(
event,
tasks: state.tasksById,
turnStartSuppressed: isTurnStartSuppressed(state),
hasForwardedToolUse: (toolUseId) => state.startedTools.has(toolUseId),
});
if (taskDeltas !== null) {
return withMirror(state, taskDeltas);
Expand Down
6 changes: 5 additions & 1 deletion plugins/provider-claude-code/src/presentation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
*/
import type { ThreadEvent } from "@bb/domain";
import { describe, expect, it } from "vitest";
import { createClaudeDeltaHarness } from "./delta-test-harness.js";
import {
createClaudeDeltaHarness,
spawningToolUseFor,
} from "./delta-test-harness.js";
import { createClaudeDeltaTranslator } from "./delta-translation.js";

function toolUse(
Expand Down Expand Up @@ -682,6 +685,7 @@ describe("claude background-task presentation", () => {
["local_bash", {}],
["local_agent", { subagent_type: "Explore" }],
] as const) {
harness.translate(spawningToolUseFor(taskStarted(taskType, extra)));
const opened = harness.translate(taskStarted(taskType, extra));
const started = startedItems(opened)[0];
presentations.set(
Expand Down
Loading
Loading