@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.
- package/CLAUDE.md +68 -35
- package/README.md +55 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/src/agent.d.ts +5 -1
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +65 -11
- package/dist/src/agent.js.map +1 -1
- package/dist/src/event-transformer.d.ts +2 -0
- package/dist/src/event-transformer.d.ts.map +1 -1
- package/dist/src/event-transformer.js +53 -5
- package/dist/src/event-transformer.js.map +1 -1
- package/dist/src/posthog-api.d.ts +34 -0
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +38 -0
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/stage-executor.d.ts +5 -2
- package/dist/src/stage-executor.d.ts.map +1 -1
- package/dist/src/stage-executor.js +20 -12
- package/dist/src/stage-executor.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +44 -0
- package/dist/src/task-progress-reporter.d.ts.map +1 -0
- package/dist/src/task-progress-reporter.js +234 -0
- package/dist/src/task-progress-reporter.js.map +1 -0
- package/dist/src/types.d.ts +20 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +2 -1
- package/src/agent.ts +77 -11
- package/src/event-transformer.ts +61 -7
- package/src/posthog-api.ts +79 -0
- package/src/stage-executor.ts +29 -15
- package/src/task-progress-reporter.ts +287 -0
- package/src/types.ts +30 -2
- package/dist/src/utils/mcp.d.ts +0 -10
- package/dist/src/utils/mcp.d.ts.map +0 -1
- package/dist/src/utils/mcp.js +0 -18
- package/dist/src/utils/mcp.js.map +0 -1
- 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(
|
|
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
|
-
|
|
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-
|
|
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',
|
|
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.
|
|
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.
|
|
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
|
}
|
package/src/event-transformer.ts
CHANGED
|
@@ -118,15 +118,14 @@ export class EventTransformer {
|
|
|
118
118
|
|
|
119
119
|
// Handle user messages
|
|
120
120
|
if (sdkMessage.type === 'user') {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
+
}
|
package/src/posthog-api.ts
CHANGED
|
@@ -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();
|
package/src/stage-executor.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
89
|
-
|
|
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
|
|
129
|
-
|
|
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
|
-
|