@posthog/agent 2.3.213 → 2.3.233
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/agent.js +20 -21
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +3 -0
- package/dist/server/agent-server.js +9294 -9241
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +9258 -9205
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/codex/codex-agent.ts +6 -3
- package/src/adapters/codex/settings.ts +18 -18
- package/src/adapters/codex/spawn.ts +9 -0
- package/src/server/agent-server.ts +53 -0
package/package.json
CHANGED
|
@@ -108,9 +108,15 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
108
108
|
super(client);
|
|
109
109
|
this.logger = new Logger({ debug: true, prefix: "[CodexAcpAgent]" });
|
|
110
110
|
|
|
111
|
+
// Load user codex settings before spawning so spawnCodexProcess can
|
|
112
|
+
// filter out any [mcp_servers.*] entries from ~/.codex/config.toml.
|
|
113
|
+
const cwd = options.codexProcessOptions.cwd ?? process.cwd();
|
|
114
|
+
const settingsManager = new CodexSettingsManager(cwd);
|
|
115
|
+
|
|
111
116
|
// Spawn the codex-acp subprocess
|
|
112
117
|
this.codexProcess = spawnCodexProcess({
|
|
113
118
|
...options.codexProcessOptions,
|
|
119
|
+
settings: settingsManager.getSettings(),
|
|
114
120
|
logger: this.logger,
|
|
115
121
|
processCallbacks: options.processCallbacks,
|
|
116
122
|
});
|
|
@@ -120,9 +126,6 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
120
126
|
const codexWritable = nodeWritableToWebWritable(this.codexProcess.stdin);
|
|
121
127
|
const codexStream = ndJsonStream(codexWritable, codexReadable);
|
|
122
128
|
|
|
123
|
-
// Set up session with CodexSettingsManager
|
|
124
|
-
const cwd = options.codexProcessOptions.cwd ?? process.cwd();
|
|
125
|
-
const settingsManager = new CodexSettingsManager(cwd);
|
|
126
129
|
const abortController = new AbortController();
|
|
127
130
|
this.session = {
|
|
128
131
|
abortController,
|
|
@@ -13,6 +13,8 @@ export interface CodexSettings {
|
|
|
13
13
|
personality?: string;
|
|
14
14
|
modelReasoningEffort?: string;
|
|
15
15
|
trustLevel?: string;
|
|
16
|
+
// Names of every `[mcp_servers.<name>]` section declared in the user's config.toml
|
|
17
|
+
mcpServerNames: string[];
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -24,32 +26,29 @@ export interface CodexSettings {
|
|
|
24
26
|
*/
|
|
25
27
|
export class CodexSettingsManager {
|
|
26
28
|
private cwd: string;
|
|
27
|
-
private settings: CodexSettings = {};
|
|
28
|
-
private initialized = false;
|
|
29
|
+
private settings: CodexSettings = { mcpServerNames: [] };
|
|
29
30
|
|
|
30
31
|
constructor(cwd: string) {
|
|
31
32
|
this.cwd = cwd;
|
|
33
|
+
this.loadSettings();
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async initialize(): Promise<void> {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
await this.loadSettings();
|
|
39
|
-
this.initialized = true;
|
|
37
|
+
// No-op: settings are loaded in the constructor. Kept async to
|
|
38
|
+
// satisfy the BaseSettingsManager interface.
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
private getConfigPath(): string {
|
|
43
42
|
return path.join(os.homedir(), ".codex", "config.toml");
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
private
|
|
45
|
+
private loadSettings(): void {
|
|
47
46
|
const configPath = this.getConfigPath();
|
|
48
47
|
try {
|
|
49
|
-
const content =
|
|
48
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
50
49
|
this.settings = parseCodexToml(content, this.cwd);
|
|
51
50
|
} catch {
|
|
52
|
-
this.settings = {};
|
|
51
|
+
this.settings = { mcpServerNames: [] };
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -62,17 +61,13 @@ export class CodexSettingsManager {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
async setCwd(cwd: string): Promise<void> {
|
|
65
|
-
if (this.cwd === cwd)
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
this.dispose();
|
|
64
|
+
if (this.cwd === cwd) return;
|
|
69
65
|
this.cwd = cwd;
|
|
70
|
-
this.
|
|
71
|
-
await this.initialize();
|
|
66
|
+
this.loadSettings();
|
|
72
67
|
}
|
|
73
68
|
|
|
74
69
|
dispose(): void {
|
|
75
|
-
|
|
70
|
+
// No-op: no resources to release. Kept to satisfy the BaseSettingsManager interface.
|
|
76
71
|
}
|
|
77
72
|
}
|
|
78
73
|
|
|
@@ -82,7 +77,8 @@ export class CodexSettingsManager {
|
|
|
82
77
|
* Does NOT handle full TOML spec — only what codex config uses.
|
|
83
78
|
*/
|
|
84
79
|
function parseCodexToml(content: string, cwd: string): CodexSettings {
|
|
85
|
-
const settings: CodexSettings = {};
|
|
80
|
+
const settings: CodexSettings = { mcpServerNames: [] };
|
|
81
|
+
const mcpServerNames = new Set<string>();
|
|
86
82
|
let currentSection = "";
|
|
87
83
|
|
|
88
84
|
for (const line of content.split("\n")) {
|
|
@@ -93,6 +89,9 @@ function parseCodexToml(content: string, cwd: string): CodexSettings {
|
|
|
93
89
|
const sectionMatch = trimmed.match(/^\[(.+)\]$/);
|
|
94
90
|
if (sectionMatch) {
|
|
95
91
|
currentSection = sectionMatch[1] ?? "";
|
|
92
|
+
if (currentSection.startsWith("mcp_servers.")) {
|
|
93
|
+
mcpServerNames.add(currentSection.slice("mcp_servers.".length));
|
|
94
|
+
}
|
|
96
95
|
continue;
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -123,5 +122,6 @@ function parseCodexToml(content: string, cwd: string): CodexSettings {
|
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
|
|
125
|
+
settings.mcpServerNames = Array.from(mcpServerNames);
|
|
126
126
|
return settings;
|
|
127
127
|
}
|
|
@@ -4,6 +4,7 @@ import { delimiter, dirname } from "node:path";
|
|
|
4
4
|
import type { Readable, Writable } from "node:stream";
|
|
5
5
|
import type { ProcessSpawnedCallback } from "../../types";
|
|
6
6
|
import { Logger } from "../../utils/logger";
|
|
7
|
+
import type { CodexSettings } from "./settings";
|
|
7
8
|
|
|
8
9
|
export interface CodexProcessOptions {
|
|
9
10
|
cwd?: string;
|
|
@@ -14,6 +15,7 @@ export interface CodexProcessOptions {
|
|
|
14
15
|
binaryPath?: string;
|
|
15
16
|
logger?: Logger;
|
|
16
17
|
processCallbacks?: ProcessSpawnedCallback;
|
|
18
|
+
settings?: CodexSettings;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface CodexProcess {
|
|
@@ -28,6 +30,13 @@ function buildConfigArgs(options: CodexProcessOptions): string[] {
|
|
|
28
30
|
|
|
29
31
|
args.push("-c", `features.remote_models=false`);
|
|
30
32
|
|
|
33
|
+
// Disable the user's local MCPs one-by-one so Codex only uses the MCPs we
|
|
34
|
+
// provide via ACP. We can't use `-c mcp_servers={}` because that makes Codex
|
|
35
|
+
// ignore MCPs entirely, including the ones we inject later.
|
|
36
|
+
for (const name of options.settings?.mcpServerNames ?? []) {
|
|
37
|
+
args.push("-c", `mcp_servers.${name}.enabled=false`);
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
if (options.apiBaseUrl) {
|
|
32
41
|
args.push("-c", `model_provider="posthog"`);
|
|
33
42
|
args.push("-c", `model_providers.posthog.name="PostHog Gateway"`);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
PROTOCOL_VERSION,
|
|
6
6
|
} from "@agentclientprotocol/sdk";
|
|
7
7
|
import { type ServerType, serve } from "@hono/node-server";
|
|
8
|
+
import { getCurrentBranch } from "@posthog/git/queries";
|
|
8
9
|
import { Hono } from "hono";
|
|
9
10
|
import packageJson from "../../package.json" with { type: "json" };
|
|
10
11
|
import { POSTHOG_NOTIFICATIONS } from "../acp-extensions";
|
|
@@ -167,6 +168,7 @@ export class AgentServer {
|
|
|
167
168
|
private posthogAPI: PostHogAPIClient;
|
|
168
169
|
private questionRelayedToSlack = false;
|
|
169
170
|
private detectedPrUrl: string | null = null;
|
|
171
|
+
private lastReportedBranch: string | null = null;
|
|
170
172
|
private resumeState: ResumeState | null = null;
|
|
171
173
|
// Guards against concurrent session initialization. autoInitializeSession() and
|
|
172
174
|
// the GET /events SSE handler can both call initializeSession() — the SSE connection
|
|
@@ -524,6 +526,10 @@ export class AgentServer {
|
|
|
524
526
|
stopReason: result.stopReason,
|
|
525
527
|
});
|
|
526
528
|
|
|
529
|
+
if (result.stopReason === "end_turn") {
|
|
530
|
+
void this.syncCloudBranchMetadata(this.session.payload);
|
|
531
|
+
}
|
|
532
|
+
|
|
527
533
|
this.broadcastTurnComplete(result.stopReason);
|
|
528
534
|
|
|
529
535
|
if (result.stopReason === "end_turn") {
|
|
@@ -879,6 +885,10 @@ export class AgentServer {
|
|
|
879
885
|
stopReason: result.stopReason,
|
|
880
886
|
});
|
|
881
887
|
|
|
888
|
+
if (result.stopReason === "end_turn") {
|
|
889
|
+
void this.syncCloudBranchMetadata(payload);
|
|
890
|
+
}
|
|
891
|
+
|
|
882
892
|
this.broadcastTurnComplete(result.stopReason);
|
|
883
893
|
|
|
884
894
|
if (result.stopReason === "end_turn") {
|
|
@@ -964,6 +974,10 @@ export class AgentServer {
|
|
|
964
974
|
stopReason: result.stopReason,
|
|
965
975
|
});
|
|
966
976
|
|
|
977
|
+
if (result.stopReason === "end_turn") {
|
|
978
|
+
void this.syncCloudBranchMetadata(payload);
|
|
979
|
+
}
|
|
980
|
+
|
|
967
981
|
this.broadcastTurnComplete(result.stopReason);
|
|
968
982
|
|
|
969
983
|
if (result.stopReason === "end_turn") {
|
|
@@ -1168,6 +1182,44 @@ ${attributionInstructions}
|
|
|
1168
1182
|
`;
|
|
1169
1183
|
}
|
|
1170
1184
|
|
|
1185
|
+
private async getCurrentGitBranch(): Promise<string | null> {
|
|
1186
|
+
if (!this.config.repositoryPath) {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
try {
|
|
1191
|
+
return await getCurrentBranch(this.config.repositoryPath);
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
this.logger.warn("Failed to determine current git branch", {
|
|
1194
|
+
repositoryPath: this.config.repositoryPath,
|
|
1195
|
+
error,
|
|
1196
|
+
});
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
private async syncCloudBranchMetadata(payload: JwtPayload): Promise<void> {
|
|
1202
|
+
const branchName = await this.getCurrentGitBranch();
|
|
1203
|
+
if (!branchName || branchName === this.lastReportedBranch) {
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
try {
|
|
1208
|
+
await this.posthogAPI.updateTaskRun(payload.task_id, payload.run_id, {
|
|
1209
|
+
branch: branchName,
|
|
1210
|
+
output: { head_branch: branchName },
|
|
1211
|
+
});
|
|
1212
|
+
this.lastReportedBranch = branchName;
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
this.logger.warn("Failed to attach current branch to task run", {
|
|
1215
|
+
taskId: payload.task_id,
|
|
1216
|
+
runId: payload.run_id,
|
|
1217
|
+
branchName,
|
|
1218
|
+
error,
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1171
1223
|
private async signalTaskComplete(
|
|
1172
1224
|
payload: JwtPayload,
|
|
1173
1225
|
stopReason: string,
|
|
@@ -1556,6 +1608,7 @@ ${attributionInstructions}
|
|
|
1556
1608
|
}
|
|
1557
1609
|
|
|
1558
1610
|
this.pendingEvents = [];
|
|
1611
|
+
this.lastReportedBranch = null;
|
|
1559
1612
|
this.session = null;
|
|
1560
1613
|
}
|
|
1561
1614
|
|