@posthog/agent 2.3.261 → 2.3.267
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/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 +16 -2
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/index.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 +17 -0
- package/dist/server/agent-server.js +199 -28
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +335 -128
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +7 -3
- package/src/acp-extensions.ts +3 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +11 -5
- 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/server/agent-server.test.ts +70 -11
- package/src/server/agent-server.ts +257 -37
- package/src/server/bin.ts +24 -0
- package/src/server/schemas.test.ts +52 -0
- package/src/server/schemas.ts +16 -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.267",
|
|
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"
|
|
@@ -82,8 +86,8 @@
|
|
|
82
86
|
"tsx": "^4.20.6",
|
|
83
87
|
"typescript": "^5.5.0",
|
|
84
88
|
"vitest": "^2.1.8",
|
|
85
|
-
"@posthog/
|
|
86
|
-
"@posthog/
|
|
89
|
+
"@posthog/shared": "1.0.0",
|
|
90
|
+
"@posthog/git": "1.0.0"
|
|
87
91
|
},
|
|
88
92
|
"dependencies": {
|
|
89
93
|
"@agentclientprotocol/sdk": "0.16.1",
|
package/src/acp-extensions.ts
CHANGED
|
@@ -63,6 +63,9 @@ export const POSTHOG_NOTIFICATIONS = {
|
|
|
63
63
|
|
|
64
64
|
/** Token usage update for a session turn */
|
|
65
65
|
USAGE_UPDATE: "_posthog/usage_update",
|
|
66
|
+
|
|
67
|
+
/** Response to a relayed permission request (plan approval, question) */
|
|
68
|
+
PERMISSION_RESPONSE: "_posthog/permission_response",
|
|
66
69
|
} as const;
|
|
67
70
|
|
|
68
71
|
type NotificationMethod =
|
|
@@ -490,11 +490,17 @@ export async function canUseTool(
|
|
|
490
490
|
return planFileResult;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
|
|
493
|
+
// In plan mode, deny tools that aren't in the allowed set. The agent must
|
|
494
|
+
// write its plan to ~/.claude/plans/ and call ExitPlanMode before it can
|
|
495
|
+
// use write or bash tools. Without this guard, cloud runs auto-approve
|
|
496
|
+
// restricted tools and the agent skips planning entirely.
|
|
497
|
+
if (session.permissionMode === "plan") {
|
|
498
|
+
const message =
|
|
499
|
+
"This tool is not available in plan mode. Write your plan " +
|
|
500
|
+
`to a file in ${getClaudePlansDir()} and call ExitPlanMode when ready.`;
|
|
501
|
+
await emitToolDenial(context, message);
|
|
502
|
+
return { behavior: "deny", message, interrupt: false };
|
|
503
|
+
}
|
|
498
504
|
|
|
499
505
|
return handleDefaultPermissionFlow(context);
|
|
500
506
|
}
|
|
@@ -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
|
+
}
|
|
@@ -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();
|