@posthog/agent 1.1.0 → 1.3.0

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 (39) hide show
  1. package/CLAUDE.md +68 -35
  2. package/README.md +55 -14
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/src/agent.d.ts +5 -1
  6. package/dist/src/agent.d.ts.map +1 -1
  7. package/dist/src/agent.js +65 -11
  8. package/dist/src/agent.js.map +1 -1
  9. package/dist/src/event-transformer.d.ts +2 -0
  10. package/dist/src/event-transformer.d.ts.map +1 -1
  11. package/dist/src/event-transformer.js +53 -5
  12. package/dist/src/event-transformer.js.map +1 -1
  13. package/dist/src/posthog-api.d.ts +34 -0
  14. package/dist/src/posthog-api.d.ts.map +1 -1
  15. package/dist/src/posthog-api.js +38 -0
  16. package/dist/src/posthog-api.js.map +1 -1
  17. package/dist/src/stage-executor.d.ts +5 -2
  18. package/dist/src/stage-executor.d.ts.map +1 -1
  19. package/dist/src/stage-executor.js +20 -12
  20. package/dist/src/stage-executor.js.map +1 -1
  21. package/dist/src/task-progress-reporter.d.ts +44 -0
  22. package/dist/src/task-progress-reporter.d.ts.map +1 -0
  23. package/dist/src/task-progress-reporter.js +234 -0
  24. package/dist/src/task-progress-reporter.js.map +1 -0
  25. package/dist/src/types.d.ts +20 -0
  26. package/dist/src/types.d.ts.map +1 -1
  27. package/dist/src/types.js.map +1 -1
  28. package/package.json +2 -1
  29. package/src/agent.ts +77 -11
  30. package/src/event-transformer.ts +61 -7
  31. package/src/posthog-api.ts +79 -0
  32. package/src/stage-executor.ts +29 -15
  33. package/src/task-progress-reporter.ts +287 -0
  34. package/src/types.ts +30 -2
  35. package/dist/src/utils/mcp.d.ts +0 -10
  36. package/dist/src/utils/mcp.d.ts.map +0 -1
  37. package/dist/src/utils/mcp.js +0 -18
  38. package/dist/src/utils/mcp.js.map +0 -1
  39. package/src/utils/mcp.ts +0 -15
package/src/agent.ts CHANGED
@@ -14,6 +14,7 @@ import { AgentRegistry } from './agent-registry.js';
14
14
  import { WorkflowRegistry } from './workflow-registry.js';
15
15
  import { StageExecutor } from './stage-executor.js';
16
16
  import { PromptBuilder } from './prompt-builder.js';
17
+ import { TaskProgressReporter } from './task-progress-reporter.js';
17
18
 
