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

Commit c6b211e

Browse files
chore: merge main into MCP transport extraction
Preserve the shared MCP boundary while adopting main's schema-validated automation transport and reasoning coverage. Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22
2 parents 1f3ad6a + a52ddb0 commit c6b211e

47 files changed

Lines changed: 1386 additions & 382 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/api-client/src/posthog-client.ts

Lines changed: 29 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@ import type {
44
CloudMcpServerImport,
55
CloudMcpServerRelayDesignation,
66
CloudRunSource,
7+
CreateTaskAutomationOptions,
78
ExecutionMode,
89
PrAuthorshipMode,
910
SourceProduct,
1011
SourceType,
1112
StoredLogEntry,
13+
TaskAutomation,
1214
TaskRunArtifactMetadata,
15+
UpdateTaskAutomationOptions,
1316
} from "@posthog/shared";
1417
import {
1518
buildCloudTaskConfigOptions,
1619
type CloudTaskConfigOption,
20+
createTaskAutomationSchema,
1721
DISMISSAL_REASON_OPTIONS,
1822
type DismissalReasonOptionValue,
1923
getCloudTaskGatewayUrl,
2024
isSupportedReasoningEffort,
2125
normalizeGatewayModelsResponse,
2226
resolveCloudInitialPermissionMode,
27+
taskAutomationListSchema,
28+
taskAutomationSchema,
29+
taskAutomationValidationErrorSchema,
30+
updateTaskAutomationSchema,
2331
} from "@posthog/shared";
2432
import type {
2533
AgentAnalyticsData,
@@ -202,22 +210,11 @@ export class TaskAutomationValidationError extends Error {
202210

203211
function rethrowTaskAutomationError(error: unknown): never {
204212
if (error instanceof ApiRequestError && error.status === 400) {
205-
const body = error.body;
206-
if (
207-
typeof body === "object" &&
208-
body !== null &&
209-
"detail" in body &&
210-
typeof body.detail === "string"
211-
) {
212-
throw new TaskAutomationValidationError({
213-
detail: body.detail,
214-
code:
215-
"code" in body && typeof body.code === "string"
216-
? body.code
217-
: "invalid_input",
218-
attr:
219-
"attr" in body && typeof body.attr === "string" ? body.attr : null,
220-
});
213+
const validationError = taskAutomationValidationErrorSchema.safeParse(
214+
error.body,
215+
);
216+
if (validationError.success) {
217+
throw new TaskAutomationValidationError(validationError.data);
221218
}
222219
}
223220

@@ -253,41 +250,6 @@ export type {
253250

254251
export type Evaluation = Schemas.Evaluation;
255252

256-
export type TaskAutomation = Omit<
257-
Schemas.TaskAutomation,
258-
"github_integration" | "timezone" | "template_id" | "enabled"
259-
> & {
260-
github_integration: number | null;
261-
timezone: string | null;
262-
template_id: string | null;
263-
enabled: boolean;
264-
};
265-
266-
export type CreateTaskAutomationOptions = Pick<
267-
Schemas.TaskAutomation,
268-
"name" | "prompt" | "repository" | "cron_expression"
269-
> &
270-
Partial<
271-
Pick<
272-
Schemas.TaskAutomation,
273-
"github_integration" | "template_id" | "enabled"
274-
>
275-
> & { timezone: string };
276-
277-
export type UpdateTaskAutomationOptions = Partial<CreateTaskAutomationOptions>;
278-
279-
function normalizeTaskAutomation(
280-
automation: Schemas.TaskAutomation,
281-
): TaskAutomation {
282-
return {
283-
...automation,
284-
github_integration: automation.github_integration ?? null,
285-
timezone: automation.timezone ?? null,
286-
template_id: automation.template_id ?? null,
287-
enabled: automation.enabled ?? true,
288-
};
289-
}
290-
291253
export interface UserGitHubIntegration {
292254
id: string;
293255
kind: "github";
@@ -2524,7 +2486,7 @@ export class PostHogAPIClient {
25242486
},
25252487
);
25262488

2527-
return data.results.map(normalizeTaskAutomation);
2489+
return taskAutomationListSchema.parse(data).results;
25282490
}
25292491

25302492
async getTaskAutomation(automationId: string): Promise<TaskAutomation> {
@@ -2536,22 +2498,24 @@ export class PostHogAPIClient {
25362498
},
25372499
);
25382500

2539-
return normalizeTaskAutomation(data);
2501+
return taskAutomationSchema.parse(data);
25402502
}
25412503

25422504
async createTaskAutomation(
25432505
options: CreateTaskAutomationOptions,
25442506
): Promise<TaskAutomation> {
25452507
const teamId = await this.getTeamId();
2508+
const body = createTaskAutomationSchema.parse(options);
2509+
25462510
try {
25472511
const data = await this.api.post(
25482512
`/api/projects/{project_id}/task_automations/`,
25492513
{
25502514
path: { project_id: teamId.toString() },
2551-
body: options as Schemas.TaskAutomation,
2515+
body: body as Schemas.TaskAutomation,
25522516
},
25532517
);
2554-
return normalizeTaskAutomation(data);
2518+
return taskAutomationSchema.parse(data);
25552519
} catch (error) {
25562520
rethrowTaskAutomationError(error);
25572521
}
@@ -2562,15 +2526,17 @@ export class PostHogAPIClient {
25622526
updates: UpdateTaskAutomationOptions,
25632527
): Promise<TaskAutomation> {
25642528
const teamId = await this.getTeamId();
2529+
const body = updateTaskAutomationSchema.parse(updates);
2530+
25652531
try {
25662532
const data = await this.api.patch(
25672533
`/api/projects/{project_id}/task_automations/{id}/`,
25682534
{
25692535
path: { project_id: teamId.toString(), id: automationId },
2570-
body: updates,
2536+
body,
25712537
},
25722538
);
2573-
return normalizeTaskAutomation(data);
2539+
return taskAutomationSchema.parse(data);
25742540
} catch (error) {
25752541
rethrowTaskAutomationError(error);
25762542
}
@@ -2593,9 +2559,7 @@ export class PostHogAPIClient {
25932559
path,
25942560
url: new URL(`${this.api.baseUrl}${path}`),
25952561
});
2596-
return normalizeTaskAutomation(
2597-
(await response.json()) as Schemas.TaskAutomation,
2598-
);
2562+
return taskAutomationSchema.parse(await response.json());
25992563
} catch (error) {
26002564
rethrowTaskAutomationError(error);
26012565
}
@@ -2640,13 +2604,16 @@ export class PostHogAPIClient {
26402604
return normalizeTaskResponse(data, { teamId });
26412605
}
26422606

2643-
async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
2607+
async updateTask(
2608+
taskId: string,
2609+
updates: Partial<Schemas.Task>,
2610+
): Promise<Task> {
26442611
const teamId = await this.getTeamId();
26452612
const data = await this.api.patch(
26462613
`/api/projects/{project_id}/tasks/{id}/`,
26472614
{
26482615
path: { project_id: teamId.toString(), id: taskId },
2649-
body: updates as unknown as Partial<Schemas.Task>,
2616+
body: updates,
26502617
},
26512618
);
26522619

packages/core/src/automations/automationTemplatePresentation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TaskAutomation } from "@posthog/api-client/posthog-client";
1+
import type { TaskAutomation } from "@posthog/shared";
22

33
export const SKILL_TEMPLATE_ID_PREFIX = "llm-skill:";
44

packages/core/src/context-menu/context-menu.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ describe("ContextMenuService.showTaskContextMenu", () => {
142142
);
143143
});
144144

145+
it("can hide Archive prior tasks for task lists without that action", async () => {
146+
const menu = new FakeContextMenu();
147+
makeService(menu).showTaskContextMenu({
148+
...baseTask,
149+
showArchivePrior: false,
150+
});
151+
await menu.shown;
152+
expect(labels(menu.lastItems)).not.toContain("Archive prior tasks");
153+
expect(labels(menu.lastItems)).toContain("Archive");
154+
});
155+
145156
it("resolves to null when the menu is dismissed", async () => {
146157
const menu = new FakeContextMenu();
147158
const result = makeService(menu).showTaskContextMenu(baseTask);

packages/core/src/context-menu/context-menu.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export class ContextMenuService {
117117
canStop,
118118
isInCommandCenter,
119119
hasEmptyCommandCenterCell,
120+
showArchivePrior = true,
120121
channels,
121122
} = input;
122123
const { apps, lastUsedAppId } = await this.getExternalAppsData();
@@ -162,7 +163,7 @@ export class ContextMenuService {
162163
...(!isInCommandCenter
163164
? [
164165
this.separator(),
165-
this.item(
166+
this.item<TaskAction>(
166167
"Add to Command Center",
167168
{ type: "add-to-command-center" as const },
168169
{ enabled: hasEmptyCommandCenterCell ?? true },
@@ -172,19 +173,23 @@ export class ContextMenuService {
172173
...fileToItems,
173174
this.separator(),
174175
this.item("Archive", { type: "archive" }),
175-
this.item(
176-
"Archive prior tasks",
177-
{ type: "archive-prior" },
178-
{
179-
confirm: {
180-
title: "Archive Prior Tasks",
181-
message: "Archive all tasks older than this one?",
182-
detail:
183-
"This will archive every task created before this one. You can unarchive them later.",
184-
confirmLabel: "Archive",
185-
},
186-
},
187-
),
176+
...(showArchivePrior
177+
? [
178+
this.item<TaskAction>(
179+
"Archive prior tasks",
180+
{ type: "archive-prior" },
181+
{
182+
confirm: {
183+
title: "Archive Prior Tasks",
184+
message: "Archive all tasks older than this one?",
185+
detail:
186+
"This will archive every task created before this one. You can unarchive them later.",
187+
confirmLabel: "Archive",
188+
},
189+
},
190+
),
191+
]
192+
: []),
188193
]);
189194
}
190195

packages/core/src/context-menu/schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const taskContextMenuInput = z.object({
99
canStop: z.boolean().optional(),
1010
isInCommandCenter: z.boolean().optional(),
1111
hasEmptyCommandCenterCell: z.boolean().optional(),
12+
showArchivePrior: z.boolean().optional(),
1213
// Top-level desktop_file_system channels available as "File to…" targets.
1314
// Omit (or pass empty) to hide the submenu entirely.
1415
channels: z.array(z.object({ id: z.string(), name: z.string() })).optional(),

packages/shared/src/reasoning-effort.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ describe("isSupportedReasoningEffort", () => {
77
["codex", "gpt-5.6-sol", "max", true],
88
["codex", "gpt-5.4", "max", false],
99
["claude", "claude-opus-4-8", "xhigh", true],
10+
["claude", "claude-opus-5", "high", true],
11+
["claude", "claude-opus-5", "max", true],
1012
["claude", "claude-sonnet-4-6", "xhigh", false],
1113
["claude", "@cf/zai-org/glm-5.2", "high", true],
1214
["claude", "@cf/zai-org/glm-5.2", "max", true],

packages/shared/src/reasoning-effort.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const CLAUDE_MODEL_EFFORTS: Readonly<
2929
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
3030
"claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
3131
"@cf/zai-org/glm-5.2": ["high", "max"],
32+
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
3233
};
3334

3435
const EFFORT_NAMES: Record<SupportedReasoningEffort, string> = {

packages/ui/src/features/canvas/components/ActivityPanel.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
CaretRightIcon,
44
XIcon,
55
} from "@phosphor-icons/react";
6-
import { Button, cn, Tabs, TabsList, TabsTrigger } from "@posthog/quill";
6+
import { Button, Tabs, TabsList, TabsTrigger } from "@posthog/quill";
77
import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events";
88
import type { Task } from "@posthog/shared/domain-types";
99
import { ActivityTimeline } from "@posthog/ui/features/canvas/components/ActivityTimeline";
@@ -35,9 +35,6 @@ const TABS_WITH_COMPOSER: ReadonlySet<ActivityTab> = new Set([
3535
"comments",
3636
]);
3737

38-
const TIMESTAMP_END_CLASS =
39-
"[&_[data-slot=thread-item-timestamp]]:ml-auto [&_[data-slot=thread-item-timestamp]]:shrink-0 [&_[data-slot=thread-item-timestamp]]:pl-2";
40-
4138
/** The 32px row this panel leads with: the tabs are the header, so the strip
4239
* lines up with the tab bar of the pane on its left (TabbedPanel) and the
4340
* review toolbar, which are the same fixed height and border. */
@@ -119,13 +116,15 @@ function ActivityConversation({
119116
onToggleCollapsed,
120117
onOpenFull,
121118
showTaskSummary,
119+
canOpenInPlace,
122120
}: {
123121
task: Task;
124122
channelId: string;
125123
onClose?: () => void;
126124
onToggleCollapsed?: () => void;
127125
onOpenFull?: () => void;
128126
showTaskSummary: boolean;
127+
canOpenInPlace?: boolean;
129128
}) {
130129
const taskId = task.id;
131130
const {
@@ -183,7 +182,13 @@ function ActivityConversation({
183182

184183
const body = () => {
185184
if (tab === "artifacts") {
186-
return <TaskArtifactsList task={task} timeline={timeline} />;
185+
return (
186+
<TaskArtifactsList
187+
task={task}
188+
timeline={timeline}
189+
canOpenInPlace={canOpenInPlace}
190+
/>
191+
);
187192
}
188193
if (tab === "comments") {
189194
return (
@@ -209,19 +214,15 @@ function ActivityConversation({
209214
currentUserEmail={currentUser?.email}
210215
isTaskAuthor={isTaskAuthor}
211216
canForward={canForward}
217+
canOpenInPlace={canOpenInPlace}
212218
onSendToAgent={sendMessageToAgent}
213219
onDelete={deleteMessage}
214220
/>
215221
);
216222
};
217223

218224
return (
219-
<div
220-
className={cn(
221-
"flex h-full min-w-0 flex-col bg-gray-1",
222-
TIMESTAMP_END_CLASS,
223-
)}
224-
>
225+
<div className="flex h-full min-w-0 flex-col bg-gray-1">
225226
<ActivityHeader
226227
tab={tab}
227228
onTabChange={handleTabChange}
@@ -269,6 +270,7 @@ export function ActivityPanel({
269270
onToggleCollapsed,
270271
onOpenFull,
271272
showTaskSummary = true,
273+
canOpenInPlace,
272274
}: {
273275
taskId: string;
274276
channelId: string;
@@ -278,6 +280,7 @@ export function ActivityPanel({
278280
onToggleCollapsed?: () => void;
279281
onOpenFull?: () => void;
280282
showTaskSummary?: boolean;
283+
canOpenInPlace?: boolean;
281284
}) {
282285
const { data: fetchedTask } = useQuery({
283286
...taskDetailQuery(taskId),
@@ -315,6 +318,7 @@ export function ActivityPanel({
315318
onToggleCollapsed={onToggleCollapsed}
316319
onOpenFull={onOpenFull}
317320
showTaskSummary={showTaskSummary}
321+
canOpenInPlace={canOpenInPlace}
318322
/>
319323
);
320324
}

0 commit comments

Comments
 (0)