Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions apps/code/src/renderer/desktop-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ container
.toConstantValue(new RendererHedgehogModeHost());
container
.bind<AgentPromptSender>(AGENT_PROMPT_SENDER)
.toConstantValue((taskId, prompt) => {
void resolveService<SessionService>(SESSION_SERVICE).sendPrompt(
.toConstantValue(async (taskId, prompt) => {
await resolveService<SessionService>(SESSION_SERVICE).sendPrompt(
taskId,
prompt,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/features/sessions/agentPromptSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import type { ContentBlock } from "@agentclientprotocol/sdk";
export type AgentPromptSender = (
taskId: string,
prompt: string | ContentBlock[],
) => void;
) => Promise<void>;

export const AGENT_PROMPT_SENDER = Symbol.for("posthog.ui.AgentPromptSender");
69 changes: 69 additions & 0 deletions packages/ui/src/features/sessions/sendPromptToAgent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { toast } from "@posthog/ui/primitives/toast";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { AGENT_PROMPT_SENDER } from "./agentPromptSender";
import { sendPromptToAgent } from "./sendPromptToAgent";

const { mockSender } = vi.hoisted(() => ({ mockSender: vi.fn() }));

vi.mock("@posthog/ui/primitives/toast", () => ({
toast: { error: vi.fn(), info: vi.fn(), success: vi.fn() },
}));

vi.mock("@posthog/di/container", () => ({
resolveService: (token: unknown) => {
if (token === AGENT_PROMPT_SENDER) return mockSender;
throw new Error(`resolveService: unmocked token ${String(token)}`);
},
}));

vi.mock("../code-review/reviewNavigationStore", () => ({
useReviewNavigationStore: {
getState: () => ({ getReviewMode: () => "split", setReviewMode: vi.fn() }),
},
}));

vi.mock("../panels/panelLayoutStore", () => ({
usePanelLayoutStore: {
getState: () => ({ taskLayouts: {}, setActiveTab: vi.fn() }),
},
}));

describe("sendPromptToAgent", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("surfaces a rejected send as an error toast", async () => {
mockSender.mockRejectedValueOnce(
new Error("Agent server is not reachable"),
);

sendPromptToAgent("task-1", "hello");

await vi.waitFor(() =>
expect(toast.error).toHaveBeenCalledWith("Agent server is not reachable"),
);
});

it("falls back to a generic message when the rejection is not an Error", async () => {
mockSender.mockRejectedValueOnce("boom");

sendPromptToAgent("task-1", "hello");

await vi.waitFor(() =>
expect(toast.error).toHaveBeenCalledWith(
"Failed to send your message to the agent. Please try again.",
),
);
});
Comment thread
charlesvien marked this conversation as resolved.
Outdated

it("does not toast when the send resolves", async () => {
mockSender.mockResolvedValueOnce(undefined);

sendPromptToAgent("task-1", "hello");

await Promise.resolve();
await Promise.resolve();
expect(toast.error).not.toHaveBeenCalled();
});
});
15 changes: 14 additions & 1 deletion packages/ui/src/features/sessions/sendPromptToAgent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ContentBlock } from "@agentclientprotocol/sdk";
import { resolveService } from "@posthog/di/container";
import { toast } from "@posthog/ui/primitives/toast";
import { useReviewNavigationStore } from "../code-review/reviewNavigationStore";
import { DEFAULT_TAB_IDS } from "../panels/panelConstants";
import { usePanelLayoutStore } from "../panels/panelLayoutStore";
Expand All @@ -17,7 +18,19 @@ export function sendPromptToAgent(
taskId: string,
prompt: string | ContentBlock[],
): void {
resolveService<AgentPromptSender>(AGENT_PROMPT_SENDER)(taskId, prompt);
// Button/review/skill-initiated prompts are fire-and-forget, but a rejected
// send (auth failure, sandbox unreachable, agent process died) must still be
// surfaced — otherwise the turn just shows "Generated in Xs" with no reply.
void resolveService<AgentPromptSender>(AGENT_PROMPT_SENDER)(
taskId,
prompt,
).catch((error: unknown) => {
toast.error(
error instanceof Error
? error.message
: "Failed to send your message to the agent. Please try again.",
);
});

const { getReviewMode, setReviewMode } = useReviewNavigationStore.getState();
if (getReviewMode(taskId) === "expanded") {
Expand Down
Loading