@posthog/agent 2.3.263 → 2.3.278
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/permissions/permission-options.js +4 -4
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +3 -3
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/adapters/codex/models.d.ts +7 -0
- package/dist/adapters/codex/models.js +13 -0
- package/dist/adapters/codex/models.js.map +1 -0
- package/dist/adapters/reasoning-effort.d.ts +10 -0
- package/dist/adapters/reasoning-effort.js +51 -0
- package/dist/adapters/reasoning-effort.js.map +1 -0
- package/dist/agent.js +12 -5
- package/dist/agent.js.map +1 -1
- package/dist/execution-mode.js +3 -3
- package/dist/execution-mode.js.map +1 -1
- package/dist/posthog-api.js +5 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +5 -0
- package/dist/server/agent-server.js +69 -28
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +205 -128
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +5 -1
- package/src/adapters/claude/permissions/permission-options.ts +1 -1
- package/src/adapters/codex/models.ts +16 -0
- package/src/adapters/codex/spawn.ts +5 -0
- package/src/adapters/reasoning-effort.ts +35 -0
- package/src/execution-mode.ts +3 -3
- package/src/server/agent-server.test.ts +70 -11
- package/src/server/agent-server.ts +82 -34
- package/src/server/bin.ts +24 -0
- package/src/server/types.ts +3 -0
- package/src/test/mocks/msw-handlers.ts +24 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/agent",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.278",
|
|
4
4
|
"repository": "https://github.com/PostHog/code",
|
|
5
5
|
"description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
6
6
|
"exports": {
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
"types": "./dist/adapters/claude/session/models.d.ts",
|
|
49
49
|
"import": "./dist/adapters/claude/session/models.js"
|
|
50
50
|
},
|
|
51
|
+
"./adapters/reasoning-effort": {
|
|
52
|
+
"types": "./dist/adapters/reasoning-effort.d.ts",
|
|
53
|
+
"import": "./dist/adapters/reasoning-effort.js"
|
|
54
|
+
},
|
|
51
55
|
"./execution-mode": {
|
|
52
56
|
"types": "./dist/execution-mode.d.ts",
|
|
53
57
|
"import": "./dist/execution-mode.js"
|
|
@@ -100,7 +100,7 @@ export function buildExitPlanModePermissionOptions(): PermissionOption[] {
|
|
|
100
100
|
if (ALLOW_BYPASS) {
|
|
101
101
|
options.push({
|
|
102
102
|
kind: "allow_always",
|
|
103
|
-
name: "Yes,
|
|
103
|
+
name: "Yes, bypass all permissions",
|
|
104
104
|
optionId: "bypassPermissions",
|
|
105
105
|
});
|
|
106
106
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface ReasoningEffortOption {
|
|
2
|
+
value: string;
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const CODEX_REASONING_EFFORT_OPTIONS: ReasoningEffortOption[] = [
|
|
7
|
+
{ value: "low", name: "Low" },
|
|
8
|
+
{ value: "medium", name: "Medium" },
|
|
9
|
+
{ value: "high", name: "High" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export function getReasoningEffortOptions(
|
|
13
|
+
_modelId: string,
|
|
14
|
+
): ReasoningEffortOption[] {
|
|
15
|
+
return CODEX_REASONING_EFFORT_OPTIONS;
|
|
16
|
+
}
|
|
@@ -11,6 +11,7 @@ export interface CodexProcessOptions {
|
|
|
11
11
|
apiBaseUrl?: string;
|
|
12
12
|
apiKey?: string;
|
|
13
13
|
model?: string;
|
|
14
|
+
reasoningEffort?: string;
|
|
14
15
|
instructions?: string;
|
|
15
16
|
binaryPath?: string;
|
|
16
17
|
logger?: Logger;
|
|
@@ -52,6 +53,10 @@ function buildConfigArgs(options: CodexProcessOptions): string[] {
|
|
|
52
53
|
args.push("-c", `model="${options.model}"`);
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
if (options.reasoningEffort) {
|
|
57
|
+
args.push("-c", `model_reasoning_effort="${options.reasoningEffort}"`);
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
if (options.instructions) {
|
|
56
61
|
const escaped = options.instructions
|
|
57
62
|
.replace(/\\/g, "\\\\")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getEffortOptions as getClaudeEffortOptions } from "./claude/session/models";
|
|
2
|
+
import { getReasoningEffortOptions as getCodexReasoningEffortOptions } from "./codex/models";
|
|
3
|
+
|
|
4
|
+
export type RuntimeAdapter = "claude" | "codex";
|
|
5
|
+
|
|
6
|
+
export type SupportedReasoningEffort = "low" | "medium" | "high" | "max";
|
|
7
|
+
|
|
8
|
+
export interface ReasoningEffortOption {
|
|
9
|
+
value: SupportedReasoningEffort;
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getReasoningEffortOptions(
|
|
14
|
+
adapter: RuntimeAdapter,
|
|
15
|
+
modelId: string,
|
|
16
|
+
): ReasoningEffortOption[] | null {
|
|
17
|
+
const options =
|
|
18
|
+
adapter === "codex"
|
|
19
|
+
? getCodexReasoningEffortOptions(modelId)
|
|
20
|
+
: getClaudeEffortOptions(modelId);
|
|
21
|
+
|
|
22
|
+
return options as ReasoningEffortOption[] | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isSupportedReasoningEffort(
|
|
26
|
+
adapter: RuntimeAdapter,
|
|
27
|
+
modelId: string,
|
|
28
|
+
value: string,
|
|
29
|
+
): value is SupportedReasoningEffort {
|
|
30
|
+
return (
|
|
31
|
+
getReasoningEffortOptions(adapter, modelId)?.some(
|
|
32
|
+
(option) => option.value === value,
|
|
33
|
+
) ?? false
|
|
34
|
+
);
|
|
35
|
+
}
|
package/src/execution-mode.ts
CHANGED
|
@@ -35,8 +35,8 @@ const availableModes: ModeInfo[] = [
|
|
|
35
35
|
if (ALLOW_BYPASS) {
|
|
36
36
|
availableModes.push({
|
|
37
37
|
id: "bypassPermissions",
|
|
38
|
-
name: "
|
|
39
|
-
description: "
|
|
38
|
+
name: "Bypass Permissions",
|
|
39
|
+
description: "Bypass all permission prompts",
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -84,7 +84,7 @@ if (ALLOW_BYPASS) {
|
|
|
84
84
|
codexModes.push({
|
|
85
85
|
id: "full-access",
|
|
86
86
|
name: "Full Access",
|
|
87
|
-
description: "
|
|
87
|
+
description: "Bypass all permission prompts",
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -21,6 +21,17 @@ interface TestableServer {
|
|
|
21
21
|
detectedPrUrl: string | null;
|
|
22
22
|
buildCloudSystemPrompt(prUrl?: string | null): string;
|
|
23
23
|
buildDetectedPrContext(prUrl: string): string;
|
|
24
|
+
buildSessionSystemPrompt(prUrl?: string | null): string | { append: string };
|
|
25
|
+
buildCodexInstructions(systemPrompt: string | { append: string }): string;
|
|
26
|
+
getRuntimeAdapter(): "claude" | "codex";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let nextTestPort = 20000;
|
|
30
|
+
|
|
31
|
+
function getNextTestPort(): number {
|
|
32
|
+
const port = nextTestPort;
|
|
33
|
+
nextTestPort += 1;
|
|
34
|
+
return port;
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
// The Claude Agent SDK has an internal readMessages() loop that rejects with
|
|
@@ -112,14 +123,16 @@ JwIDAQAB
|
|
|
112
123
|
|
|
113
124
|
describe("AgentServer HTTP Mode", () => {
|
|
114
125
|
let repo: TestRepo;
|
|
115
|
-
let server: AgentServer;
|
|
126
|
+
let server: AgentServer | undefined;
|
|
116
127
|
let mswServer: SetupServerApi;
|
|
117
128
|
let appendLogCalls: unknown[][];
|
|
118
|
-
|
|
129
|
+
let port: number;
|
|
119
130
|
|
|
120
131
|
beforeEach(async () => {
|
|
121
132
|
repo = await createTestRepo("agent-server-http");
|
|
122
133
|
appendLogCalls = [];
|
|
134
|
+
// Use a unique high port per test to avoid reuse and browser-blocked ports.
|
|
135
|
+
port = getNextTestPort();
|
|
123
136
|
mswServer = setupServer(
|
|
124
137
|
...createPostHogHandlers({
|
|
125
138
|
baseUrl: "http://localhost:8000",
|
|
@@ -132,12 +145,15 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
132
145
|
afterEach(async () => {
|
|
133
146
|
if (server) {
|
|
134
147
|
await server.stop();
|
|
148
|
+
server = undefined;
|
|
135
149
|
}
|
|
136
150
|
mswServer.close();
|
|
137
151
|
await repo.cleanup();
|
|
138
152
|
});
|
|
139
153
|
|
|
140
|
-
const createServer = (
|
|
154
|
+
const createServer = (
|
|
155
|
+
overrides: Partial<ConstructorParameters<typeof AgentServer>[0]> = {},
|
|
156
|
+
) => {
|
|
141
157
|
server = new AgentServer({
|
|
142
158
|
port,
|
|
143
159
|
jwtPublicKey: TEST_PUBLIC_KEY,
|
|
@@ -148,6 +164,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
148
164
|
mode: "interactive",
|
|
149
165
|
taskId: "test-task-id",
|
|
150
166
|
runId: "test-run-id",
|
|
167
|
+
...overrides,
|
|
151
168
|
});
|
|
152
169
|
return server;
|
|
153
170
|
};
|
|
@@ -176,7 +193,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
176
193
|
|
|
177
194
|
expect(response.status).toBe(200);
|
|
178
195
|
expect(body).toEqual({ status: "ok", hasSession: true });
|
|
179
|
-
});
|
|
196
|
+
}, 30000);
|
|
180
197
|
});
|
|
181
198
|
|
|
182
199
|
describe("GET /events", () => {
|
|
@@ -188,7 +205,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
188
205
|
|
|
189
206
|
expect(response.status).toBe(401);
|
|
190
207
|
expect(body.error).toBe("Missing authorization header");
|
|
191
|
-
});
|
|
208
|
+
}, 20000);
|
|
192
209
|
|
|
193
210
|
it("returns 401 with invalid token", async () => {
|
|
194
211
|
await createServer().start();
|
|
@@ -200,7 +217,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
200
217
|
|
|
201
218
|
expect(response.status).toBe(401);
|
|
202
219
|
expect(body.code).toBe("invalid_signature");
|
|
203
|
-
});
|
|
220
|
+
}, 20000);
|
|
204
221
|
|
|
205
222
|
it("accepts valid JWT and returns SSE stream", async () => {
|
|
206
223
|
await createServer().start();
|
|
@@ -212,7 +229,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
212
229
|
|
|
213
230
|
expect(response.status).toBe(200);
|
|
214
231
|
expect(response.headers.get("content-type")).toBe("text/event-stream");
|
|
215
|
-
});
|
|
232
|
+
}, 20000);
|
|
216
233
|
});
|
|
217
234
|
|
|
218
235
|
describe("POST /command", () => {
|
|
@@ -230,7 +247,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
230
247
|
});
|
|
231
248
|
|
|
232
249
|
expect(response.status).toBe(401);
|
|
233
|
-
});
|
|
250
|
+
}, 20000);
|
|
234
251
|
|
|
235
252
|
it("returns 400 when run_id does not match active session", async () => {
|
|
236
253
|
await createServer().start();
|
|
@@ -252,7 +269,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
252
269
|
expect(response.status).toBe(400);
|
|
253
270
|
const body = await response.json();
|
|
254
271
|
expect(body.error).toBe("No active session for this run");
|
|
255
|
-
});
|
|
272
|
+
}, 20000);
|
|
256
273
|
|
|
257
274
|
it("accepts structured user_message content", async () => {
|
|
258
275
|
await createServer().start();
|
|
@@ -276,7 +293,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
276
293
|
expect(response.status).toBe(400);
|
|
277
294
|
const body = await response.json();
|
|
278
295
|
expect(body.error).toBe("No active session for this run");
|
|
279
|
-
});
|
|
296
|
+
}, 20000);
|
|
280
297
|
});
|
|
281
298
|
|
|
282
299
|
describe("404 handling", () => {
|
|
@@ -288,7 +305,7 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
288
305
|
|
|
289
306
|
expect(response.status).toBe(404);
|
|
290
307
|
expect(body.error).toBe("Not found");
|
|
291
|
-
});
|
|
308
|
+
}, 20000);
|
|
292
309
|
});
|
|
293
310
|
|
|
294
311
|
describe("getInitialPromptOverride", () => {
|
|
@@ -335,6 +352,48 @@ describe("AgentServer HTTP Mode", () => {
|
|
|
335
352
|
});
|
|
336
353
|
});
|
|
337
354
|
|
|
355
|
+
describe("runtime adapter selection", () => {
|
|
356
|
+
it("defaults to claude when no runtime adapter is configured", () => {
|
|
357
|
+
const s = createServer();
|
|
358
|
+
|
|
359
|
+
expect((s as unknown as TestableServer).getRuntimeAdapter()).toBe(
|
|
360
|
+
"claude",
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it("uses codex when the runtime adapter is configured", () => {
|
|
365
|
+
const s = createServer({ runtimeAdapter: "codex" });
|
|
366
|
+
|
|
367
|
+
expect((s as unknown as TestableServer).getRuntimeAdapter()).toBe(
|
|
368
|
+
"codex",
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("flattens append-style prompts into plain codex instructions", () => {
|
|
373
|
+
const s = createServer({
|
|
374
|
+
claudeCode: {
|
|
375
|
+
systemPrompt: {
|
|
376
|
+
type: "preset",
|
|
377
|
+
preset: "claude_code",
|
|
378
|
+
append: "User codex instructions",
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const sessionPrompt = (
|
|
384
|
+
s as unknown as TestableServer
|
|
385
|
+
).buildSessionSystemPrompt("https://github.com/PostHog/code/pull/1");
|
|
386
|
+
|
|
387
|
+
expect(typeof sessionPrompt).toBe("object");
|
|
388
|
+
expect(
|
|
389
|
+
(s as unknown as TestableServer).buildCodexInstructions(sessionPrompt),
|
|
390
|
+
).toContain("User codex instructions");
|
|
391
|
+
expect(
|
|
392
|
+
(s as unknown as TestableServer).buildCodexInstructions(sessionPrompt),
|
|
393
|
+
).toContain("Cloud Task Execution");
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
338
397
|
describe("detectedPrUrl tracking", () => {
|
|
339
398
|
it("stores PR URL when detectAndAttachPrUrl finds a match", () => {
|
|
340
399
|
const s = createServer();
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../adapters/acp-connection";
|
|
20
20
|
import { selectRecentTurns } from "../adapters/claude/session/jsonl-hydration";
|
|
21
21
|
import type { CodeExecutionMode } from "../execution-mode";
|
|
22
|
+
import { DEFAULT_CODEX_MODEL } from "../gateway-models";
|
|
22
23
|
import { PostHogAPIClient } from "../posthog-api";
|
|
23
24
|
import {
|
|
24
25
|
type ConversationTurn,
|
|
@@ -168,6 +169,20 @@ interface ActiveSession {
|
|
|
168
169
|
hasDesktopConnected: boolean;
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
function getTaskRunStateString(
|
|
173
|
+
taskRun: TaskRun | null,
|
|
174
|
+
key: string,
|
|
175
|
+
): string | null {
|
|
176
|
+
const state = taskRun?.state;
|
|
177
|
+
|
|
178
|
+
if (!state || typeof state !== "object") {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const value = (state as Record<string, unknown>)[key];
|
|
183
|
+
return typeof value === "string" ? value : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
171
186
|
export class AgentServer {
|
|
172
187
|
private config: AgentServerConfig;
|
|
173
188
|
private logger: Logger;
|
|
@@ -242,6 +257,10 @@ export class AgentServer {
|
|
|
242
257
|
this.app = this.createApp();
|
|
243
258
|
}
|
|
244
259
|
|
|
260
|
+
private getRuntimeAdapter(): "claude" | "codex" {
|
|
261
|
+
return this.config.runtimeAdapter ?? "claude";
|
|
262
|
+
}
|
|
263
|
+
|
|
245
264
|
private getEffectiveMode(payload: JwtPayload): AgentMode {
|
|
246
265
|
return payload.mode ?? this.config.mode;
|
|
247
266
|
}
|
|
@@ -702,6 +721,39 @@ export class AgentServer {
|
|
|
702
721
|
|
|
703
722
|
this.configureEnvironment();
|
|
704
723
|
|
|
724
|
+
const [preTaskRun, preTask] = await Promise.all([
|
|
725
|
+
this.posthogAPI
|
|
726
|
+
.getTaskRun(payload.task_id, payload.run_id)
|
|
727
|
+
.catch((err) => {
|
|
728
|
+
this.logger.warn("Failed to fetch task run for session context", {
|
|
729
|
+
taskId: payload.task_id,
|
|
730
|
+
runId: payload.run_id,
|
|
731
|
+
error: err,
|
|
732
|
+
});
|
|
733
|
+
return null;
|
|
734
|
+
}),
|
|
735
|
+
this.posthogAPI.getTask(payload.task_id).catch((err) => {
|
|
736
|
+
this.logger.warn("Failed to fetch task for session context", {
|
|
737
|
+
taskId: payload.task_id,
|
|
738
|
+
error: err,
|
|
739
|
+
});
|
|
740
|
+
return null;
|
|
741
|
+
}),
|
|
742
|
+
]);
|
|
743
|
+
|
|
744
|
+
const prUrl = getTaskRunStateString(preTaskRun, "slack_notified_pr_url");
|
|
745
|
+
|
|
746
|
+
if (prUrl) {
|
|
747
|
+
this.detectedPrUrl = prUrl;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const runtimeAdapter = this.getRuntimeAdapter();
|
|
751
|
+
const sessionSystemPrompt = this.buildSessionSystemPrompt(prUrl);
|
|
752
|
+
const codexInstructions =
|
|
753
|
+
runtimeAdapter === "codex"
|
|
754
|
+
? this.buildCodexInstructions(sessionSystemPrompt)
|
|
755
|
+
: undefined;
|
|
756
|
+
|
|
705
757
|
const posthogAPI = new PostHogAPIClient({
|
|
706
758
|
apiUrl: this.config.apiUrl,
|
|
707
759
|
projectId: this.config.projectId,
|
|
@@ -725,10 +777,23 @@ export class AgentServer {
|
|
|
725
777
|
});
|
|
726
778
|
|
|
727
779
|
const acpConnection = createAcpConnection({
|
|
780
|
+
adapter: runtimeAdapter,
|
|
728
781
|
taskRunId: payload.run_id,
|
|
729
782
|
taskId: payload.task_id,
|
|
730
783
|
deviceType: deviceInfo.type,
|
|
731
784
|
logWriter,
|
|
785
|
+
logger: this.logger,
|
|
786
|
+
codexOptions:
|
|
787
|
+
runtimeAdapter === "codex"
|
|
788
|
+
? {
|
|
789
|
+
cwd: this.config.repositoryPath ?? "/tmp/workspace",
|
|
790
|
+
apiBaseUrl: process.env.OPENAI_BASE_URL,
|
|
791
|
+
apiKey: this.config.apiKey,
|
|
792
|
+
model: this.config.model ?? DEFAULT_CODEX_MODEL,
|
|
793
|
+
reasoningEffort: this.config.reasoningEffort,
|
|
794
|
+
instructions: codexInstructions,
|
|
795
|
+
}
|
|
796
|
+
: undefined,
|
|
732
797
|
onStructuredOutput: async (output) => {
|
|
733
798
|
await this.posthogAPI.setTaskRunOutput(
|
|
734
799
|
payload.task_id,
|
|
@@ -773,37 +838,6 @@ export class AgentServer {
|
|
|
773
838
|
clientCapabilities: {},
|
|
774
839
|
});
|
|
775
840
|
|
|
776
|
-
const [preTaskRun, preTask] = await Promise.all([
|
|
777
|
-
this.posthogAPI
|
|
778
|
-
.getTaskRun(payload.task_id, payload.run_id)
|
|
779
|
-
.catch((err) => {
|
|
780
|
-
this.logger.warn("Failed to fetch task run for session context", {
|
|
781
|
-
taskId: payload.task_id,
|
|
782
|
-
runId: payload.run_id,
|
|
783
|
-
error: err,
|
|
784
|
-
});
|
|
785
|
-
return null;
|
|
786
|
-
}),
|
|
787
|
-
this.posthogAPI.getTask(payload.task_id).catch((err) => {
|
|
788
|
-
this.logger.warn("Failed to fetch task for session context", {
|
|
789
|
-
taskId: payload.task_id,
|
|
790
|
-
error: err,
|
|
791
|
-
});
|
|
792
|
-
return null;
|
|
793
|
-
}),
|
|
794
|
-
]);
|
|
795
|
-
|
|
796
|
-
const prUrl =
|
|
797
|
-
typeof (preTaskRun?.state as Record<string, unknown>)
|
|
798
|
-
?.slack_notified_pr_url === "string"
|
|
799
|
-
? ((preTaskRun?.state as Record<string, unknown>)
|
|
800
|
-
.slack_notified_pr_url as string)
|
|
801
|
-
: null;
|
|
802
|
-
|
|
803
|
-
if (prUrl) {
|
|
804
|
-
this.detectedPrUrl = prUrl;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
841
|
const runState = preTaskRun?.state as Record<string, unknown> | undefined;
|
|
808
842
|
// Cloud runs default to bypassPermissions (auto-approve everything).
|
|
809
843
|
// Only PostHog Code sets initial_permission_mode explicitly (e.g., "plan").
|
|
@@ -811,21 +845,27 @@ export class AgentServer {
|
|
|
811
845
|
typeof runState?.initial_permission_mode === "string"
|
|
812
846
|
? (runState.initial_permission_mode as CodeExecutionMode)
|
|
813
847
|
: "bypassPermissions";
|
|
814
|
-
|
|
815
848
|
const sessionResponse = await clientConnection.newSession({
|
|
816
849
|
cwd: this.config.repositoryPath ?? "/tmp/workspace",
|
|
817
850
|
mcpServers: this.config.mcpServers ?? [],
|
|
818
851
|
_meta: {
|
|
819
852
|
sessionId: payload.run_id,
|
|
820
853
|
taskRunId: payload.run_id,
|
|
821
|
-
systemPrompt:
|
|
854
|
+
systemPrompt: sessionSystemPrompt,
|
|
855
|
+
...(this.config.model && { model: this.config.model }),
|
|
822
856
|
allowedDomains: this.config.allowedDomains,
|
|
823
857
|
jsonSchema: preTask?.json_schema ?? null,
|
|
824
858
|
permissionMode: initialPermissionMode,
|
|
825
859
|
...(this.config.claudeCode?.plugins?.length && {
|
|
826
860
|
claudeCode: {
|
|
827
861
|
options: {
|
|
828
|
-
|
|
862
|
+
...(this.config.claudeCode?.plugins?.length && {
|
|
863
|
+
plugins: this.config.claudeCode.plugins,
|
|
864
|
+
}),
|
|
865
|
+
...(runtimeAdapter === "claude" &&
|
|
866
|
+
this.config.reasoningEffort && {
|
|
867
|
+
effort: this.config.reasoningEffort,
|
|
868
|
+
}),
|
|
829
869
|
},
|
|
830
870
|
},
|
|
831
871
|
}),
|
|
@@ -1197,6 +1237,14 @@ export class AgentServer {
|
|
|
1197
1237
|
return { append: cloudAppend };
|
|
1198
1238
|
}
|
|
1199
1239
|
|
|
1240
|
+
private buildCodexInstructions(
|
|
1241
|
+
systemPrompt: string | { append: string },
|
|
1242
|
+
): string {
|
|
1243
|
+
return typeof systemPrompt === "string"
|
|
1244
|
+
? systemPrompt
|
|
1245
|
+
: systemPrompt.append;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1200
1248
|
private getCloudInteractionOrigin(): string | undefined {
|
|
1201
1249
|
return (
|
|
1202
1250
|
process.env.POSTHOG_CODE_INTERACTION_ORIGIN ??
|
package/src/server/bin.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { z } from "zod/v4";
|
|
4
|
+
import { isSupportedReasoningEffort } from "../adapters/reasoning-effort";
|
|
4
5
|
import { AgentServer } from "./agent-server";
|
|
5
6
|
import { claudeCodeConfigSchema, mcpServersSchema } from "./schemas";
|
|
6
7
|
|
|
@@ -26,6 +27,11 @@ const envSchema = z.object({
|
|
|
26
27
|
})
|
|
27
28
|
.regex(/^\d+$/, "POSTHOG_PROJECT_ID must be a numeric string")
|
|
28
29
|
.transform((val) => parseInt(val, 10)),
|
|
30
|
+
POSTHOG_CODE_RUNTIME_ADAPTER: z.enum(["claude", "codex"]).optional(),
|
|
31
|
+
POSTHOG_CODE_MODEL: z.string().optional(),
|
|
32
|
+
POSTHOG_CODE_REASONING_EFFORT: z
|
|
33
|
+
.enum(["low", "medium", "high", "max"])
|
|
34
|
+
.optional(),
|
|
29
35
|
});
|
|
30
36
|
|
|
31
37
|
const program = new Command();
|
|
@@ -124,6 +130,21 @@ program
|
|
|
124
130
|
.filter(Boolean)
|
|
125
131
|
: undefined;
|
|
126
132
|
|
|
133
|
+
if (
|
|
134
|
+
env.POSTHOG_CODE_RUNTIME_ADAPTER &&
|
|
135
|
+
env.POSTHOG_CODE_MODEL &&
|
|
136
|
+
env.POSTHOG_CODE_REASONING_EFFORT &&
|
|
137
|
+
!isSupportedReasoningEffort(
|
|
138
|
+
env.POSTHOG_CODE_RUNTIME_ADAPTER,
|
|
139
|
+
env.POSTHOG_CODE_MODEL,
|
|
140
|
+
env.POSTHOG_CODE_REASONING_EFFORT,
|
|
141
|
+
)
|
|
142
|
+
) {
|
|
143
|
+
program.error(
|
|
144
|
+
`POSTHOG_CODE_REASONING_EFFORT '${env.POSTHOG_CODE_REASONING_EFFORT}' is not supported for ${env.POSTHOG_CODE_RUNTIME_ADAPTER} model '${env.POSTHOG_CODE_MODEL}'.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
127
148
|
const server = new AgentServer({
|
|
128
149
|
port: parseInt(options.port, 10),
|
|
129
150
|
jwtPublicKey: env.JWT_PUBLIC_KEY,
|
|
@@ -139,6 +160,9 @@ program
|
|
|
139
160
|
baseBranch: options.baseBranch,
|
|
140
161
|
claudeCode,
|
|
141
162
|
allowedDomains,
|
|
163
|
+
runtimeAdapter: env.POSTHOG_CODE_RUNTIME_ADAPTER,
|
|
164
|
+
model: env.POSTHOG_CODE_MODEL,
|
|
165
|
+
reasoningEffort: env.POSTHOG_CODE_REASONING_EFFORT,
|
|
142
166
|
});
|
|
143
167
|
|
|
144
168
|
process.on("SIGINT", async () => {
|
package/src/server/types.ts
CHANGED
|
@@ -20,6 +20,30 @@ export function createPostHogHandlers(options: PostHogHandlersOptions = {}) {
|
|
|
20
20
|
} = options;
|
|
21
21
|
|
|
22
22
|
return [
|
|
23
|
+
// GET local LLM gateway models - session initialization fetches these in the
|
|
24
|
+
// background for command/model metadata.
|
|
25
|
+
http.get("http://localhost:3308/:product/v1/models", () => {
|
|
26
|
+
return HttpResponse.json({
|
|
27
|
+
object: "list",
|
|
28
|
+
data: [
|
|
29
|
+
{
|
|
30
|
+
id: "claude-opus-4-6",
|
|
31
|
+
owned_by: "anthropic",
|
|
32
|
+
context_window: 200000,
|
|
33
|
+
supports_streaming: true,
|
|
34
|
+
supports_vision: true,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "gpt-5.4",
|
|
38
|
+
owned_by: "openai",
|
|
39
|
+
context_window: 200000,
|
|
40
|
+
supports_streaming: true,
|
|
41
|
+
supports_vision: true,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
}),
|
|
46
|
+
|
|
23
47
|
// POST /append_log/ - Agent log entries
|
|
24
48
|
http.post(
|
|
25
49
|
`${baseUrl}/api/projects/:projectId/tasks/:taskId/runs/:runId/append_log/`,
|