@posthog/agent 2.3.216 → 2.3.256

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.3.216",
3
+ "version": "2.3.256",
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": {
@@ -496,7 +496,7 @@ export async function hydrateSessionJsonl(params: {
496
496
  permissionMode?: string;
497
497
  posthogAPI: PostHogAPIClient;
498
498
  log: HydrationLog;
499
- }): Promise<void> {
499
+ }): Promise<boolean> {
500
500
  const { posthogAPI, log } = params;
501
501
 
502
502
  try {
@@ -506,7 +506,7 @@ export async function hydrateSessionJsonl(params: {
506
506
  log.info("Local JSONL exists, skipping S3 hydration", {
507
507
  sessionId: params.sessionId,
508
508
  });
509
- return;
509
+ return true;
510
510
  } catch {
511
511
  // File doesn't exist, proceed with hydration
512
512
  }
@@ -514,13 +514,13 @@ export async function hydrateSessionJsonl(params: {
514
514
  const taskRun = await posthogAPI.getTaskRun(params.taskId, params.runId);
515
515
  if (!taskRun.log_url) {
516
516
  log.info("No log URL, skipping JSONL hydration");
517
- return;
517
+ return false;
518
518
  }
519
519
 
520
520
  const entries = await posthogAPI.fetchTaskRunLogs(taskRun);
521
521
  if (entries.length === 0) {
522
522
  log.info("No S3 log entries, skipping JSONL hydration");
523
- return;
523
+ return false;
524
524
  }
525
525
 
526
526
  const entryCounts: Record<string, number> = {};
@@ -545,7 +545,7 @@ export async function hydrateSessionJsonl(params: {
545
545
  const allTurns = rebuildConversation(entries);
546
546
  if (allTurns.length === 0) {
547
547
  log.info("No conversation in S3 logs, skipping JSONL hydration");
548
- return;
548
+ return false;
549
549
  }
550
550
 
551
551
  const maxTokens = supports1MContext(params.model ?? "")
@@ -577,10 +577,12 @@ export async function hydrateSessionJsonl(params: {
577
577
  turns: conversation.length,
578
578
  lines: jsonlLines.length,
579
579
  });
580
+ return true;
580
581
  } catch (err) {
581
582
  log.warn("Failed to hydrate session JSONL, continuing", {
582
583
  sessionId: params.sessionId,
583
584
  error: err instanceof Error ? err.message : String(err),
584
585
  });
586
+ return false;
585
587
  }
586
588
  }
@@ -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
- if (this.initialized) {
36
- return;
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 async loadSettings(): Promise<void> {
45
+ private loadSettings(): void {
47
46
  const configPath = this.getConfigPath();
48
47
  try {
49
- const content = await fs.promises.readFile(configPath, "utf-8");
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.initialized = false;
71
- await this.initialize();
66
+ this.loadSettings();
72
67
  }
73
68
 
74
69
  dispose(): void {
75
- this.initialized = false;
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"`);