@posthog/agent 2.1.45 → 2.1.47
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-DcBmoTR4.d.ts → agent-BJ7Uacyp.d.ts} +2 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +140 -56
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +265 -183
- package/dist/index.js.map +1 -1
- package/dist/server/agent-server.js +264 -183
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +264 -183
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/acp-connection.ts +6 -1
- package/src/adapters/claude/claude-agent.ts +105 -60
- package/src/agent.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/agent",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.47",
|
|
4
4
|
"repository": "https://github.com/PostHog/twig",
|
|
5
5
|
"description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
6
6
|
"exports": {
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"tsx": "^4.20.6",
|
|
73
73
|
"typescript": "^5.5.0",
|
|
74
74
|
"vitest": "^2.1.8",
|
|
75
|
-
"@
|
|
76
|
-
"@
|
|
75
|
+
"@posthog/shared": "1.0.0",
|
|
76
|
+
"@twig/git": "1.0.0"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
79
|
"@agentclientprotocol/sdk": "^0.14.0",
|
|
@@ -23,6 +23,8 @@ export type AcpConnectionConfig = {
|
|
|
23
23
|
/** Deployment environment - "local" for desktop, "cloud" for cloud sandbox */
|
|
24
24
|
deviceType?: "local" | "cloud";
|
|
25
25
|
logger?: Logger;
|
|
26
|
+
/** Enable dev-only instrumentation (timing, verbose logging) */
|
|
27
|
+
debug?: boolean;
|
|
26
28
|
processCallbacks?: ProcessSpawnedCallback;
|
|
27
29
|
codexOptions?: CodexProcessOptions;
|
|
28
30
|
allowedModelIds?: Set<string>;
|
|
@@ -194,7 +196,10 @@ function createClaudeConnection(config: AcpConnectionConfig): AcpConnection {
|
|
|
194
196
|
|
|
195
197
|
let agent: ClaudeAcpAgent | null = null;
|
|
196
198
|
const agentConnection = new AgentSideConnection((client) => {
|
|
197
|
-
agent = new ClaudeAcpAgent(client, logWriter,
|
|
199
|
+
agent = new ClaudeAcpAgent(client, logWriter, {
|
|
200
|
+
...config.processCallbacks,
|
|
201
|
+
debug: config.debug,
|
|
202
|
+
});
|
|
198
203
|
logger.info(`Created ${agent.adapterName} agent`);
|
|
199
204
|
return agent;
|
|
200
205
|
}, agentStream);
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
type SDKMessage,
|
|
30
30
|
type SDKUserMessage,
|
|
31
31
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
32
|
+
import { createTimingCollector } from "@posthog/shared";
|
|
32
33
|
import { v7 as uuidv7 } from "uuid";
|
|
33
34
|
import packageJson from "../../../package.json" with { type: "json" };
|
|
34
35
|
import type { SessionContext } from "../../otel-log-writer.js";
|
|
@@ -69,6 +70,8 @@ import type {
|
|
|
69
70
|
export interface ClaudeAcpAgentOptions {
|
|
70
71
|
onProcessSpawned?: (info: ProcessSpawnedInfo) => void;
|
|
71
72
|
onProcessExited?: (pid: number) => void;
|
|
73
|
+
/** Enable dev-only instrumentation (timing, verbose logging) */
|
|
74
|
+
debug?: boolean;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
@@ -78,17 +81,19 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
78
81
|
backgroundTerminals: { [key: string]: BackgroundTerminal } = {};
|
|
79
82
|
clientCapabilities?: ClientCapabilities;
|
|
80
83
|
private logWriter?: SessionLogWriter;
|
|
81
|
-
private
|
|
84
|
+
private options?: ClaudeAcpAgentOptions;
|
|
82
85
|
private lastSentConfigOptions?: SessionConfigOption[];
|
|
86
|
+
private debug: boolean;
|
|
83
87
|
|
|
84
88
|
constructor(
|
|
85
89
|
client: AgentSideConnection,
|
|
86
90
|
logWriter?: SessionLogWriter,
|
|
87
|
-
|
|
91
|
+
options?: ClaudeAcpAgentOptions,
|
|
88
92
|
) {
|
|
89
93
|
super(client);
|
|
90
94
|
this.logWriter = logWriter;
|
|
91
|
-
this.
|
|
95
|
+
this.options = options;
|
|
96
|
+
this.debug = options?.debug ?? false;
|
|
92
97
|
this.toolUseCache = {};
|
|
93
98
|
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
94
99
|
}
|
|
@@ -136,6 +141,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
136
141
|
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
|
137
142
|
this.checkAuthStatus();
|
|
138
143
|
|
|
144
|
+
const tc = createTimingCollector(this.debug, (msg, data) =>
|
|
145
|
+
this.logger.info(msg, data),
|
|
146
|
+
);
|
|
147
|
+
|
|
139
148
|
const meta = params._meta as NewSessionMeta | undefined;
|
|
140
149
|
const sessionId = uuidv7();
|
|
141
150
|
const permissionMode: TwigExecutionMode =
|
|
@@ -144,29 +153,29 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
144
153
|
? (meta.permissionMode as TwigExecutionMode)
|
|
145
154
|
: "default";
|
|
146
155
|
|
|
147
|
-
const mcpServers = parseMcpServers(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// used later during permission checks, not needed by buildSessionOptions or query()
|
|
151
|
-
const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
|
|
156
|
+
const mcpServers = tc.timeSync("parseMcpServers", () =>
|
|
157
|
+
parseMcpServers(params),
|
|
158
|
+
);
|
|
152
159
|
|
|
153
|
-
const options = buildSessionOptions(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
160
|
+
const options = tc.timeSync("buildSessionOptions", () =>
|
|
161
|
+
buildSessionOptions({
|
|
162
|
+
cwd: params.cwd,
|
|
163
|
+
mcpServers,
|
|
164
|
+
permissionMode,
|
|
165
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
166
|
+
logger: this.logger,
|
|
167
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
168
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
169
|
+
sessionId,
|
|
170
|
+
isResume: false,
|
|
171
|
+
onModeChange: this.createOnModeChange(sessionId),
|
|
172
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
173
|
+
onProcessExited: this.options?.onProcessExited,
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
167
176
|
|
|
168
177
|
const input = new Pushable<SDKUserMessage>();
|
|
169
|
-
const q = query({ prompt: input, options });
|
|
178
|
+
const q = tc.timeSync("sdkQuery", () => query({ prompt: input, options }));
|
|
170
179
|
|
|
171
180
|
const session = this.createSession(
|
|
172
181
|
sessionId,
|
|
@@ -180,28 +189,36 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
180
189
|
this.registerPersistence(sessionId, meta as Record<string, unknown>);
|
|
181
190
|
|
|
182
191
|
if (meta?.taskRunId) {
|
|
183
|
-
await
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
await tc.time("extNotification", () =>
|
|
193
|
+
this.client.extNotification("_posthog/sdk_session", {
|
|
194
|
+
taskRunId: meta.taskRunId!,
|
|
195
|
+
sessionId,
|
|
196
|
+
adapter: "claude",
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
188
199
|
}
|
|
189
200
|
|
|
190
|
-
//
|
|
191
|
-
|
|
201
|
+
// Only await model config — slash commands and MCP metadata are deferred
|
|
202
|
+
// since they're not needed to return configOptions to the client.
|
|
203
|
+
const modelOptions = await tc.time("fetchModels", () =>
|
|
192
204
|
this.getModelConfigOptions(),
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Deferred: slash commands + MCP metadata (not needed to return configOptions)
|
|
208
|
+
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
196
209
|
|
|
197
210
|
session.modelId = modelOptions.currentModelId;
|
|
198
211
|
await this.trySetModel(q, modelOptions.currentModelId);
|
|
199
212
|
|
|
200
|
-
|
|
213
|
+
const configOptions = await tc.time("buildConfigOptions", () =>
|
|
214
|
+
this.buildConfigOptions(modelOptions),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
tc.summarize("newSession");
|
|
201
218
|
|
|
202
219
|
return {
|
|
203
220
|
sessionId,
|
|
204
|
-
configOptions
|
|
221
|
+
configOptions,
|
|
205
222
|
};
|
|
206
223
|
}
|
|
207
224
|
|
|
@@ -212,6 +229,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
212
229
|
async resumeSession(
|
|
213
230
|
params: LoadSessionRequest,
|
|
214
231
|
): Promise<LoadSessionResponse> {
|
|
232
|
+
const tc = createTimingCollector(this.debug, (msg, data) =>
|
|
233
|
+
this.logger.info(msg, data),
|
|
234
|
+
);
|
|
235
|
+
|
|
215
236
|
const meta = params._meta as NewSessionMeta | undefined;
|
|
216
237
|
const sessionId = meta?.sessionId;
|
|
217
238
|
if (!sessionId) {
|
|
@@ -221,10 +242,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
221
242
|
return {};
|
|
222
243
|
}
|
|
223
244
|
|
|
224
|
-
const mcpServers = parseMcpServers(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
|
|
245
|
+
const mcpServers = tc.timeSync("parseMcpServers", () =>
|
|
246
|
+
parseMcpServers(params),
|
|
247
|
+
);
|
|
228
248
|
|
|
229
249
|
const permissionMode: TwigExecutionMode =
|
|
230
250
|
meta?.permissionMode &&
|
|
@@ -232,32 +252,33 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
232
252
|
? (meta.permissionMode as TwigExecutionMode)
|
|
233
253
|
: "default";
|
|
234
254
|
|
|
235
|
-
const { query: q, session } = await
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
255
|
+
const { query: q, session } = await tc.time("initializeQuery", () =>
|
|
256
|
+
this.initializeQuery({
|
|
257
|
+
cwd: params.cwd,
|
|
258
|
+
permissionMode,
|
|
259
|
+
mcpServers,
|
|
260
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
261
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
262
|
+
sessionId,
|
|
263
|
+
isResume: true,
|
|
264
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
245
267
|
|
|
246
268
|
session.taskRunId = meta?.taskRunId;
|
|
247
269
|
|
|
248
270
|
this.registerPersistence(sessionId, meta as Record<string, unknown>);
|
|
249
271
|
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
getAvailableSlashCommands(q),
|
|
253
|
-
mcpMetadataPromise,
|
|
254
|
-
]);
|
|
272
|
+
// Deferred: slash commands + MCP metadata (not needed to return configOptions)
|
|
273
|
+
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
255
274
|
|
|
256
|
-
|
|
275
|
+
const configOptions = await tc.time("buildConfigOptions", () =>
|
|
276
|
+
this.buildConfigOptions(),
|
|
277
|
+
);
|
|
257
278
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
279
|
+
tc.summarize("resumeSession");
|
|
280
|
+
|
|
281
|
+
return { configOptions };
|
|
261
282
|
}
|
|
262
283
|
|
|
263
284
|
async prompt(params: PromptRequest): Promise<PromptResponse> {
|
|
@@ -366,8 +387,8 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
366
387
|
isResume: config.isResume,
|
|
367
388
|
additionalDirectories: config.additionalDirectories,
|
|
368
389
|
onModeChange: this.createOnModeChange(config.sessionId),
|
|
369
|
-
onProcessSpawned: this.
|
|
370
|
-
onProcessExited: this.
|
|
390
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
391
|
+
onProcessExited: this.options?.onProcessExited,
|
|
371
392
|
});
|
|
372
393
|
|
|
373
394
|
const q = query({ prompt: input, options });
|
|
@@ -503,6 +524,30 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
503
524
|
}
|
|
504
525
|
}
|
|
505
526
|
|
|
527
|
+
/**
|
|
528
|
+
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
529
|
+
* Both populate caches used later — neither is needed to return configOptions.
|
|
530
|
+
*/
|
|
531
|
+
private deferBackgroundFetches(
|
|
532
|
+
tc: ReturnType<typeof createTimingCollector>,
|
|
533
|
+
q: Query,
|
|
534
|
+
sessionId: string,
|
|
535
|
+
mcpServers: ReturnType<typeof parseMcpServers>,
|
|
536
|
+
): void {
|
|
537
|
+
Promise.all([
|
|
538
|
+
tc.time("slashCommands", () => getAvailableSlashCommands(q)),
|
|
539
|
+
tc.time("mcpMetadata", () =>
|
|
540
|
+
fetchMcpToolMetadata(mcpServers, this.logger),
|
|
541
|
+
),
|
|
542
|
+
])
|
|
543
|
+
.then(([slashCommands]) => {
|
|
544
|
+
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
545
|
+
})
|
|
546
|
+
.catch((err) => {
|
|
547
|
+
this.logger.warn("Failed to fetch deferred session data", { err });
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
|
|
506
551
|
private registerPersistence(
|
|
507
552
|
sessionId: string,
|
|
508
553
|
meta: Record<string, unknown> | undefined,
|