@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.
- package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +9 -0
- package/dist/adapters/claude/conversion/tool-use-to-acp.js +15 -1
- package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/permissions/permission-options.js +18 -11
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
- package/dist/adapters/claude/session/models.d.ts +2 -2
- package/dist/adapters/claude/session/models.js +12 -6
- package/dist/adapters/claude/session/models.js.map +1 -1
- package/dist/adapters/claude/tools.js +15 -13
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/adapters/reasoning-effort.d.ts +1 -1
- package/dist/adapters/reasoning-effort.js +11 -5
- package/dist/adapters/reasoning-effort.js.map +1 -1
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +6946 -587
- package/dist/agent.js.map +1 -1
- package/dist/execution-mode.d.ts +1 -1
- package/dist/execution-mode.js +14 -12
- package/dist/execution-mode.js.map +1 -1
- package/dist/posthog-api.js +4 -3
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +1 -1
- package/dist/server/agent-server.js +9260 -2939
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +11289 -4967
- package/dist/server/bin.cjs.map +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +7 -6
- package/src/adapters/acp-connection.ts +14 -1
- package/src/adapters/claude/UPSTREAM.md +24 -4
- package/src/adapters/claude/claude-agent.ts +161 -14
- package/src/adapters/claude/conversion/sdk-to-acp.ts +14 -2
- package/src/adapters/claude/conversion/tool-use-to-acp.ts +18 -1
- package/src/adapters/claude/hooks.test.ts +189 -0
- package/src/adapters/claude/hooks.ts +93 -3
- package/src/adapters/claude/permissions/permission-handlers.ts +2 -1
- package/src/adapters/claude/permissions/permission-options.ts +5 -0
- package/src/adapters/claude/session/models.ts +11 -5
- package/src/adapters/claude/session/options.ts +19 -3
- package/src/adapters/claude/session/settings.ts +17 -9
- package/src/adapters/claude/tools.ts +1 -1
- package/src/adapters/claude/types.ts +8 -1
- package/src/adapters/codex/codex-agent.ts +15 -2
- package/src/adapters/codex/codex-client.test.ts +112 -0
- package/src/adapters/codex/codex-client.ts +14 -1
- package/src/adapters/reasoning-effort.ts +6 -1
- package/src/agent.ts +6 -0
- package/src/enrichment/file-enricher.test.ts +163 -0
- package/src/enrichment/file-enricher.ts +82 -0
- package/src/execution-mode.test.ts +1 -0
- package/src/execution-mode.ts +13 -11
- package/src/server/bin.ts +1 -1
- package/src/server/types.ts +1 -1
- 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
|
-
|
|
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 =
|
|
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
|
+
}
|
package/src/execution-mode.ts
CHANGED
|
@@ -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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
package/src/server/types.ts
CHANGED
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
|
}
|