@posthog/agent 2.3.326 → 2.3.341

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.
Files changed (55) hide show
  1. package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +9 -0
  2. package/dist/adapters/claude/conversion/tool-use-to-acp.js +15 -1
  3. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
  4. package/dist/adapters/claude/permissions/permission-options.js +18 -11
  5. package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
  6. package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
  7. package/dist/adapters/claude/session/models.d.ts +2 -2
  8. package/dist/adapters/claude/session/models.js +12 -6
  9. package/dist/adapters/claude/session/models.js.map +1 -1
  10. package/dist/adapters/claude/tools.js +15 -13
  11. package/dist/adapters/claude/tools.js.map +1 -1
  12. package/dist/adapters/reasoning-effort.d.ts +1 -1
  13. package/dist/adapters/reasoning-effort.js +11 -5
  14. package/dist/adapters/reasoning-effort.js.map +1 -1
  15. package/dist/agent.d.ts +2 -0
  16. package/dist/agent.js +6946 -587
  17. package/dist/agent.js.map +1 -1
  18. package/dist/execution-mode.d.ts +1 -1
  19. package/dist/execution-mode.js +14 -12
  20. package/dist/execution-mode.js.map +1 -1
  21. package/dist/posthog-api.js +4 -3
  22. package/dist/posthog-api.js.map +1 -1
  23. package/dist/server/agent-server.d.ts +1 -1
  24. package/dist/server/agent-server.js +9260 -2939
  25. package/dist/server/agent-server.js.map +1 -1
  26. package/dist/server/bin.cjs +11289 -4967
  27. package/dist/server/bin.cjs.map +1 -1
  28. package/dist/types.d.ts +8 -0
  29. package/package.json +7 -6
  30. package/src/adapters/acp-connection.ts +14 -1
  31. package/src/adapters/claude/UPSTREAM.md +24 -4
  32. package/src/adapters/claude/claude-agent.ts +161 -14
  33. package/src/adapters/claude/conversion/sdk-to-acp.ts +14 -2
  34. package/src/adapters/claude/conversion/tool-use-to-acp.ts +18 -1
  35. package/src/adapters/claude/hooks.test.ts +189 -0
  36. package/src/adapters/claude/hooks.ts +93 -3
  37. package/src/adapters/claude/permissions/permission-handlers.ts +2 -1
  38. package/src/adapters/claude/permissions/permission-options.ts +5 -0
  39. package/src/adapters/claude/session/models.ts +11 -5
  40. package/src/adapters/claude/session/options.ts +19 -3
  41. package/src/adapters/claude/session/settings.ts +17 -9
  42. package/src/adapters/claude/tools.ts +1 -1
  43. package/src/adapters/claude/types.ts +8 -1
  44. package/src/adapters/codex/codex-agent.ts +15 -2
  45. package/src/adapters/codex/codex-client.test.ts +112 -0
  46. package/src/adapters/codex/codex-client.ts +14 -1
  47. package/src/adapters/reasoning-effort.ts +6 -1
  48. package/src/agent.ts +6 -0
  49. package/src/enrichment/file-enricher.test.ts +163 -0
  50. package/src/enrichment/file-enricher.ts +82 -0
  51. package/src/execution-mode.test.ts +1 -0
  52. package/src/execution-mode.ts +13 -11
  53. package/src/server/bin.ts +1 -1
  54. package/src/server/types.ts +1 -1
  55. package/src/types.ts +6 -0
