Skip to content

Commit c7e3dd9

Browse files
oliverb123Copilot
andauthored
feat(sig): Add user autonomy config, refactor API (#1672)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: oliverb123 <8105072+oliverb123@users.noreply.github.com>
1 parent 2fbe93e commit c7e3dd9

13 files changed

Lines changed: 445 additions & 114 deletions

File tree

apps/code/src/renderer/api/posthogClient.ts

Lines changed: 153 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import type {
1515
SignalReportStatus,
1616
SignalReportsQueryParams,
1717
SignalReportsResponse,
18+
SignalReportTask,
19+
SignalTeamConfig,
20+
SignalUserAutonomyConfig,
1821
SuggestedReviewersArtefact,
1922
Task,
2023
TaskRun,
@@ -412,7 +415,7 @@ export class PostHogAPIClient {
412415
async listSignalSourceConfigs(
413416
projectId: number,
414417
): Promise<SignalSourceConfig[]> {
415-
const urlPath = `/api/projects/${projectId}/signal_source_configs/`;
418+
const urlPath = `/api/projects/${projectId}/signals/source_configs/`;
416419
const url = new URL(`${this.api.baseUrl}${urlPath}`);
417420
const response = await this.api.fetcher.fetch({
418421
method: "get",
@@ -439,7 +442,7 @@ export class PostHogAPIClient {
439442
config?: Record<string, unknown>;
440443
},
441444
): Promise<SignalSourceConfig> {
442-
const urlPath = `/api/projects/${projectId}/signal_source_configs/`;
445+
const urlPath = `/api/projects/${projectId}/signals/source_configs/`;
443446
const url = new URL(`${this.api.baseUrl}${urlPath}`);
444447
const response = await this.api.fetcher.fetch({
445448
method: "post",
@@ -466,7 +469,7 @@ export class PostHogAPIClient {
466469
configId: string,
467470
updates: { enabled: boolean },
468471
): Promise<SignalSourceConfig> {
469-
const urlPath = `/api/projects/${projectId}/signal_source_configs/${configId}/`;
472+
const urlPath = `/api/projects/${projectId}/signals/source_configs/${configId}/`;
470473
const url = new URL(`${this.api.baseUrl}${urlPath}`);
471474
const response = await this.api.fetcher.fetch({
472475
method: "patch",
@@ -1212,7 +1215,7 @@ export class PostHogAPIClient {
12121215
): Promise<SignalReportsResponse> {
12131216
const teamId = await this.getTeamId();
12141217
const url = new URL(
1215-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/`,
1218+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/`,
12161219
);
12171220

12181221
if (params?.limit != null) {
@@ -1237,7 +1240,7 @@ export class PostHogAPIClient {
12371240
const response = await this.api.fetcher.fetch({
12381241
method: "get",
12391242
url,
1240-
path: `/api/projects/${teamId}/signal_reports/`,
1243+
path: `/api/projects/${teamId}/signals/reports/`,
12411244
});
12421245

12431246
if (!response.ok) {
@@ -1254,9 +1257,9 @@ export class PostHogAPIClient {
12541257
async getSignalProcessingState(): Promise<SignalProcessingStateResponse> {
12551258
const teamId = await this.getTeamId();
12561259
const url = new URL(
1257-
`${this.api.baseUrl}/api/projects/${teamId}/signal_processing/`,
1260+
`${this.api.baseUrl}/api/projects/${teamId}/signals/processing/`,
12581261
);
1259-
const path = `/api/projects/${teamId}/signal_processing/`;
1262+
const path = `/api/projects/${teamId}/signals/processing/`;
12601263

12611264
const response = await this.api.fetcher.fetch({
12621265
method: "get",
@@ -1282,9 +1285,9 @@ export class PostHogAPIClient {
12821285
): Promise<AvailableSuggestedReviewersResponse> {
12831286
const teamId = await this.getTeamId();
12841287
const url = new URL(
1285-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/available_reviewers/`,
1288+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/available_reviewers/`,
12861289
);
1287-
const path = `/api/projects/${teamId}/signal_reports/available_reviewers/`;
1290+
const path = `/api/projects/${teamId}/signals/reports/available_reviewers/`;
12881291

12891292
if (query?.trim()) {
12901293
url.searchParams.set("query", query.trim());
@@ -1311,12 +1314,12 @@ export class PostHogAPIClient {
13111314
try {
13121315
const teamId = await this.getTeamId();
13131316
const url = new URL(
1314-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/signals/`,
1317+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/signals/`,
13151318
);
13161319
const response = await this.api.fetcher.fetch({
13171320
method: "get",
13181321
url,
1319-
path: `/api/projects/${teamId}/signal_reports/${reportId}/signals/`,
1322+
path: `/api/projects/${teamId}/signals/reports/${reportId}/signals/`,
13201323
});
13211324

13221325
if (!response.ok) {
@@ -1343,9 +1346,9 @@ export class PostHogAPIClient {
13431346
): Promise<SignalReportArtefactsResponse> {
13441347
const teamId = await this.getTeamId();
13451348
const url = new URL(
1346-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/artefacts/`,
1349+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/artefacts/`,
13471350
);
1348-
const path = `/api/projects/${teamId}/signal_reports/${reportId}/artefacts/`;
1351+
const path = `/api/projects/${teamId}/signals/reports/${reportId}/artefacts/`;
13491352

13501353
try {
13511354
const response = await this.api.fetcher.fetch({
@@ -1410,9 +1413,9 @@ export class PostHogAPIClient {
14101413
): Promise<SignalReport> {
14111414
const teamId = await this.getTeamId();
14121415
const url = new URL(
1413-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/state/`,
1416+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/state/`,
14141417
);
1415-
const path = `/api/projects/${teamId}/signal_reports/${reportId}/state/`;
1418+
const path = `/api/projects/${teamId}/signals/reports/${reportId}/state/`;
14161419

14171420
const response = await this.api.fetcher.fetch({
14181421
method: "post",
@@ -1437,9 +1440,9 @@ export class PostHogAPIClient {
14371440
}> {
14381441
const teamId = await this.getTeamId();
14391442
const url = new URL(
1440-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/`,
1443+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/`,
14411444
);
1442-
const path = `/api/projects/${teamId}/signal_reports/${reportId}/`;
1445+
const path = `/api/projects/${teamId}/signals/reports/${reportId}/`;
14431446

14441447
const response = await this.api.fetcher.fetch({
14451448
method: "delete",
@@ -1464,9 +1467,9 @@ export class PostHogAPIClient {
14641467
}> {
14651468
const teamId = await this.getTeamId();
14661469
const url = new URL(
1467-
`${this.api.baseUrl}/api/projects/${teamId}/signal_reports/${reportId}/reingest/`,
1470+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/reingest/`,
14681471
);
1469-
const path = `/api/projects/${teamId}/signal_reports/${reportId}/reingest/`;
1472+
const path = `/api/projects/${teamId}/signals/reports/${reportId}/reingest/`;
14701473

14711474
const response = await this.api.fetcher.fetch({
14721475
method: "post",
@@ -1485,6 +1488,137 @@ export class PostHogAPIClient {
14851488
};
14861489
}
14871490

1491+
async getSignalReportTasks(
1492+
reportId: string,
1493+
options?: { relationship?: SignalReportTask["relationship"] },
1494+
): Promise<SignalReportTask[]> {
1495+
const teamId = await this.getTeamId();
1496+
const url = new URL(
1497+
`${this.api.baseUrl}/api/projects/${teamId}/signals/reports/${reportId}/tasks/`,
1498+
);
1499+
if (options?.relationship) {
1500+
url.searchParams.set("relationship", options.relationship);
1501+
}
1502+
const path = `/api/projects/${teamId}/signals/reports/${reportId}/tasks/`;
1503+
1504+
const response = await this.api.fetcher.fetch({
1505+
method: "get",
1506+
url,
1507+
path,
1508+
});
1509+
1510+
if (!response.ok) {
1511+
throw new Error(
1512+
`Failed to fetch signal report tasks: ${response.statusText}`,
1513+
);
1514+
}
1515+
1516+
const data = await response.json();
1517+
return data.results ?? [];
1518+
}
1519+
1520+
async getSignalTeamConfig(): Promise<SignalTeamConfig> {
1521+
const teamId = await this.getTeamId();
1522+
const url = new URL(
1523+
`${this.api.baseUrl}/api/projects/${teamId}/signals/config/`,
1524+
);
1525+
const path = `/api/projects/${teamId}/signals/config/`;
1526+
1527+
const response = await this.api.fetcher.fetch({
1528+
method: "get",
1529+
url,
1530+
path,
1531+
});
1532+
1533+
if (!response.ok) {
1534+
throw new Error(
1535+
`Failed to fetch signal team config: ${response.statusText}`,
1536+
);
1537+
}
1538+
1539+
return (await response.json()) as SignalTeamConfig;
1540+
}
1541+
1542+
async updateSignalTeamConfig(updates: {
1543+
default_autostart_priority: string;
1544+
}): Promise<SignalTeamConfig> {
1545+
const teamId = await this.getTeamId();
1546+
const url = new URL(
1547+
`${this.api.baseUrl}/api/projects/${teamId}/signals/config/`,
1548+
);
1549+
const path = `/api/projects/${teamId}/signals/config/`;
1550+
1551+
const response = await this.api.fetcher.fetch({
1552+
method: "post",
1553+
url,
1554+
path,
1555+
overrides: {
1556+
body: JSON.stringify(updates),
1557+
},
1558+
});
1559+
1560+
if (!response.ok) {
1561+
throw new Error(
1562+
`Failed to update signal team config: ${response.statusText}`,
1563+
);
1564+
}
1565+
1566+
return (await response.json()) as SignalTeamConfig;
1567+
}
1568+
1569+
async getSignalUserAutonomyConfig(): Promise<SignalUserAutonomyConfig | null> {
1570+
const url = new URL(`${this.api.baseUrl}/api/users/@me/signal_autonomy/`);
1571+
const path = "/api/users/@me/signal_autonomy/";
1572+
1573+
const response = await this.api.fetcher.fetch({
1574+
method: "get",
1575+
url,
1576+
path,
1577+
});
1578+
1579+
return (await response.json()) as SignalUserAutonomyConfig;
1580+
}
1581+
1582+
async updateSignalUserAutonomyConfig(updates: {
1583+
autostart_priority: string | null;
1584+
}): Promise<SignalUserAutonomyConfig> {
1585+
const url = new URL(`${this.api.baseUrl}/api/users/@me/signal_autonomy/`);
1586+
const path = "/api/users/@me/signal_autonomy/";
1587+
1588+
const response = await this.api.fetcher.fetch({
1589+
method: "post",
1590+
url,
1591+
path,
1592+
overrides: {
1593+
body: JSON.stringify(updates),
1594+
},
1595+
});
1596+
1597+
if (!response.ok) {
1598+
throw new Error(
1599+
`Failed to update signal user autonomy config: ${response.statusText}`,
1600+
);
1601+
}
1602+
return (await response.json()) as SignalUserAutonomyConfig;
1603+
}
1604+
1605+
async deleteSignalUserAutonomyConfig(): Promise<void> {
1606+
const url = new URL(`${this.api.baseUrl}/api/users/@me/signal_autonomy/`);
1607+
const path = "/api/users/@me/signal_autonomy/";
1608+
1609+
const response = await this.api.fetcher.fetch({
1610+
method: "delete",
1611+
url,
1612+
path,
1613+
});
1614+
1615+
if (!response.ok) {
1616+
throw new Error(
1617+
`Failed to delete signal user autonomy config: ${response.statusText}`,
1618+
);
1619+
}
1620+
}
1621+
14881622
async getMcpServers(): Promise<McpRecommendedServer[]> {
14891623
const teamId = await this.getTeamId();
14901624
const url = new URL(

apps/code/src/renderer/features/inbox/components/SignalSourceToggles.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@ interface SignalSourceToggleCardProps {
4747
onSetup?: () => void;
4848
loading?: boolean;
4949
statusSection?: React.ReactNode;
50+
syncStatus?: string | null;
51+
}
52+
53+
function syncStatusLabel(status: string | null | undefined): {
54+
text: string;
55+
color: string;
56+
} | null {
57+
if (!status) return null;
58+
switch (status) {
59+
case "running":
60+
return { text: "Syncing…", color: "var(--amber-11)" };
61+
case "completed":
62+
return { text: "Synced", color: "var(--green-11)" };
63+
case "failed":
64+
return { text: "Sync failed", color: "var(--red-11)" };
65+
default:
66+
return null;
67+
}
5068
}
5169

5270
const SignalSourceToggleCard = memo(function SignalSourceToggleCard({
@@ -61,7 +79,10 @@ const SignalSourceToggleCard = memo(function SignalSourceToggleCard({
6179
onSetup,
6280
loading,
6381
statusSection,
82+
syncStatus,
6483
}: SignalSourceToggleCardProps) {
84+
const statusInfo = checked ? syncStatusLabel(syncStatus) : null;
85+
6586
return (
6687
<Box
6788
p="4"
@@ -92,6 +113,11 @@ const SignalSourceToggleCard = memo(function SignalSourceToggleCard({
92113
{label}
93114
</Text>
94115
{labelSuffix}
116+
{statusInfo && (
117+
<Text size="1" style={{ color: statusInfo.color }}>
118+
{statusInfo.text}
119+
</Text>
120+
)}
95121
</Flex>
96122
<Text size="1" style={{ color: "var(--gray-11)" }}>
97123
{description}
@@ -276,7 +302,7 @@ interface SignalSourceTogglesProps {
276302
sourceStates?: Partial<
277303
Record<
278304
keyof SignalSourceValues,
279-
{ requiresSetup: boolean; loading: boolean }
305+
{ requiresSetup: boolean; loading: boolean; syncStatus?: string | null }
280306
>
281307
>;
282308
sessionAnalysisStatus?: SignalSourceConfig["status"];
@@ -334,6 +360,7 @@ export function SignalSourceToggles({
334360
checked={value.error_tracking}
335361
onCheckedChange={toggleErrorTracking}
336362
disabled={disabled}
363+
syncStatus={sourceStates?.error_tracking?.syncStatus}
337364
/>
338365
<SignalSourceToggleCard
339366
icon={<ChatsIcon size={20} />}
@@ -386,6 +413,7 @@ export function SignalSourceToggles({
386413
requiresSetup={sourceStates?.github?.requiresSetup}
387414
onSetup={setupGithub}
388415
loading={sourceStates?.github?.loading}
416+
syncStatus={sourceStates?.github?.syncStatus}
389417
/>
390418
<SignalSourceToggleCard
391419
icon={<KanbanIcon size={20} />}
@@ -397,6 +425,7 @@ export function SignalSourceToggles({
397425
requiresSetup={sourceStates?.linear?.requiresSetup}
398426
onSetup={setupLinear}
399427
loading={sourceStates?.linear?.loading}
428+
syncStatus={sourceStates?.linear?.syncStatus}
400429
/>
401430
<SignalSourceToggleCard
402431
icon={<TicketIcon size={20} />}
@@ -408,6 +437,7 @@ export function SignalSourceToggles({
408437
requiresSetup={sourceStates?.zendesk?.requiresSetup}
409438
onSetup={setupZendesk}
410439
loading={sourceStates?.zendesk?.loading}
440+
syncStatus={sourceStates?.zendesk?.syncStatus}
411441
/>
412442
</Flex>
413443
);

0 commit comments

Comments
 (0)