18
19
  export class Agent {
19
20
  private workingDirectory: string;
@@ -28,12 +29,39 @@ export class Agent {
28
29
  private agentRegistry: AgentRegistry;
29
30
  private workflowRegistry: WorkflowRegistry;
30
31
  private stageExecutor: StageExecutor;
32
+ private progressReporter: TaskProgressReporter;
33
+ private mcpServers?: Record<string, any>;
31
34
  public debug: boolean;
32
35
 
33
36
  constructor(config: AgentConfig = {}) {
34
37
  this.workingDirectory = config.workingDirectory || process.cwd();
35
38
  this.onEvent = config.onEvent;
36
39
  this.debug = config.debug || false;
40
+
41
+ // Build default PostHog MCP server configuration
42
+ const posthogMcpUrl = config.posthogMcpUrl
43
+ || process.env.POSTHOG_MCP_URL
44
+ || 'https://mcp.posthog.com/mcp';
45
+
46
+ // Add auth if API key provided
47
+ const headers: Record<string, string> = {};
48
+ if (config.posthogApiKey) {
49
+ headers['Authorization'] = `Bearer ${config.posthogApiKey}`;
50
+ }
51
+
52
+ const defaultMcpServers = {
53
+ posthog: {
54
+ type: 'http' as const,
55
+ url: posthogMcpUrl,
56
+ ...(Object.keys(headers).length > 0 ? { headers } : {}),
57
+ }
58
+ };
59
+
60
+ // Merge default PostHog MCP with user-provided servers (user config takes precedence)
61
+ this.mcpServers = {
62
+ ...defaultMcpServers,
63
+ ...config.mcpServers
64
+ };
37
65
  this.logger = new Logger({ debug: this.debug, prefix: '[PostHog Agent]' });
38
66
  this.taskManager = new TaskManager();
39
67
  this.eventTransformer = new EventTransformer();
@@ -63,7 +91,15 @@ export class Agent {
63
91
  generatePlanTemplate: (vars) => this.templateManager.generatePlan(vars),
64
92
  logger: this.logger.child('PromptBuilder')
65
93
  });
66
- this.stageExecutor = new StageExecutor(this.agentRegistry, this.logger, promptBuilder);
94
+ this.stageExecutor = new StageExecutor(
95
+ this.agentRegistry,
96
+ this.logger,
97
+ promptBuilder,
98
+ undefined, // eventHandler set via setEventHandler below
99
+ this.mcpServers
100
+ );
101
+ this.stageExecutor.setEventHandler((event) => this.emitEvent(event));
102
+ this.progressReporter = new TaskProgressReporter(this.posthogAPI, this.logger);
67
103
  }
68
104
 
69
105
  /**
@@ -82,6 +118,7 @@ export class Agent {
82
118
  if (!workflow) {
83
119
  throw new Error(`Workflow ${workflowId} not found`);
84
120
  }
121
+ const orderedStages = [...workflow.stages].sort((a, b) => a.position - b.position);
85
122
 
86
123
  // Ensure task is assigned to workflow and positioned at first stage
87
124
  if (this.posthogAPI) {
@@ -103,9 +140,13 @@ export class Agent {
103
140
  const executionId = this.taskManager.generateExecutionId();
104
141
  this.logger.info('Starting workflow execution', { taskId: task.id, workflowId, executionId });
105
142
  this.taskManager.startExecution(task.id, 'plan_and_build', executionId);
143
+ await this.progressReporter.start(task.id, {
144
+ workflowId,
145
+ workflowRunId: executionId,
146
+ totalSteps: orderedStages.length,
147
+ });
106
148
 
107
149
  try {
108
- const orderedStages = [...workflow.stages].sort((a, b) => a.position - b.position);
109
150
  let startIndex = 0;
110
151
  const currentStageId = (task as any).current_stage as string | undefined;
111
152
 
@@ -114,7 +155,10 @@ export class Agent {
114
155
  const currIdx = orderedStages.findIndex(s => s.id === currentStageId);
115
156
  const atLastStage = currIdx >= 0 && currIdx === orderedStages.length - 1;
116
157
  if (atLastStage) {
117
- this.emitEvent(this.eventTransformer.createStatusEvent('no_next_stage', { stage: orderedStages[currIdx].key }));
158
+ const finalStageKey = orderedStages[currIdx]?.key;
159
+ this.emitEvent(this.eventTransformer.createStatusEvent('no_next_stage', { stage: finalStageKey }));
160
+ await this.progressReporter.noNextStage(finalStageKey);
161
+ await this.progressReporter.complete();
118
162
  this.taskManager.completeExecution(executionId, { task, workflow });
119
163
  return { task, workflow };
120
164
  }
@@ -135,7 +179,9 @@ export class Agent {
135
179
 
136
180
  for (let i = startIndex; i < orderedStages.length; i++) {
137
181
  const stage = orderedStages[i];
182
+ await this.progressReporter.stageStarted(stage.key, i);
138
183
  await this.executeStage(task, stage, options);
184
+ await this.progressReporter.stageCompleted(stage.key, i + 1);
139
185
  if (options.autoProgress) {
140
186
  const hasNext = i < orderedStages.length - 1;
141
187
  if (hasNext) {
@@ -143,9 +189,11 @@ export class Agent {
143
189
  }
144
190
  }
145
191
  }
192
+ await this.progressReporter.complete();
146
193
  this.taskManager.completeExecution(executionId, { task, workflow });
147
194
  return { task, workflow };
148
195
  } catch (error) {
196
+ await this.progressReporter.fail(error as Error);
149
197
  this.taskManager.failExecution(executionId, error as Error);
150
198
  throw error;
151
199
  }
@@ -167,10 +215,12 @@ export class Agent {
167
215
  const planningBranch = await this.createPlanningBranch(task.id);
168
216
  await this.updateTaskBranch(task.id, planningBranch);
169
217
  this.emitEvent(this.eventTransformer.createStatusEvent('branch_created', { stage: stage.key, branch: planningBranch }));
218
+ await this.progressReporter.branchCreated(stage.key, planningBranch);
170
219
  } else if (!isPlanning && !isManual && shouldCreateImplBranch) {
171
220
  const implBranch = await this.createImplementationBranch(task.id);
172
221
  await this.updateTaskBranch(task.id, implBranch);
173
222
  this.emitEvent(this.eventTransformer.createStatusEvent('branch_created', { stage: stage.key, branch: implBranch }));
223
+ await this.progressReporter.branchCreated(stage.key, implBranch);
174
224
  }
175
225
 
176
226
  const result = await this.stageExecutor.execute(task, stage, options);
@@ -179,6 +229,7 @@ export class Agent {
179
229
  await this.writePlan(task.id, result.plan);
180
230
  await this.commitPlan(task.id, task.title);
181
231
  this.emitEvent(this.eventTransformer.createStatusEvent('commit_made', { stage: stage.key, kind: 'plan' }));
232
+ await this.progressReporter.commitMade(stage.key, 'plan');
182
233
  }
183
234
 
184
235
  if (isManual) {
@@ -193,12 +244,14 @@ export class Agent {
193
244
  await this.updateTaskBranch(task.id, implBranch);
194
245
  branchName = implBranch;
195
246
  this.emitEvent(this.eventTransformer.createStatusEvent('branch_created', { stage: stage.key, branch: implBranch }));
247
+ await this.progressReporter.branchCreated(stage.key, implBranch);
196
248
  }
197
249
  try {
198
250
  const prUrl = await this.createPullRequest(task.id, branchName, task.title, task.description);
199
251
  await this.updateTaskBranch(task.id, branchName);
200
- await this.attachPullRequestToTask(task.id, prUrl);
252
+ await this.attachPullRequestToTask(task.id, prUrl, branchName);
201
253
  this.emitEvent(this.eventTransformer.createStatusEvent('pr_created', { stage: stage.key, prUrl }));
254
+ await this.progressReporter.pullRequestCreated(stage.key, prUrl);
202
255
  } catch {}
203
256
  }
204
257
  // Do not auto-progress on manual stages
@@ -211,6 +264,7 @@ export class Agent {
211
264
  const planSummary = existingPlan ? existingPlan.split('\n')[0] : undefined;
212
265
  await this.commitImplementation(task.id, task.title, planSummary);
213
266
  this.emitEvent(this.eventTransformer.createStatusEvent('commit_made', { stage: stage.key, kind: 'implementation' }));
267
+ await this.progressReporter.commitMade(stage.key, 'implementation');
214
268
  }
215
269
 
216
270
  // PR creation on complete stage (or if explicitly requested), regardless of whether edits occurred
@@ -222,8 +276,9 @@ export class Agent {
222
276
  try {
223
277
  const prUrl = await this.createPullRequest(task.id, branchName, task.title, task.description);
224
278
  await this.updateTaskBranch(task.id, branchName);
225
- await this.attachPullRequestToTask(task.id, prUrl);
279
+ await this.attachPullRequestToTask(task.id, prUrl, branchName);
226
280
  this.emitEvent(this.eventTransformer.createStatusEvent('pr_created', { stage: stage.key, prUrl }));
281
+ await this.progressReporter.pullRequestCreated(stage.key, prUrl);
227
282
  } catch {}
228
283
  }
229
284
  }
@@ -239,10 +294,11 @@ export class Agent {
239
294
  // Direct prompt execution - still supported for low-level usage
240
295
  async run(prompt: string, options: { repositoryPath?: string; permissionMode?: import('./types.js').PermissionMode; queryOverrides?: Record<string, any> } = {}): Promise<ExecutionResult> {
241
296
  const baseOptions: Record<string, any> = {
242
- model: "claude-4-5-sonnet",
297
+ model: "claude-sonnet-4-5-20250929",
243
298
  cwd: options.repositoryPath || this.workingDirectory,
244
299
  permissionMode: (options.permissionMode as any) || "default",
245
300
  settingSources: ["local"],
301
+ mcpServers: this.mcpServers,
246
302
  };
247
303
 
248
304
  const response = query({
@@ -252,7 +308,7 @@ export class Agent {
252
308
 
253
309
  const results = [];
254
310
  for await (const message of response) {
255
- this.logger.debug('Received message in direct run', { type: message.type });
311
+ this.logger.debug('Received message in direct run', message);
256
312
  const transformedEvent = this.eventTransformer.transform(message);
257
313
  this.onEvent?.(transformedEvent);
258
314
  results.push(message);
@@ -271,6 +327,10 @@ export class Agent {
271
327
  }
272
328
  return this.posthogAPI.fetchTask(taskId);
273
329
  }
330
+
331
+ getPostHogClient(): PostHogAPIClient | undefined {
332
+ return this.posthogAPI;
333
+ }
274
334
 
275
335
  async listTasks(filters?: {
276
336
  repository?: string;
@@ -367,8 +427,8 @@ Generated by PostHog Agent`;
367
427
  return prUrl;
368
428
  }
369
429
 
370
- async attachPullRequestToTask(taskId: string, prUrl: string): Promise<void> {
371
- this.logger.info('Attaching PR to task', { taskId, prUrl });
430
+ async attachPullRequestToTask(taskId: string, prUrl: string, branchName?: string): Promise<void> {
431
+ this.logger.info('Attaching PR to task', { taskId, prUrl, branchName });
372
432
 
373
433
  if (!this.posthogAPI) {
374
434
  const error = new Error('PostHog API not configured. Cannot attach PR to task.');
@@ -376,7 +436,7 @@ Generated by PostHog Agent`;
376
436
  throw error;
377
437
  }
378
438
 
379
- await this.posthogAPI.updateTask(taskId, { github_pr_url: prUrl });
439
+ await this.posthogAPI.attachTaskPullRequest(taskId, prUrl, branchName);
380
440
  this.logger.debug('PR attached to task', { taskId, prUrl });
381
441
  }
382
442
 
@@ -389,7 +449,7 @@ Generated by PostHog Agent`;
389
449
  throw error;
390
450
  }
391
451
 
392
- await this.posthogAPI.updateTask(taskId, { github_branch: branchName });
452
+ await this.posthogAPI.setTaskBranch(taskId, branchName);
393
453
  this.logger.debug('Task branch updated', { taskId, branchName });
394
454
  }
395
455
 
@@ -419,6 +479,12 @@ Generated by PostHog Agent`;
419
479
  // Log all events except tokens (too verbose)
420
480
  this.logger.debug('Emitting event', { type: event.type, ts: event.ts });
421
481
  }
482
+ const persistPromise = this.progressReporter.recordEvent(event);
483
+ if (persistPromise && typeof persistPromise.then === 'function') {
484
+ persistPromise.catch((error: Error) =>
485
+ this.logger.debug('Failed to persist agent event', { message: error.message })
486
+ );
487
+ }
422
488
  this.onEvent?.(event);
423
489
  }
424
490
  }
@@ -118,15 +118,14 @@ export class EventTransformer {
118
118
 
119
119
  // Handle user messages
120
120
  if (sdkMessage.type === 'user') {
121
- const content = sdkMessage.message.content;
122
- const textContent = Array.isArray(content)
123
- ? content.find(c => c.type === 'text')?.text
124
- : typeof content === 'string' ? content : '';
125
-
121
+ const textContent = this.extractUserContent(sdkMessage.message?.content);
122
+ if (!textContent) {
123
+ return null;
124
+ }
126
125
  return {
127
126
  ...baseEvent,
128
127
  type: 'user_message',
129
- content: textContent || '',
128
+ content: textContent,
130
129
  isSynthetic: sdkMessage.isSynthetic
131
130
  };
132
131
  }
@@ -186,4 +185,59 @@ export class EventTransformer {
186
185
  ...additionalData
187
186
  };
188
187
  }
189
- }
188
+
189
+ private extractUserContent(content: unknown): string | null {
190
+ if (!content) {
191
+ return null;
192
+ }
193
+
194
+ if (typeof content === 'string') {
195
+ const trimmed = content.trim();
196
+ return trimmed.length > 0 ? trimmed : null;
197
+ }
198
+
199
+ if (Array.isArray(content)) {
200
+ const parts: string[] = [];
201
+ for (const block of content) {
202
+ const extracted = this.extractUserContent(block);
203
+ if (extracted) {
204
+ parts.push(extracted);
205
+ } else if (block && typeof block === 'object') {
206
+ const candidate = this.extractFromObject(block as Record<string, unknown>);
207
+ if (candidate) {
208
+ parts.push(candidate);
209
+ }
210
+ }
211
+ }
212
+ const text = parts.join('\n').trim();
213
+ return text.length > 0 ? text : null;
214
+ }
215
+
216
+ if (typeof content === 'object') {
217
+ return this.extractFromObject(content as Record<string, unknown>);
218
+ }
219
+
220
+ return null;
221
+ }
222
+
223
+ private extractFromObject(value: Record<string, unknown>): string | null {
224
+ const preferredKeys = ['text', 'input_text', 'input', 'markdown', 'content', 'message'];
225
+ for (const key of preferredKeys) {
226
+ if (typeof value[key] === 'string') {
227
+ const trimmed = (value[key] as string).trim();
228
+ if (trimmed.length > 0) {
229
+ return trimmed;
230
+ }
231
+ }
232
+ }
233
+
234
+ for (const entry of Object.values(value)) {
235
+ const extracted = this.extractUserContent(entry);
236
+ if (extracted) {
237
+ return extracted;
238
+ }
239
+ }
240
+
241
+ return null;
242
+ }
243
+ }
@@ -26,6 +26,36 @@ interface TaskProgressResponse {
26
26
  message?: string;
27
27
  }
28
28
 
29
+ export interface TaskProgressRecord {
30
+ id: string;
31
+ task: string;
32
+ status: "started" | "in_progress" | "completed" | "failed";
33
+ current_step?: string | null;
34
+ completed_steps?: number | null;
35
+ total_steps?: number | null;
36
+ progress_percentage?: number | null;
37
+ output_log?: string | null;
38
+ error_message?: string | null;
39
+ workflow_id?: string | null;
40
+ workflow_run_id?: string | null;
41
+ activity_id?: string | null;
42
+ created_at: string;
43
+ updated_at: string;
44
+ completed_at?: string | null;
45
+ }
46
+
47
+ export interface TaskProgressUpdate {
48
+ status?: TaskProgressRecord["status"];
49
+ current_step?: string | null;
50
+ completed_steps?: number | null;
51
+ total_steps?: number | null;
52
+ output_log?: string | null;
53
+ error_message?: string | null;
54
+ workflow_id?: string | null;
55
+ workflow_run_id?: string | null;
56
+ activity_id?: string | null;
57
+ }
58
+
29
59
  export class PostHogAPIClient {
30
60
  private config: PostHogAPIConfig;
31
61
  private _teamId: number | null = null;
@@ -137,11 +167,60 @@ export class PostHogAPIClient {
137
167
  });
138
168
  }
139
169
 
170
+ async setTaskBranch(taskId: string, branch: string): Promise<Task> {
171
+ const teamId = await this.getTeamId();
172
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/set_branch/`, {
173
+ method: "POST",
174
+ body: JSON.stringify({ branch }),
175
+ });
176
+ }
177
+
178
+ async attachTaskPullRequest(taskId: string, prUrl: string, branch?: string): Promise<Task> {
179
+ const teamId = await this.getTeamId();
180
+ const payload: Record<string, string> = { pr_url: prUrl };
181
+ if (branch) {
182
+ payload.branch = branch;
183
+ }
184
+ return this.apiRequest<Task>(`/api/projects/${teamId}/tasks/${taskId}/attach_pr/`, {
185
+ method: "POST",
186
+ body: JSON.stringify(payload),
187
+ });
188
+ }
189
+
140
190
  async getTaskProgress(taskId: string): Promise<TaskProgressResponse> {
141
191
  const teamId = await this.getTeamId();
142
192
  return this.apiRequest<TaskProgressResponse>(`/api/projects/${teamId}/tasks/${taskId}/progress/`);
143
193
  }
144
194
 
195
+ async createTaskProgress(
196
+ taskId: string,
197
+ payload: TaskProgressUpdate & { status: TaskProgressRecord["status"] }
198
+ ): Promise<TaskProgressRecord> {
199
+ const teamId = await this.getTeamId();
200
+ return this.apiRequest<TaskProgressRecord>(`/api/projects/${teamId}/task_progress/`, {
201
+ method: "POST",
202
+ body: JSON.stringify({
203
+ ...payload,
204
+ task: taskId,
205
+ }),
206
+ });
207
+ }
208
+
209
+ async updateTaskProgress(
210
+ taskId: string,
211
+ progressId: string,
212
+ payload: TaskProgressUpdate
213
+ ): Promise<TaskProgressRecord> {
214
+ const teamId = await this.getTeamId();
215
+ return this.apiRequest<TaskProgressRecord>(`/api/projects/${teamId}/task_progress/${progressId}/`, {
216
+ method: "PATCH",
217
+ body: JSON.stringify({
218
+ ...payload,
219
+ task: taskId,
220
+ }),
221
+ });
222
+ }
223
+
145
224
  // Workflow endpoints
146
225
  async fetchWorkflow(workflowId: string): Promise<WorkflowDefinition> {
147
226
  const teamId = await this.getTeamId();
@@ -2,20 +2,27 @@ import { query } from '@anthropic-ai/claude-agent-sdk';
2
2
  import { Logger } from './utils/logger.js';
3
3
  import { EventTransformer } from './event-transformer.js';
4
4
  import { AgentRegistry } from './agent-registry.js';
5
- import type { Task } from './types.js';
5
+ import type { AgentEvent, Task, McpServerConfig } from './types.js';
6
6
  import type { WorkflowStage, WorkflowStageExecutionResult, WorkflowExecutionOptions } from './workflow-types.js';
7
7
  import { PLANNING_SYSTEM_PROMPT } from './agents/planning.js';
8
8
  import { EXECUTION_SYSTEM_PROMPT } from './agents/execution.js';
9
9
  import { PromptBuilder } from './prompt-builder.js';
10
- import { POSTHOG_MCP } from './utils/mcp.js';
11
10
 
12
11
  export class StageExecutor {
13
12
  private registry: AgentRegistry;
14
13
  private logger: Logger;
15
14
  private eventTransformer: EventTransformer;
16
15
  private promptBuilder: PromptBuilder;
17
-
18
- constructor(registry: AgentRegistry, logger: Logger, promptBuilder?: PromptBuilder) {
16
+ private eventHandler?: (event: AgentEvent) => void;
17
+ private mcpServers?: Record<string, McpServerConfig>;
18
+
19
+ constructor(
20
+ registry: AgentRegistry,
21
+ logger: Logger,
22
+ promptBuilder?: PromptBuilder,
23
+ eventHandler?: (event: AgentEvent) => void,
24
+ mcpServers?: Record<string, McpServerConfig>,
25
+ ) {
19
26
  this.registry = registry;
20
27
  this.logger = logger.child('StageExecutor');
21
28
  this.eventTransformer = new EventTransformer();
@@ -24,6 +31,12 @@ export class StageExecutor {
24
31
  generatePlanTemplate: async () => '',
25
32
  logger,
26
33
  });
34
+ this.eventHandler = eventHandler;
35
+ this.mcpServers = mcpServers;
36
+ }
37
+
38
+ setEventHandler(handler?: (event: AgentEvent) => void): void {
39
+ this.eventHandler = handler;
27
40
  }
28
41
 
29
42
  async execute(task: Task, stage: WorkflowStage, options: WorkflowExecutionOptions): Promise<WorkflowStageExecutionResult> {
@@ -72,9 +85,7 @@ export class StageExecutor {
72
85
  cwd,
73
86
  permissionMode: 'plan',
74
87
  settingSources: ['local'],
75
- mcpServers: {
76
- ...POSTHOG_MCP
77
- }
88
+ mcpServers: this.mcpServers
78
89
  };
79
90
 
80
91
  const response = query({
@@ -85,8 +96,11 @@ export class StageExecutor {
85
96
  let plan = '';
86
97
  for await (const message of response) {
87
98
  const transformed = this.eventTransformer.transform(message);
88
- if (transformed && transformed.type !== 'token') {
89
- this.logger.debug('Planning event', { type: transformed.type });
99
+ if (transformed) {
100
+ if (transformed.type !== 'token') {
101
+ this.logger.debug('Planning event', { type: transformed.type });
102
+ }
103
+ this.eventHandler?.(transformed);
90
104
  }
91
105
  if (message.type === 'assistant' && message.message?.content) {
92
106
  for (const c of message.message.content) {
@@ -113,9 +127,7 @@ export class StageExecutor {
113
127
  cwd,
114
128
  permissionMode,
115
129
  settingSources: ['local'],
116
- mcpServers: {
117
- ...POSTHOG_MCP
118
- }
130
+ mcpServers: this.mcpServers
119
131
  };
120
132
 
121
133
  const response = query({
@@ -125,12 +137,14 @@ export class StageExecutor {
125
137
  const results: any[] = [];
126
138
  for await (const message of response) {
127
139
  const transformed = this.eventTransformer.transform(message);
128
- if (transformed && transformed.type !== 'token') {
129
- this.logger.debug('Execution event', { type: transformed.type });
140
+ if (transformed) {
141
+ if (transformed.type !== 'token') {
142
+ this.logger.debug('Execution event', { type: transformed.type });
143
+ }
144
+ this.eventHandler?.(transformed);
130
145
  }
131
146
  results.push(message);
132
147
  }
133
148
  return { results };
134
149
  }
135
150
  }
136
-