@@ -0,0 +1,112 @@
1
+ import type {
2
+ AgentSideConnection,
3
+ ReadTextFileRequest,
4
+ ReadTextFileResponse,
5
+ } from "@agentclientprotocol/sdk";
6
+ import { describe, expect, test, vi } from "vitest";
7
+ import type { FileEnrichmentDeps } from "../../enrichment/file-enricher";
8
+ import { Logger } from "../../utils/logger";
9
+
10
+ const enrichFileMock = vi.hoisted(() => vi.fn());
11
+ vi.mock("../../enrichment/file-enricher", () => ({
12
+ enrichFileForAgent: enrichFileMock,
13
+ }));
14
+
15
+ import { createCodexClient } from "./codex-client";
16
+ import { createSessionState } from "./session-state";
17
+
18
+ function makeUpstream(response: ReadTextFileResponse): AgentSideConnection & {
19
+ readTextFile: ReturnType<typeof vi.fn>;
20
+ } {
21
+ const mock = {
22
+ readTextFile: vi.fn(async (_: ReadTextFileRequest) => response),
23
+ writeTextFile: vi.fn(),
24
+ requestPermission: vi.fn(),
25
+ sessionUpdate: vi.fn(),
26
+ createTerminal: vi.fn(),
27
+ terminalOutput: vi.fn(),
28
+ releaseTerminal: vi.fn(),
29
+ waitForTerminalExit: vi.fn(),
30
+ killTerminal: vi.fn(),
31
+ extMethod: vi.fn(),
32
+ extNotification: vi.fn(),
33
+ };
34
+ return mock as unknown as AgentSideConnection & {
35
+ readTextFile: ReturnType<typeof vi.fn>;
36
+ };
37
+ }
38
+
39
+ describe("createCodexClient readTextFile", () => {
40
+ const logger = new Logger({ debug: false, prefix: "[test]" });
41
+ const sessionState = createSessionState("", "/tmp");
42
+
43
+ test("returns upstream response unchanged when enrichmentDeps is absent", async () => {
44
+ enrichFileMock.mockReset();
45
+ const upstream = makeUpstream({ content: "const x = 1;" });
46
+ const client = createCodexClient(upstream, logger, sessionState);
47
+
48
+ const result = await client.readTextFile?.({
49
+ sessionId: "s",
50
+ path: "/tmp/a.ts",
51
+ });
52
+ expect(result?.content).toBe("const x = 1;");
53
+ expect(enrichFileMock).not.toHaveBeenCalled();
54
+ });
55
+
56
+ test("returns enriched content when helper returns a string", async () => {
57
+ enrichFileMock.mockReset();
58
+ enrichFileMock.mockResolvedValueOnce("const x = 1; // [PostHog] Flag ...");
59
+
60
+ const upstream = makeUpstream({ content: "const x = 1;" });
61
+ const deps = {} as FileEnrichmentDeps;
62
+ const client = createCodexClient(upstream, logger, sessionState, {
63
+ enrichmentDeps: deps,
64
+ });
65
+
66
+ const result = await client.readTextFile?.({
67
+ sessionId: "s",
68
+ path: "/tmp/a.ts",
69
+ });
70
+ expect(result?.content).toBe("const x = 1; // [PostHog] Flag ...");
71
+ expect(enrichFileMock).toHaveBeenCalledWith(
72
+ deps,
73
+ "/tmp/a.ts",
74
+ "const x = 1;",
75
+ );
76
+ });
77
+
78
+ test("falls back to upstream response when helper returns null", async () => {
79
+ enrichFileMock.mockReset();
80
+ enrichFileMock.mockResolvedValueOnce(null);
81
+
82
+ const upstream = makeUpstream({ content: "no posthog here" });
83
+ const client = createCodexClient(upstream, logger, sessionState, {
84
+ enrichmentDeps: {} as FileEnrichmentDeps,
85
+ });
86
+
87
+ const result = await client.readTextFile?.({
88
+ sessionId: "s",
89
+ path: "/tmp/a.ts",
90
+ });
91
+ expect(result?.content).toBe("no posthog here");
92
+ });
93
+
94
+ test("calls upstream.readTextFile with original params (UI sees original)", async () => {
95
+ enrichFileMock.mockReset();
96
+ enrichFileMock.mockResolvedValueOnce("enriched");
97
+
98
+ const upstream = makeUpstream({ content: "original" });
99
+ const client = createCodexClient(upstream, logger, sessionState, {
100
+ enrichmentDeps: {} as FileEnrichmentDeps,
101
+ });
102
+
103
+ const params = {
104
+ sessionId: "s",
105
+ path: "/tmp/a.ts",
106
+ line: 10,
107
+ limit: 5,
108
+ };
109
+ await client.readTextFile?.(params);
110
+ expect(upstream.readTextFile).toHaveBeenCalledWith(params);
111
+ });
112
+ });
@@ -29,6 +29,10 @@ import type {
29
29
  WriteTextFileRequest,
30
30
  WriteTextFileResponse,
31
31
  } from "@agentclientprotocol/sdk";
