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

Commit 32c0189

Browse files
authored
fix(pi): preserve cloud model selection (#4032)
1 parent b939358 commit 32c0189

19 files changed

Lines changed: 353 additions & 104 deletions

File tree

‎apps/web/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@posthog/core": "workspace:*",
2020
"@posthog/di": "workspace:*",
2121
"@posthog/host-router": "workspace:*",
22+
"@posthog/harness": "workspace:*",
2223
"@posthog/host-trpc": "workspace:*",
2324
"@posthog/platform": "workspace:*",
2425
"@posthog/shared": "workspace:*",

‎apps/web/src/web-host-router.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { fetchPosthogPiModelCatalog } from "@posthog/agent/pi/model-catalog";
21
import { getLlmGatewayUrl } from "@posthog/agent/posthog-api";
32
import type { AuthService } from "@posthog/core/auth/auth";
43
import { AUTH_SERVICE } from "@posthog/core/auth/auth.module";
54
import { TEAM_SKILLS_SERVICE } from "@posthog/core/skills/identifiers";
65
import type { TeamSkillsService } from "@posthog/core/skills/teamSkillsService";
76
import { resolveService } from "@posthog/di/container";
7+
import { fetchPosthogPiModelCatalog } from "@posthog/harness/extensions/posthog-provider/model-catalog";
88
import { analyticsRouter } from "@posthog/host-router/routers/analytics.router";
99
import { authRouter } from "@posthog/host-router/routers/auth.router";
1010
import { canvasDataRouter } from "@posthog/host-router/routers/canvas-data.router";

‎packages/agent/package.json‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@
5252
"types": "./dist/pi/types.d.ts",
5353
"import": "./dist/pi/types.js"
5454
},
55-
"./pi/model-catalog": {
56-
"types": "./dist/pi/model-catalog.d.ts",
57-
"import": "./dist/pi/model-catalog.js"
58-
},
5955
"./pr-url-detector": {
6056
"types": "./dist/pr-url-detector.d.ts",
6157
"import": "./dist/pr-url-detector.js"

‎packages/agent/tsup.config.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ export default defineConfig([
132132
"src/pi/rpc-client.ts",
133133
"src/pi/runtime.ts",
134134
"src/pi/types.ts",
135-
"src/pi/model-catalog.ts",
136135
"src/pi/conversation/translatePiConversation.ts",
137136
"src/resume.ts",
138137
"src/types.ts",

‎packages/core/src/pi-runtime/piSessionController.test.ts‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,43 @@ describe("PiSessionController", () => {
819819
expect(resumedSession.client.prompt).toHaveBeenCalledWith("continue");
820820
});
821821

822+
it("applies deferred Pi config before the first resumed prompt", async () => {
823+
const terminalSession = {
824+
...createSession(),
825+
resumeRequired: true,
826+
taskRunId: "run-1",
827+
};
828+
const resumedSession = createSession();
829+
const provider = {
830+
get: vi
831+
.fn()
832+
.mockResolvedValueOnce(terminalSession)
833+
.mockResolvedValue(resumedSession),
834+
} as PiSessionProvider;
835+
const resumeCloudPiRun = vi.fn(async () => ({ id: "run-1" }));
836+
const controller = new PiSessionController(provider, {
837+
resumeCloudPiRun,
838+
} as unknown as TaskService);
839+
840+
await controller.connect("task-1");
841+
await controller.submit("task-1", "continue", false, "steer", {
842+
model: { provider: "posthog", id: "gpt-5.6-terra" },
843+
thinkingLevel: "high",
844+
});
845+
846+
expect(resumedSession.client.setModel).toHaveBeenCalledWith(
847+
"posthog",
848+
"gpt-5.6-terra",
849+
);
850+
expect(resumedSession.client.setThinkingLevel).toHaveBeenCalledWith("high");
851+
expect(resumedSession.client.prompt).toHaveBeenCalledWith("continue");
852+
expect(
853+
vi.mocked(resumedSession.client.setModel).mock.invocationCallOrder[0],
854+
).toBeLessThan(
855+
vi.mocked(resumedSession.client.prompt).mock.invocationCallOrder[0],
856+
);
857+
});
858+
822859
it("resumes and retries a message when the prior sandbox is gone", async () => {
823860
const staleSession = {
824861
...createSession(),

‎packages/core/src/pi-runtime/piSessionController.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export type {
3535

3636
export type PiModelSelection = Pick<PiNativeModelInfo, "provider" | "id">;
3737

38+
export interface PiDeferredConfig {
39+
model?: PiModelSelection;
40+
thinkingLevel?: PiThinkingLevel;
41+
}
42+
3843
export const PI_SESSION_PROVIDER = Symbol.for("posthog.pi.sessionProvider");
3944
export const LOCAL_PI_SESSION_FACTORY = Symbol.for(
4045
"posthog.pi.localSessionFactory",
@@ -301,6 +306,7 @@ export class PiSessionController {
301306
text: string,
302307
isStreaming: boolean,
303308
messagingMode: PiMessagingMode,
309+
deferredConfig?: PiDeferredConfig,
304310
): Promise<PiSubmitResult> {
305311
const message = text.trim();
306312
const action = this.getSubmitAction(message, isStreaming, messagingMode);
@@ -382,6 +388,7 @@ export class PiSessionController {
382388

383389
try {
384390
const session = await this.getWritablePiSession(taskId);
391+
await this.applyDeferredConfig(session, deferredConfig);
385392
this.markTurnPending(taskId);
386393
if (session.sendUserMessage && messageId) {
387394
const taskRunId = this.taskRunIds.get(taskId);
@@ -1088,6 +1095,22 @@ export class PiSessionController {
10881095
});
10891096
}
10901097

1098+
private async applyDeferredConfig(
1099+
session: PiSession,
1100+
config: PiDeferredConfig | undefined,
1101+
): Promise<void> {
1102+
if (!config) {
1103+
return;
1104+
}
1105+
1106+
if (config.model) {
1107+
await session.client.setModel(config.model.provider, config.model.id);
1108+
}
1109+
if (config.thinkingLevel) {
1110+
await session.client.setThinkingLevel(config.thinkingLevel);
1111+
}
1112+
}
1113+
10911114
private async refreshStatus(taskId: string): Promise<void> {
10921115
const session = await this.getPiSession(taskId);
10931116
const status = await session.client.getState();

‎packages/core/src/task-detail/taskCreationSaga.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,8 @@ describe("TaskCreationSaga", () => {
453453
branch: "main",
454454
adapter: undefined,
455455
piRuntime: true,
456-
model: undefined,
457-
reasoningLevel: undefined,
456+
model: "gpt-5.4",
457+
reasoningLevel: "high",
458458
initialPermissionMode: undefined,
459459
}),
460460
);

‎packages/core/src/task-detail/taskCreationSaga.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,8 @@ export class TaskCreationSaga extends Saga<
408408
branch,
409409
adapter: cloudAdapter,
410410
...(isPiRuntime ? { piRuntime: true } : {}),
411-
model: isPiRuntime ? undefined : input.model,
412-
reasoningLevel: isPiRuntime ? undefined : input.reasoningLevel,
411+
model: input.model,
412+
reasoningLevel: input.reasoningLevel,
413413
contextWindow: isPiRuntime ? undefined : input.contextWindow,
414414
fastMode: isPiRuntime ? undefined : input.fastMode,
415415
sandboxEnvironmentId: input.sandboxEnvironmentId,

packages/agent/src/pi/model-catalog.test.ts renamed to packages/harness/src/extensions/posthog-provider/model-catalog.test.ts

File renamed without changes.

packages/agent/src/pi/model-catalog.ts renamed to packages/harness/src/extensions/posthog-provider/model-catalog.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@ import {
22
getSupportedThinkingLevels,
33
type ModelThinkingLevel,
44
} from "@earendil-works/pi-ai";
5+
import type { ModelInfo } from "@earendil-works/pi-coding-agent";
6+
import type { CloudRegion } from "@posthog/shared";
57
import {
68
fetchPosthogGatewayModels,
79
type GatewayModel,
810
resolveModelConfigsFromGatewayModels,
9-
} from "@posthog/harness/extensions/posthog-provider/models";
10-
import type { CloudRegion } from "@posthog/shared";
11+
} from "./models";
1112

12-
export interface PiModelCatalogEntry {
13+
export type PiModelCatalogEntry = Omit<
14+
Pick<ModelInfo, "provider" | "id" | "contextWindow">,
15+
"provider"
16+
> & {
1317
provider: "posthog";
14-
id: string;
1518
name: string;
16-
contextWindow: number;
1719
thinkingLevels: ModelThinkingLevel[];
18-
}
20+
};
1921

2022
export function resolvePosthogPiModelCatalog(
2123
gatewayModels: GatewayModel[],

0 commit comments

Comments
 (0)