Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 9ea61b1

Browse files
authored
feat: signal to task (#905)
### TL;DR Replace "Run signal" functionality with "Create task" that generates a structured prompt from signal data. ### What changed? - Replaced the "Run" and "Run with context" buttons with a single "Create task" button - Added a new utility function `buildSignalTaskPrompt` that formats signal data into a structured markdown prompt - When clicking "Create task", the app now: - Generates a formatted prompt with signal information, evidence, and replay links - Sets this content in the task input draft - Navigates the user to the task input screen - Removed all code related to the manual context input and dropdown menu
1 parent 2975836 commit 9ea61b1

2 files changed

Lines changed: 60 additions & 91 deletions

File tree

‎apps/twig/src/renderer/features/inbox/components/InboxSignalsTab.tsx‎

Lines changed: 18 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,18 @@ import {
55
useInboxReports,
66
} from "@features/inbox/hooks/useInboxReports";
77
import { useInboxSignalsSidebarStore } from "@features/inbox/stores/inboxSignalsSidebarStore";
8+
import { buildSignalTaskPrompt } from "@features/inbox/utils/buildSignalTaskPrompt";
9+
import { useDraftStore } from "@features/message-editor/stores/draftStore";
810
import {
911
ArrowSquareOutIcon,
1012
ClockIcon,
1113
SparkleIcon,
1214
XIcon,
1315
} from "@phosphor-icons/react";
14-
import { ChevronDownIcon } from "@radix-ui/react-icons";
15-
import {
16-
Badge,
17-
Box,
18-
Button,
19-
DropdownMenu,
20-
Flex,
21-
ScrollArea,
22-
Text,
23-
TextField,
24-
} from "@radix-ui/themes";
16+
import { Badge, Box, Button, Flex, ScrollArea, Text } from "@radix-ui/themes";
2517
import type { SignalReport } from "@shared/types";
18+
import { useNavigationStore } from "@stores/navigationStore";
2619
import { useEffect, useMemo, useState } from "react";
27-
import { toast } from "sonner";
2820
import { getCloudUrlFromRegion } from "@/constants/oauth";
2921

3022
interface InboxSignalsTabProps {
@@ -121,8 +113,6 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
121113
const { data, isLoading, error } = useInboxReports();
122114
const reports = data?.results ?? [];
123115
const [selectedReportId, setSelectedReportId] = useState<string | null>(null);
124-
const [showRunWithContext, setShowRunWithContext] = useState(false);
125-
const [manualRunContext, setManualRunContext] = useState("");
126116
const sidebarOpen = useInboxSignalsSidebarStore((state) => state.open);
127117
const sidebarWidth = useInboxSignalsSidebarStore((state) => state.width);
128118
const sidebarIsResizing = useInboxSignalsSidebarStore(
@@ -170,27 +160,22 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
170160
? `${getCloudUrlFromRegion(cloudRegion)}/project/${projectId}/replay`
171161
: null;
172162

173-
useEffect(() => {
174-
setShowRunWithContext(false);
175-
setManualRunContext("");
176-
}, []);
163+
const { navigateToTaskInput } = useNavigationStore();
164+
const draftActions = useDraftStore((s) => s.actions);
177165

178-
const handleRunSignal = (context?: string) => {
166+
const handleCreateTask = () => {
179167
if (!selectedReport) return;
180168

181-
const trimmedContext = context?.trim();
182-
if (trimmedContext) {
183-
toast.success(
184-
`Started signal run for "${selectedReport.title ?? "Untitled signal"}" with context (stub).`,
185-
);
186-
} else {
187-
toast.success(
188-
`Started signal run for "${selectedReport.title ?? "Untitled signal"}" (stub).`,
189-
);
190-
}
169+
const prompt = buildSignalTaskPrompt({
170+
report: selectedReport,
171+
artefacts: visibleArtefacts,
172+
replayBaseUrl,
173+
});
191174

192-
setShowRunWithContext(false);
193-
setManualRunContext("");
175+
draftActions.setPendingContent("task-input", {
176+
segments: [{ type: "text", text: prompt }],
177+
});
178+
navigateToTaskInput();
194179
};
195180

196181
if (isLoading) {
@@ -305,30 +290,11 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
305290
<Button
306291
size="1"
307292
variant="solid"
308-
onClick={() => handleRunSignal()}
293+
onClick={handleCreateTask}
309294
className="font-mono text-[11px]"
310295
>
311-
Run
296+
Create task
312297
</Button>
313-
<DropdownMenu.Root>
314-
<DropdownMenu.Trigger>
315-
<Button
316-
size="1"
317-
variant="outline"
318-
color="gray"
319-
className="font-mono text-[11px]"
320-
>
321-
<ChevronDownIcon />
322-
</Button>
323-
</DropdownMenu.Trigger>
324-
<DropdownMenu.Content align="end" size="1">
325-
<DropdownMenu.Item
326-
onSelect={() => setShowRunWithContext(true)}
327-
>
328-
Run with context
329-
</DropdownMenu.Item>
330-
</DropdownMenu.Content>
331-
</DropdownMenu.Root>
332298
<Button
333299
size="1"
334300
variant="ghost"
@@ -345,45 +311,6 @@ export function InboxSignalsTab({ onGoToSetup }: InboxSignalsTabProps) {
345311
</Flex>
346312
<ScrollArea type="auto" style={{ height: "calc(100% - 41px)" }}>
347313
<Flex direction="column" gap="2" p="2">
348-
{showRunWithContext && (
349-
<Box className="rounded border border-gray-6 bg-gray-2 p-2">
350-
<Flex direction="column" gap="2">
351-
<Text size="1" className="font-mono text-[11px]">
352-
Additional context
353-
</Text>
354-
<TextField.Root
355-
size="1"
356-
value={manualRunContext}
357-
onChange={(event) =>
358-
setManualRunContext(event.target.value)
359-
}
360-
placeholder="Add instructions for this signal run"
361-
/>
362-
<Flex align="center" gap="1">
363-
<Button
364-
size="1"
365-
variant="solid"
366-
className="font-mono text-[11px]"
367-
disabled={!manualRunContext.trim()}
368-
onClick={() => handleRunSignal(manualRunContext)}
369-
>
370-
Run with context
371-
</Button>
372-
<Button
373-
size="1"
374-
variant="ghost"
375-
className="font-mono text-[11px]"
376-
onClick={() => {
377-
setShowRunWithContext(false);
378-
setManualRunContext("");
379-
}}
380-
>
381-
Cancel
382-
</Button>
383-
</Flex>
384-
</Flex>
385-
</Box>
386-
)}
387314
<Text
388315
size="1"
389316
color="gray"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { SignalReport, SignalReportArtefact } from "@shared/types";
2+
3+
export interface SignalPromptInput {
4+
report: SignalReport;
5+
artefacts: SignalReportArtefact[];
6+
replayBaseUrl: string | null;
7+
}
8+
9+
export function buildSignalTaskPrompt({
10+
report,
11+
artefacts,
12+
replayBaseUrl,
13+
}: SignalPromptInput): string {
14+
const title = report.title ?? "Untitled signal";
15+
const summary = report.summary ?? "No summary available.";
16+
17+
const lines: string[] = [
18+
`# Investigate: ${title}`,
19+
"",
20+
"## Summary",
21+
"",
22+
summary,
23+
"",
24+
`**Signal strength:** ${report.signal_count} occurrences, ${report.relevant_user_count ?? 0} affected users`,
25+
];
26+
27+
if (artefacts.length > 0) {
28+
lines.push("", "## Evidence");
29+
30+
for (const artefact of artefacts) {
31+
const timestamp = new Date(artefact.content.start_time).toLocaleString();
32+
lines.push("", `### Session ${timestamp}`, "", artefact.content.content);
33+
if (replayBaseUrl) {
34+
lines.push(
35+
`[View replay](${replayBaseUrl}/${artefact.content.session_id})`,
36+
);
37+
}
38+
}
39+
}
40+
41+
return lines.join("\n");
42+
}

0 commit comments

Comments
 (0)