32
+ import {
33
+ enrichFileForAgent,
34
+ type FileEnrichmentDeps,
35
+ } from "../../enrichment/file-enricher";
32
36
  import type { PermissionMode } from "../../execution-mode";
33
37
  import type { Logger } from "../../utils/logger";
34
38
  import type { CodexSessionState } from "./session-state";
@@ -36,6 +40,8 @@ import type { CodexSessionState } from "./session-state";
36
40
  export interface CodexClientCallbacks {
37
41
  /** Called when a usage_update session notification is received */
38
42
  onUsageUpdate?: (update: Record<string, unknown>) => void;
43
+ /** When set, Read responses are annotated with PostHog enrichment before reaching codex-acp. */
44
+ enrichmentDeps?: FileEnrichmentDeps;
39
45
  }
40
46
 
41
47
  const AUTO_APPROVED_KINDS: Record<PermissionMode, Set<ToolKind>> = {
@@ -152,7 +158,14 @@ export function createCodexClient(
152
158
  async readTextFile(
153
159
  params: ReadTextFileRequest,
154
160
  ): Promise<ReadTextFileResponse> {
155
- return upstreamClient.readTextFile(params);
161
+ const response = await upstreamClient.readTextFile(params);
162
+ if (!callbacks?.enrichmentDeps) return response;
163
+ const enriched = await enrichFileForAgent(
164
+ callbacks.enrichmentDeps,
165
+ params.path,
166
+ response.content,
167
+ );
168
+ return enriched ? { ...response, content: enriched } : response;
156
169
  },
157
170
 
158
171
  async writeTextFile(
@@ -3,7 +3,12 @@ import { getReasoningEffortOptions as getCodexReasoningEffortOptions } from "./c
3
3
 
4
4
  export type RuntimeAdapter = "claude" | "codex";
5
5
 
6
- export type SupportedReasoningEffort = "low" | "medium" | "high" | "max";
6
+ export type SupportedReasoningEffort =
7
+ | "low"
8
+ | "medium"
9
+ | "high"
10
+ | "xhigh"
11
+ | "max";
7
12
 
8
13
  export interface ReasoningEffortOption {
9
14
  value: SupportedReasoningEffort;
package/src/agent.ts CHANGED
@@ -19,6 +19,8 @@ export class Agent {
19
19
  private acpConnection?: InProcessAcpConnection;
20
20
  private taskRunId?: string;
21
21
  private sessionLogWriter?: SessionLogWriter;
22
+ private posthogApiConfig?: AgentConfig["posthog"];
23
+ private enricherEnabled: boolean;
22
24
 
23
25
  constructor(config: AgentConfig) {
24
26
  this.logger = new Logger({
@@ -29,7 +31,9 @@ export class Agent {
29
31
 
30
32
  if (config.posthog) {
31
33
  this.posthogAPI = new PostHogAPIClient(config.posthog);
34
+ this.posthogApiConfig = config.posthog;
32
35
  }
36
+ this.enricherEnabled = config.enricher?.enabled !== false;
33
37
 
34
38
  if (config.posthog && !config.skipLogPersistence) {
35
39
  this.sessionLogWriter = new SessionLogWriter({
@@ -121,6 +125,8 @@ export class Agent {
121
125
  processCallbacks: options.processCallbacks,
122
126
  onStructuredOutput: options.onStructuredOutput,
123
127
  allowedModelIds,
128
+ posthogApiConfig: this.posthogApiConfig,
129
+ enricherEnabled: this.enricherEnabled,
124
130
  codexOptions:
125
131
  options.adapter === "codex" && gatewayConfig
126
132
  ? {
@@ -0,0 +1,163 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { enrichFileForAgent, type FileEnrichmentDeps } from "./file-enricher";
3
+
4
+ function makeDeps(overrides: {
5
+ toInlineCommentsReturn?: string;
6
+ callsCount?: number;
7
+ initCallsCount?: number;
8
+ parseRejects?: Error;
9
+ isSupported?: boolean;
10
+ getApiKey?: () => string | Promise<string>;
11
+ }): {
12
+ deps: FileEnrichmentDeps;
13
+ parseSpy: ReturnType<typeof vi.fn>;
14
+ enrichFromApiSpy: ReturnType<typeof vi.fn>;
15
+ getApiKeySpy: ReturnType<typeof vi.fn>;
16
+ } {
17
+ const enrichFromApiSpy = vi.fn(async () => ({
18
+ toInlineComments: () =>
19
+ overrides.toInlineCommentsReturn ?? "enriched content",
20
+ }));
21
+
22
+ const parseSpy = vi.fn(async () => {
23
+ if (overrides.parseRejects) throw overrides.parseRejects;
24
+ return {
25
+ calls: Array.from({ length: overrides.callsCount ?? 1 }),
26
+ initCalls: Array.from({ length: overrides.initCallsCount ?? 0 }),
27
+ enrichFromApi: enrichFromApiSpy,
28
+ };
29
+ });
30
+
31
+ const getApiKeySpy = vi.fn(overrides.getApiKey ?? (() => "phx_test"));
32
+
33
+ const deps: FileEnrichmentDeps = {
34
+ enricher: {
35
+ isSupported: vi.fn(() => overrides.isSupported ?? true),
36
+ parse: parseSpy,
37
+ } as unknown as FileEnrichmentDeps["enricher"],
38
+ apiConfig: {
39
+ apiUrl: "https://test.posthog.com",
40
+ projectId: 1,
41
+ getApiKey: getApiKeySpy,
42
+ },
43
+ };
44
+
45
+ return { deps, parseSpy, enrichFromApiSpy, getApiKeySpy };
46
+ }
47
+
48
+ describe("enrichFileForAgent", () => {
49
+ test("returns null for unsupported extension", async () => {
50
+ const { deps, parseSpy } = makeDeps({});
51
+ const result = await enrichFileForAgent(
52
+ deps,
53
+ "/tmp/notes.txt",
54
+ "some text",
55
+ );
56
+ expect(result).toBeNull();
57
+ expect(parseSpy).not.toHaveBeenCalled();
58
+ });
59
+
60
+ test("returns null for empty content", async () => {
61
+ const { deps, parseSpy } = makeDeps({});
62
+ const result = await enrichFileForAgent(deps, "/tmp/code.ts", "");
63
+ expect(result).toBeNull();
64
+ expect(parseSpy).not.toHaveBeenCalled();
65
+ });
66
+
67
+ test("returns null for content > 1MB", async () => {
68
+ const { deps, parseSpy } = makeDeps({});
69
+ const huge = "x".repeat(1_000_001);
70
+ const result = await enrichFileForAgent(deps, "/tmp/code.ts", huge);
71
+ expect(result).toBeNull();
72
+ expect(parseSpy).not.toHaveBeenCalled();
73
+ });
74
+
75
+ test("returns null when language not supported by enricher", async () => {
76
+ const { deps, parseSpy } = makeDeps({ isSupported: false });
77
+ const result = await enrichFileForAgent(
78
+ deps,
79
+ "/tmp/code.ts",
80
+ "posthog.capture('x');",
81
+ );
82
+ expect(result).toBeNull();
83
+ expect(parseSpy).not.toHaveBeenCalled();
84
+ });
85
+
86
+ test("returns null when no PostHog calls detected", async () => {
87
+ const { deps, enrichFromApiSpy } = makeDeps({
88
+ callsCount: 0,
89
+ initCallsCount: 0,
90
+ });
91
+ const result = await enrichFileForAgent(
92
+ deps,
93
+ "/tmp/code.ts",
94
+ "posthog.capture('x');",
95
+ );
96
+ expect(result).toBeNull();
97
+ expect(enrichFromApiSpy).not.toHaveBeenCalled();
98
+ });
99
+
100
+ test("returns null and skips parse when content has no posthog reference", async () => {
101
+ const { deps, parseSpy } = makeDeps({});
102
+ const result = await enrichFileForAgent(
103
+ deps,
104
+ "/tmp/code.ts",
105
+ "const x = 1;\nfunction foo() {}",
106
+ );
107
+ expect(result).toBeNull();
108
+ expect(parseSpy).not.toHaveBeenCalled();
109
+ });
110
+
111
+ test("returns null when getApiKey yields empty string", async () => {
112
+ const { deps, enrichFromApiSpy } = makeDeps({ getApiKey: () => "" });
113
+ const result = await enrichFileForAgent(
114
+ deps,
115
+ "/tmp/code.ts",
116
+ "posthog.capture('x');",
117
+ );
118
+ expect(result).toBeNull();
119
+ expect(enrichFromApiSpy).not.toHaveBeenCalled();
120
+ });
121
+
122
+ test("returns null when toInlineComments produces no change", async () => {
123
+ const original = "posthog.capture('x');";
124
+ const { deps } = makeDeps({ toInlineCommentsReturn: original });
125
+ const result = await enrichFileForAgent(deps, "/tmp/code.ts", original);
126
+ expect(result).toBeNull();
127
+ });
128
+
129
+ test("returns null and logs debug when enricher throws", async () => {
130
+ const logger = { debug: vi.fn() };
131
+ const { deps } = makeDeps({ parseRejects: new Error("boom") });
132
+ deps.logger = logger as unknown as FileEnrichmentDeps["logger"];
133
+ const result = await enrichFileForAgent(
134
+ deps,
135
+ "/tmp/code.ts",
136
+ "posthog.capture('x');",
137
+ );
138
+ expect(result).toBeNull();
139
+ expect(logger.debug).toHaveBeenCalledWith(
140
+ "File enrichment failed",
141
+ expect.objectContaining({ filePath: "/tmp/code.ts" }),
142
+ );
143
+ });
144
+
145
+ test("returns enriched string when happy path completes", async () => {
146
+ const { deps, enrichFromApiSpy } = makeDeps({
147
+ toInlineCommentsReturn: "posthog.capture('x'); // [PostHog] Event: \"x\"",
148
+ });
149
+ const result = await enrichFileForAgent(
150
+ deps,
151
+ "/tmp/code.ts",
152
+ "posthog.capture('x');",
153
+ );
154
+ expect(result).toBe("posthog.capture('x'); // [PostHog] Event: \"x\"");
155
+ expect(enrichFromApiSpy).toHaveBeenCalledWith(
156
+ expect.objectContaining({
157
+ apiKey: "phx_test",
158
+ host: "https://test.posthog.com",
159
+ projectId: 1,
160
+ }),
161
+ );
162
+ });
163
+ });
@@ -0,0 +1,82 @@
1
+ import * as path from "node:path";
2
+ import { EXT_TO_LANG_ID, PostHogEnricher } from "@posthog/enricher";
3
+ import type { PostHogAPIConfig } from "../types";
4
+ import type { Logger } from "../utils/logger";
5
+
6
+ export interface FileEnrichmentDeps {
7
+ enricher: PostHogEnricher;
8
+ apiConfig: PostHogAPIConfig;
9
+ logger?: Logger;
10
+ }
11
+
12
+ export interface Enrichment {
13
+ deps: FileEnrichmentDeps;
14
+ dispose(): void;
15
+ }
16
+
17
+ export function createEnrichment(
18
+ apiConfig: PostHogAPIConfig | undefined,
19
+ logger?: Logger,
20
+ ): Enrichment | undefined {
21
+ if (!apiConfig) return undefined;
22
+ const enricher = new PostHogEnricher();
23
+ return {
24
+ deps: { enricher, apiConfig, logger },
25
+ dispose: () => enricher.dispose(),
26
+ };
27
+ }
28
+
29
+ const MAX_ENRICHMENT_BYTES = 1_000_000;
30
+
31
+ export async function enrichFileForAgent(
32
+ deps: FileEnrichmentDeps,
33
+ filePath: string,
34
+ content: string,
35
+ ): Promise<string | null> {
36
+ if (!content || content.length > MAX_ENRICHMENT_BYTES) return null;
37
+
38
+ // Skip the tree-sitter parse for files with no PostHog references.
39
+ if (!/posthog/i.test(content)) return null;
40
+
41
+ const ext = path.extname(filePath).toLowerCase();
42
+ const langId = EXT_TO_LANG_ID[ext];
43
+ if (!langId || !deps.enricher.isSupported(langId)) return null;
44
+
45
+ try {
46
+ const parsed = await deps.enricher.parse(content, langId);
47
+ if (parsed.calls.length === 0 && parsed.initCalls.length === 0) {
48
+ return null;
49
+ }
50
+
51
+ const apiKey = await deps.apiConfig.getApiKey();
52
+ if (!apiKey) return null;
53
+
54
+ const enriched = await parsed.enrichFromApi({
55
+ apiKey,
56
+ host: deps.apiConfig.apiUrl,
57
+ projectId: deps.apiConfig.projectId,
58
+ timeoutMs: 5_000,
59
+ });
60
+
61
+ const annotated = enriched.toInlineComments();
62
+ if (annotated === content) {
63
+ deps.logger?.debug("File enrichment produced no changes", {
64
+ filePath,
65
+ calls: parsed.calls.length,
66
+ });
67
+ return null;
68
+ }
69
+ deps.logger?.debug("File enriched", {
70
+ filePath,
71
+ calls: parsed.calls.length,
72
+ });
73
+ return annotated;
74
+ } catch (err) {
75
+ const detail =
76
+ err instanceof Error
77
+ ? { message: err.message, name: err.name, stack: err.stack }
78
+ : { value: String(err) };
79
+ deps.logger?.debug("File enrichment failed", { filePath, ...detail });
80
+ return null;
81
+ }
82
+ }
@@ -8,6 +8,7 @@ describe("execution modes", () => {
8
8
  "acceptEdits",
9
9
  "plan",
10
10
  "bypassPermissions",
11
+ "auto",
11
12
  ]);
12
13
  });
13
14
 
@@ -25,19 +25,21 @@ const availableModes: ModeInfo[] = [
25
25
  name: "Plan Mode",
26
26
  description: "Planning mode, no actual tool execution",
27
27
  },
28
- // {
29
- // id: "dontAsk",
30
- // name: "Don't Ask",
31
- // description: "Don't prompt for permissions, deny if not pre-approved",
32
- // },
33
28
  ];
34
29
 
35
30
  if (ALLOW_BYPASS) {
36
- availableModes.push({
37
- id: "bypassPermissions",
38
- name: "Auto-accept Permissions",
39
- description: "Auto-accept all permission requests",
40
- });
31
+ availableModes.push(
32
+ {
33
+ id: "bypassPermissions",
34
+ name: "Bypass Permissions",
35
+ description: "Auto-accept all permission requests",
36
+ },
37
+ {
38
+ id: "auto",
39
+ name: "Auto Mode",
40
+ description: "Use a model classifier to approve/deny permission prompts",
41
+ },
42
+ );
41
43
  }
42
44
 
43
45
  // Expose execution mode IDs in type-safe order for type checks
@@ -45,8 +47,8 @@ export const CODE_EXECUTION_MODES = [
45
47
  "default",
46
48
  "acceptEdits",
47
49
  "plan",
48
- // "dontAsk",
49
50
  "bypassPermissions",
51
+ "auto",
50
52
  ] as const;
51
53
 
52
54
  export type CodeExecutionMode = (typeof CODE_EXECUTION_MODES)[number];
package/src/server/bin.ts CHANGED
@@ -30,7 +30,7 @@ const envSchema = z.object({
30
30
  POSTHOG_CODE_RUNTIME_ADAPTER: z.enum(["claude", "codex"]).optional(),
31
31
  POSTHOG_CODE_MODEL: z.string().optional(),
32
32
  POSTHOG_CODE_REASONING_EFFORT: z
33
- .enum(["low", "medium", "high", "max"])
33
+ .enum(["low", "medium", "high", "xhigh", "max"])
34
34
  .optional(),
35
35
  });
36
36
 
@@ -26,5 +26,5 @@ export interface AgentServerConfig {
26
26
  allowedDomains?: string[];
27
27
  runtimeAdapter?: "claude" | "codex";
28
28
  model?: string;
29
- reasoningEffort?: "low" | "medium" | "high" | "max";
29
+ reasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max";
30
30
  }
package/src/types.ts CHANGED
@@ -152,6 +152,12 @@ export interface AgentConfig {
152
152
  skipLogPersistence?: boolean;
153
153
  /** Local cache path for instant log loading (e.g., ~/.posthog-code) */
154
154
  localCachePath?: string;
155
+ /**
156
+ * Annotate files the agent reads with PostHog enrichment (event volume,
157
+ * flag rollout/staleness, experiment links). Defaults to enabled when
158
+ * `posthog` config is present; set `{ enabled: false }` to opt out.
159
+ */
160
+ enricher?: { enabled?: boolean };
155
161
  debug?: boolean;
156
162
  onLog?: OnLogCallback;
157
163
  }