@posthog/agent 1.24.0 → 1.24.2
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/LICENSE +33 -0
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts +3 -3
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +111 -156
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts +1 -1
- package/dist/src/adapters/claude/tool-mapper.d.ts.map +1 -1
- package/dist/src/adapters/claude/tool-mapper.js.map +1 -1
- package/dist/src/adapters/types.d.ts +1 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +7 -7
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +85 -143
- package/dist/src/agent.js.map +1 -1
- package/dist/src/agents/execution.js.map +1 -1
- package/dist/src/agents/planning.js.map +1 -1
- package/dist/src/agents/research.js.map +1 -1
- package/dist/src/file-manager.d.ts +4 -4
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +58 -59
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -1
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +70 -87
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +3 -2
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +22 -22
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/prompt-builder.d.ts +3 -3
- package/dist/src/prompt-builder.d.ts.map +1 -1
- package/dist/src/prompt-builder.js +93 -123
- package/dist/src/prompt-builder.js.map +1 -1
- package/dist/src/task-manager.d.ts +4 -4
- package/dist/src/task-manager.d.ts.map +1 -1
- package/dist/src/task-manager.js +18 -19
- package/dist/src/task-manager.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +4 -3
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +54 -59
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/template-manager.d.ts +1 -1
- package/dist/src/template-manager.d.ts.map +1 -1
- package/dist/src/template-manager.js +28 -30
- package/dist/src/template-manager.js.map +1 -1
- package/dist/src/todo-manager.d.ts +3 -3
- package/dist/src/todo-manager.d.ts.map +1 -1
- package/dist/src/todo-manager.js +24 -29
- package/dist/src/todo-manager.js.map +1 -1
- package/dist/src/tools/registry.d.ts +1 -1
- package/dist/src/tools/registry.js +60 -60
- package/dist/src/tools/registry.js.map +1 -1
- package/dist/src/tools/types.d.ts +31 -31
- package/dist/src/types.d.ts +33 -33
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/logger.d.ts +4 -4
- package/dist/src/utils/logger.d.ts.map +1 -1
- package/dist/src/utils/logger.js +8 -8
- package/dist/src/utils/logger.js.map +1 -1
- package/dist/src/workflow/config.d.ts +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +18 -18
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.d.ts +1 -1
- package/dist/src/workflow/steps/build.d.ts.map +1 -1
- package/dist/src/workflow/steps/build.js +38 -46
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +1 -1
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -1
- package/dist/src/workflow/steps/finalize.js +48 -54
- package/dist/src/workflow/steps/finalize.js.map +1 -1
- package/dist/src/workflow/steps/plan.d.ts +1 -1
- package/dist/src/workflow/steps/plan.d.ts.map +1 -1
- package/dist/src/workflow/steps/plan.js +46 -58
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.d.ts +1 -1
- package/dist/src/workflow/steps/research.d.ts.map +1 -1
- package/dist/src/workflow/steps/research.js +56 -68
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/dist/src/workflow/types.d.ts +12 -12
- package/dist/src/workflow/types.d.ts.map +1 -1
- package/dist/src/workflow/utils.d.ts +1 -1
- package/dist/src/workflow/utils.d.ts.map +1 -1
- package/dist/src/workflow/utils.js +4 -7
- package/dist/src/workflow/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/adapters/claude/claude-adapter.ts +168 -220
- package/src/adapters/claude/tool-mapper.ts +2 -2
- package/src/adapters/types.ts +1 -1
- package/src/agent.ts +444 -579
- package/src/agents/execution.ts +1 -1
- package/src/agents/planning.ts +1 -1
- package/src/agents/research.ts +1 -0
- package/src/file-manager.ts +63 -64
- package/src/git-manager.ts +88 -144
- package/src/posthog-api.ts +82 -122
- package/src/prompt-builder.ts +135 -180
- package/src/task-manager.ts +30 -38
- package/src/task-progress-reporter.ts +59 -70
- package/src/template-manager.ts +45 -98
- package/src/todo-manager.ts +30 -35
- package/src/tools/registry.ts +62 -62
- package/src/tools/types.ts +36 -36
- package/src/types.ts +71 -93
- package/src/utils/logger.ts +56 -62
- package/src/workflow/config.ts +48 -48
- package/src/workflow/steps/build.ts +113 -122
- package/src/workflow/steps/finalize.ts +182 -214
- package/src/workflow/steps/plan.ts +131 -151
- package/src/workflow/steps/research.ts +186 -205
- package/src/workflow/types.ts +36 -38
- package/src/workflow/utils.ts +34 -37
package/src/agent.ts
CHANGED
|
@@ -1,403 +1,322 @@
|
|
|
1
1
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Task,
|
|
16
|
-
} from "./types.js";
|
|
17
|
-
import { Logger } from "./utils/logger.js";
|
|
18
|
-
import { TASK_WORKFLOW } from "./workflow/config.js";
|
|
19
|
-
import type { WorkflowRuntime } from "./workflow/types.js";
|
|
2
|
+
import type { Task, ExecutionResult, AgentConfig, CanUseTool } from './types.js';
|
|
3
|
+
import { TaskManager } from './task-manager.js';
|
|
4
|
+
import { PostHogAPIClient } from './posthog-api.js';
|
|
5
|
+
import { PostHogFileManager } from './file-manager.js';
|
|
6
|
+
import { GitManager } from './git-manager.js';
|
|
7
|
+
import { TemplateManager } from './template-manager.js';
|
|
8
|
+
import { ClaudeAdapter } from './adapters/claude/claude-adapter.js';
|
|
9
|
+
import type { ProviderAdapter } from './adapters/types.js';
|
|
10
|
+
import { Logger } from './utils/logger.js';
|
|
11
|
+
import { PromptBuilder } from './prompt-builder.js';
|
|
12
|
+
import { TaskProgressReporter } from './task-progress-reporter.js';
|
|
13
|
+
import { TASK_WORKFLOW } from './workflow/config.js';
|
|
14
|
+
import type { WorkflowRuntime } from './workflow/types.js';
|
|
20
15
|
|
|
21
16
|
export class Agent {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
17
|
+
private workingDirectory: string;
|
|
18
|
+
private onEvent?: (event: any) => void;
|
|
19
|
+
private taskManager: TaskManager;
|
|
20
|
+
private posthogAPI?: PostHogAPIClient;
|
|
21
|
+
private fileManager: PostHogFileManager;
|
|
22
|
+
private gitManager: GitManager;
|
|
23
|
+
private templateManager: TemplateManager;
|
|
24
|
+
private adapter: ProviderAdapter;
|
|
25
|
+
private logger: Logger;
|
|
26
|
+
private progressReporter: TaskProgressReporter;
|
|
27
|
+
private promptBuilder: PromptBuilder;
|
|
28
|
+
private mcpServers?: Record<string, any>;
|
|
29
|
+
private canUseTool?: CanUseTool;
|
|
30
|
+
public debug: boolean;
|
|
31
|
+
|
|
32
|
+
constructor(config: AgentConfig) {
|
|
33
|
+
this.workingDirectory = config.workingDirectory || process.cwd();
|
|
34
|
+
this.onEvent = config.onEvent;
|
|
35
|
+
this.canUseTool = config.canUseTool;
|
|
36
|
+
this.debug = config.debug || false;
|
|
37
|
+
|
|
38
|
+
// Build default PostHog MCP server configuration
|
|
39
|
+
const posthogMcpUrl = config.posthogMcpUrl
|
|
40
|
+
|| process.env.POSTHOG_MCP_URL
|
|
41
|
+
|| 'https://mcp.posthog.com/mcp';
|
|
42
|
+
|
|
43
|
+
// Add auth if API key provided
|
|
44
|
+
const headers: Record<string, string> = {};
|
|
45
|
+
if (config.posthogApiKey) {
|
|
46
|
+
headers['Authorization'] = `Bearer ${config.posthogApiKey}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const defaultMcpServers = {
|
|
50
|
+
posthog: {
|
|
51
|
+
type: 'http' as const,
|
|
52
|
+
url: posthogMcpUrl,
|
|
53
|
+
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Merge default PostHog MCP with user-provided servers (user config takes precedence)
|
|
58
|
+
this.mcpServers = {
|
|
59
|
+
...defaultMcpServers,
|
|
60
|
+
...config.mcpServers
|
|
61
|
+
};
|
|
62
|
+
this.logger = new Logger({ debug: this.debug, prefix: '[PostHog Agent]' });
|
|
63
|
+
this.taskManager = new TaskManager();
|
|
64
|
+
// Hardcode Claude adapter for now - extensible for other providers later
|
|
65
|
+
this.adapter = new ClaudeAdapter();
|
|
66
|
+
|
|
67
|
+
this.fileManager = new PostHogFileManager(
|
|
68
|
+
this.workingDirectory,
|
|
69
|
+
this.logger.child('FileManager')
|
|
70
|
+
);
|
|
71
|
+
this.gitManager = new GitManager({
|
|
72
|
+
repositoryPath: this.workingDirectory,
|
|
73
|
+
logger: this.logger.child('GitManager')
|
|
74
|
+
// TODO: Add author config from environment or config
|
|
75
|
+
});
|
|
76
|
+
this.templateManager = new TemplateManager();
|
|
77
|
+
|
|
78
|
+
if (config.posthogApiUrl && config.posthogApiKey) {
|
|
79
|
+
this.posthogAPI = new PostHogAPIClient({
|
|
80
|
+
apiUrl: config.posthogApiUrl,
|
|
81
|
+
apiKey: config.posthogApiKey,
|
|
82
|
+
projectId: config.posthogProjectId,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
54
85
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Merge default PostHog MCP with user-provided servers (user config takes precedence)
|
|
64
|
-
this.mcpServers = {
|
|
65
|
-
...defaultMcpServers,
|
|
66
|
-
...config.mcpServers,
|
|
67
|
-
};
|
|
68
|
-
this.logger = new Logger({ debug: this.debug, prefix: "[PostHog Agent]" });
|
|
69
|
-
this.taskManager = new TaskManager();
|
|
70
|
-
// Hardcode Claude adapter for now - extensible for other providers later
|
|
71
|
-
this.adapter = new ClaudeAdapter();
|
|
72
|
-
|
|
73
|
-
this.fileManager = new PostHogFileManager(
|
|
74
|
-
this.workingDirectory,
|
|
75
|
-
this.logger.child("FileManager"),
|
|
76
|
-
);
|
|
77
|
-
this.gitManager = new GitManager({
|
|
78
|
-
repositoryPath: this.workingDirectory,
|
|
79
|
-
logger: this.logger.child("GitManager"),
|
|
80
|
-
// TODO: Add author config from environment or config
|
|
81
|
-
});
|
|
82
|
-
this.templateManager = new TemplateManager();
|
|
83
|
-
|
|
84
|
-
if (config.posthogApiUrl && config.posthogApiKey) {
|
|
85
|
-
this.posthogAPI = new PostHogAPIClient({
|
|
86
|
-
apiUrl: config.posthogApiUrl,
|
|
87
|
-
apiKey: config.posthogApiKey,
|
|
88
|
-
projectId: config.posthogProjectId,
|
|
89
|
-
});
|
|
86
|
+
this.promptBuilder = new PromptBuilder({
|
|
87
|
+
getTaskFiles: (taskId: string) => this.getTaskFiles(taskId),
|
|
88
|
+
generatePlanTemplate: (vars) => this.templateManager.generatePlan(vars),
|
|
89
|
+
posthogClient: this.posthogAPI,
|
|
90
|
+
logger: this.logger.child('PromptBuilder')
|
|
91
|
+
});
|
|
92
|
+
this.progressReporter = new TaskProgressReporter(this.posthogAPI, this.logger);
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.progressReporter = new TaskProgressReporter(
|
|
99
|
-
this.posthogAPI,
|
|
100
|
-
this.logger,
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Enable or disable debug logging
|
|
106
|
-
*/
|
|
107
|
-
setDebug(enabled: boolean) {
|
|
108
|
-
this.debug = enabled;
|
|
109
|
-
this.logger.setDebug(enabled);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Configure LLM gateway environment variables for Claude Code CLI
|
|
114
|
-
*/
|
|
115
|
-
private async _configureLlmGateway(): Promise<void> {
|
|
116
|
-
if (!this.posthogAPI) {
|
|
117
|
-
return;
|
|
95
|
+
/**
|
|
96
|
+
* Enable or disable debug logging
|
|
97
|
+
*/
|
|
98
|
+
setDebug(enabled: boolean) {
|
|
99
|
+
this.debug = enabled;
|
|
100
|
+
this.logger.setDebug(enabled);
|
|
118
101
|
}
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Configure LLM gateway environment variables for Claude Code CLI
|
|
105
|
+
*/
|
|
106
|
+
private async _configureLlmGateway(): Promise<void> {
|
|
107
|
+
if (!this.posthogAPI) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const gatewayUrl = this.posthogAPI.getLlmGatewayUrl();
|
|
113
|
+
const apiKey = this.posthogAPI.getApiKey();
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
115
|
+
process.env.ANTHROPIC_BASE_URL = gatewayUrl;
|
|
116
|
+
process.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
117
|
+
this.ensureOpenAIGatewayEnv(gatewayUrl, apiKey);
|
|
127
118
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
119
|
+
this.logger.debug('Configured LLM gateway', { gatewayUrl });
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.logger.error('Failed to configure LLM gateway', error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Adaptive task execution orchestrated via workflow steps
|
|
127
|
+
async runTask(taskOrId: Task | string, options: import('./types.js').TaskExecutionOptions = {}): Promise<void> {
|
|
128
|
+
await this._configureLlmGateway();
|
|
129
|
+
|
|
130
|
+
const task = typeof taskOrId === 'string' ? await this.fetchTask(taskOrId) : taskOrId;
|
|
131
|
+
const cwd = options.repositoryPath || this.workingDirectory;
|
|
132
|
+
const isCloudMode = options.isCloudMode ?? false;
|
|
133
|
+
const taskSlug = (task as any).slug || task.id;
|
|
134
|
+
|
|
135
|
+
this.logger.info('Starting adaptive task execution', { taskId: task.id, taskSlug, isCloudMode });
|
|
136
|
+
|
|
137
|
+
// Initialize progress reporter for task run tracking (needed for PR attachment)
|
|
138
|
+
await this.progressReporter.start(task.id, { totalSteps: TASK_WORKFLOW.length });
|
|
139
|
+
this.emitEvent(this.adapter.createStatusEvent('run_started', { runId: this.progressReporter.runId }));
|
|
140
|
+
|
|
141
|
+
await this.prepareTaskBranch(taskSlug, isCloudMode);
|
|
142
|
+
|
|
143
|
+
let taskError: Error | undefined;
|
|
144
|
+
try {
|
|
145
|
+
const workflowContext: WorkflowRuntime = {
|
|
146
|
+
task,
|
|
147
|
+
taskSlug,
|
|
148
|
+
cwd,
|
|
149
|
+
isCloudMode,
|
|
150
|
+
options,
|
|
151
|
+
logger: this.logger,
|
|
152
|
+
fileManager: this.fileManager,
|
|
153
|
+
gitManager: this.gitManager,
|
|
154
|
+
promptBuilder: this.promptBuilder,
|
|
155
|
+
progressReporter: this.progressReporter,
|
|
156
|
+
adapter: this.adapter,
|
|
157
|
+
mcpServers: this.mcpServers,
|
|
158
|
+
posthogAPI: this.posthogAPI,
|
|
159
|
+
emitEvent: (event: any) => this.emitEvent(event),
|
|
160
|
+
stepResults: {},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
for (const step of TASK_WORKFLOW) {
|
|
164
|
+
const result = await step.run({ step, context: workflowContext });
|
|
165
|
+
if (result.halt) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const shouldCreatePR = options.createPR ?? isCloudMode;
|
|
171
|
+
if (shouldCreatePR) {
|
|
172
|
+
await this.ensurePullRequest(task, workflowContext.stepResults);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.logger.info('Task execution complete', { taskId: task.id });
|
|
176
|
+
this.emitEvent(this.adapter.createStatusEvent('task_complete', { taskId: task.id }));
|
|
177
|
+
} catch (error) {
|
|
178
|
+
taskError = error instanceof Error ? error : new Error(String(error));
|
|
179
|
+
this.logger.error('Task execution failed', { taskId: task.id, error: taskError.message });
|
|
180
|
+
} finally {
|
|
181
|
+
if (taskError) {
|
|
182
|
+
await this.progressReporter.fail(taskError);
|
|
183
|
+
throw taskError;
|
|
184
|
+
} else {
|
|
185
|
+
await this.progressReporter.complete();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
132
188
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
let taskError: Error | undefined;
|
|
167
|
-
try {
|
|
168
|
-
const workflowContext: WorkflowRuntime = {
|
|
169
|
-
task,
|
|
170
|
-
taskSlug,
|
|
171
|
-
cwd,
|
|
172
|
-
isCloudMode,
|
|
173
|
-
options,
|
|
174
|
-
logger: this.logger,
|
|
175
|
-
fileManager: this.fileManager,
|
|
176
|
-
gitManager: this.gitManager,
|
|
177
|
-
promptBuilder: this.promptBuilder,
|
|
178
|
-
progressReporter: this.progressReporter,
|
|
179
|
-
adapter: this.adapter,
|
|
180
|
-
mcpServers: this.mcpServers,
|
|
181
|
-
posthogAPI: this.posthogAPI,
|
|
182
|
-
emitEvent: (event: any) => this.emitEvent(event),
|
|
183
|
-
stepResults: {},
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
for (const step of TASK_WORKFLOW) {
|
|
187
|
-
const result = await step.run({ step, context: workflowContext });
|
|
188
|
-
if (result.halt) {
|
|
189
|
-
return;
|
|
189
|
+
|
|
190
|
+
// Direct prompt execution - still supported for low-level usage
|
|
191
|
+
async run(prompt: string, options: { repositoryPath?: string; permissionMode?: import('./types.js').PermissionMode; queryOverrides?: Record<string, any>; canUseTool?: CanUseTool } = {}): Promise<ExecutionResult> {
|
|
192
|
+
await this._configureLlmGateway();
|
|
193
|
+
const baseOptions: Record<string, any> = {
|
|
194
|
+
model: "claude-sonnet-4-5-20250929",
|
|
195
|
+
cwd: options.repositoryPath || this.workingDirectory,
|
|
196
|
+
permissionMode: (options.permissionMode as any) || "default",
|
|
197
|
+
settingSources: ["local"],
|
|
198
|
+
mcpServers: this.mcpServers,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Add canUseTool hook if provided (options take precedence over instance config)
|
|
202
|
+
const canUseTool = options.canUseTool || this.canUseTool;
|
|
203
|
+
if (canUseTool) {
|
|
204
|
+
baseOptions.canUseTool = canUseTool;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const response = query({
|
|
208
|
+
prompt,
|
|
209
|
+
options: { ...baseOptions, ...(options.queryOverrides || {}) },
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const results = [];
|
|
213
|
+
for await (const message of response) {
|
|
214
|
+
this.logger.debug('Received message in direct run', message);
|
|
215
|
+
// Emit raw SDK event
|
|
216
|
+
this.emitEvent(this.adapter.createRawSDKEvent(message));
|
|
217
|
+
const transformedEvents = this.adapter.transform(message);
|
|
218
|
+
for (const event of transformedEvents) {
|
|
219
|
+
this.emitEvent(event);
|
|
220
|
+
}
|
|
221
|
+
results.push(message);
|
|
190
222
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const shouldCreatePR = options.createPR ?? isCloudMode;
|
|
194
|
-
if (shouldCreatePR) {
|
|
195
|
-
await this.ensurePullRequest(task, workflowContext.stepResults);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
this.logger.info("Task execution complete", { taskId: task.id });
|
|
199
|
-
this.emitEvent(
|
|
200
|
-
this.adapter.createStatusEvent("task_complete", { taskId: task.id }),
|
|
201
|
-
);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
taskError = error instanceof Error ? error : new Error(String(error));
|
|
204
|
-
this.logger.error("Task execution failed", {
|
|
205
|
-
taskId: task.id,
|
|
206
|
-
error: taskError.message,
|
|
207
|
-
});
|
|
208
|
-
} finally {
|
|
209
|
-
if (taskError) {
|
|
210
|
-
await this.progressReporter.fail(taskError);
|
|
211
|
-
// biome-ignore lint/correctness/noUnsafeFinally: we actually want to throw the error
|
|
212
|
-
throw taskError;
|
|
213
|
-
} else {
|
|
214
|
-
await this.progressReporter.complete();
|
|
215
|
-
}
|
|
223
|
+
|
|
224
|
+
return { results };
|
|
216
225
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
} = {},
|
|
228
|
-
): Promise<ExecutionResult> {
|
|
229
|
-
await this._configureLlmGateway();
|
|
230
|
-
const baseOptions: Record<string, any> = {
|
|
231
|
-
model: "claude-sonnet-4-5-20250929",
|
|
232
|
-
cwd: options.repositoryPath || this.workingDirectory,
|
|
233
|
-
permissionMode: (options.permissionMode as any) || "default",
|
|
234
|
-
settingSources: ["local"],
|
|
235
|
-
mcpServers: this.mcpServers,
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
// Add canUseTool hook if provided (options take precedence over instance config)
|
|
239
|
-
const canUseTool = options.canUseTool || this.canUseTool;
|
|
240
|
-
if (canUseTool) {
|
|
241
|
-
baseOptions.canUseTool = canUseTool;
|
|
226
|
+
|
|
227
|
+
// PostHog task operations
|
|
228
|
+
async fetchTask(taskId: string): Promise<Task> {
|
|
229
|
+
this.logger.debug('Fetching task from PostHog', { taskId });
|
|
230
|
+
if (!this.posthogAPI) {
|
|
231
|
+
const error = new Error('PostHog API not configured. Provide posthogApiUrl and posthogApiKey in constructor.');
|
|
232
|
+
this.logger.error('PostHog API not configured', error);
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
return this.posthogAPI.fetchTask(taskId);
|
|
242
236
|
}
|
|
243
237
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const transformedEvents = this.adapter.transform(message);
|
|
256
|
-
for (const event of transformedEvents) {
|
|
257
|
-
this.emitEvent(event);
|
|
238
|
+
getPostHogClient(): PostHogAPIClient | undefined {
|
|
239
|
+
return this.posthogAPI;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async listTasks(filters?: {
|
|
243
|
+
repository?: string;
|
|
244
|
+
organization?: string;
|
|
245
|
+
origin_product?: string;
|
|
246
|
+
}): Promise<Task[]> {
|
|
247
|
+
if (!this.posthogAPI) {
|
|
248
|
+
throw new Error('PostHog API not configured. Provide posthogApiUrl and posthogApiKey in constructor.');
|
|
258
249
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
250
|
+
return this.posthogAPI.listTasks(filters);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// File system operations for task artifacts
|
|
254
|
+
async writeTaskFile(taskId: string, fileName: string, content: string, type: 'plan' | 'context' | 'reference' | 'output' = 'reference'): Promise<void> {
|
|
255
|
+
this.logger.debug('Writing task file', { taskId, fileName, type, contentLength: content.length });
|
|
256
|
+
await this.fileManager.writeTaskFile(taskId, { name: fileName, content, type });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async readTaskFile(taskId: string, fileName: string): Promise<string | null> {
|
|
260
|
+
this.logger.debug('Reading task file', { taskId, fileName });
|
|
261
|
+
return await this.fileManager.readTaskFile(taskId, fileName);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async getTaskFiles(taskId: string): Promise<any[]> {
|
|
265
|
+
this.logger.debug('Getting task files', { taskId });
|
|
266
|
+
const files = await this.fileManager.getTaskFiles(taskId);
|
|
267
|
+
this.logger.debug('Found task files', { taskId, fileCount: files.length });
|
|
268
|
+
return files;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async writePlan(taskId: string, plan: string): Promise<void> {
|
|
272
|
+
this.logger.info('Writing plan', { taskId, planLength: plan.length });
|
|
273
|
+
await this.fileManager.writePlan(taskId, plan);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async readPlan(taskId: string): Promise<string | null> {
|
|
277
|
+
this.logger.debug('Reading plan', { taskId });
|
|
278
|
+
return await this.fileManager.readPlan(taskId);
|
|
264
279
|
}
|
|
265
280
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
281
|
+
// Git operations for task execution
|
|
282
|
+
async createPlanningBranch(taskId: string): Promise<string> {
|
|
283
|
+
this.logger.info('Creating planning branch', { taskId });
|
|
284
|
+
const branchName = await this.gitManager.createTaskPlanningBranch(taskId);
|
|
285
|
+
this.logger.debug('Planning branch created', { taskId, branchName });
|
|
286
|
+
return branchName;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async commitPlan(taskId: string, taskTitle: string): Promise<string> {
|
|
290
|
+
this.logger.info('Committing plan', { taskId, taskTitle });
|
|
291
|
+
const commitHash = await this.gitManager.commitPlan(taskId, taskTitle);
|
|
292
|
+
this.logger.debug('Plan committed', { taskId, commitHash });
|
|
293
|
+
return commitHash;
|
|
278
294
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
async listTasks(filters?: {
|
|
287
|
-
repository?: string;
|
|
288
|
-
organization?: string;
|
|
289
|
-
origin_product?: string;
|
|
290
|
-
}): Promise<Task[]> {
|
|
291
|
-
if (!this.posthogAPI) {
|
|
292
|
-
throw new Error(
|
|
293
|
-
"PostHog API not configured. Provide posthogApiUrl and posthogApiKey in constructor.",
|
|
294
|
-
);
|
|
295
|
+
|
|
296
|
+
async createImplementationBranch(taskId: string, planningBranchName?: string): Promise<string> {
|
|
297
|
+
this.logger.info('Creating implementation branch', { taskId, fromBranch: planningBranchName });
|
|
298
|
+
const branchName = await this.gitManager.createTaskImplementationBranch(taskId, planningBranchName);
|
|
299
|
+
this.logger.debug('Implementation branch created', { taskId, branchName });
|
|
300
|
+
return branchName;
|
|
295
301
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
content,
|
|
315
|
-
type,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
async readTaskFile(taskId: string, fileName: string): Promise<string | null> {
|
|
320
|
-
this.logger.debug("Reading task file", { taskId, fileName });
|
|
321
|
-
return await this.fileManager.readTaskFile(taskId, fileName);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async getTaskFiles(taskId: string): Promise<any[]> {
|
|
325
|
-
this.logger.debug("Getting task files", { taskId });
|
|
326
|
-
const files = await this.fileManager.getTaskFiles(taskId);
|
|
327
|
-
this.logger.debug("Found task files", { taskId, fileCount: files.length });
|
|
328
|
-
return files;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async writePlan(taskId: string, plan: string): Promise<void> {
|
|
332
|
-
this.logger.info("Writing plan", { taskId, planLength: plan.length });
|
|
333
|
-
await this.fileManager.writePlan(taskId, plan);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async readPlan(taskId: string): Promise<string | null> {
|
|
337
|
-
this.logger.debug("Reading plan", { taskId });
|
|
338
|
-
return await this.fileManager.readPlan(taskId);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Git operations for task execution
|
|
342
|
-
async createPlanningBranch(taskId: string): Promise<string> {
|
|
343
|
-
this.logger.info("Creating planning branch", { taskId });
|
|
344
|
-
const branchName = await this.gitManager.createTaskPlanningBranch(taskId);
|
|
345
|
-
this.logger.debug("Planning branch created", { taskId, branchName });
|
|
346
|
-
return branchName;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
async commitPlan(taskId: string, taskTitle: string): Promise<string> {
|
|
350
|
-
this.logger.info("Committing plan", { taskId, taskTitle });
|
|
351
|
-
const commitHash = await this.gitManager.commitPlan(taskId, taskTitle);
|
|
352
|
-
this.logger.debug("Plan committed", { taskId, commitHash });
|
|
353
|
-
return commitHash;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async createImplementationBranch(
|
|
357
|
-
taskId: string,
|
|
358
|
-
planningBranchName?: string,
|
|
359
|
-
): Promise<string> {
|
|
360
|
-
this.logger.info("Creating implementation branch", {
|
|
361
|
-
taskId,
|
|
362
|
-
fromBranch: planningBranchName,
|
|
363
|
-
});
|
|
364
|
-
const branchName = await this.gitManager.createTaskImplementationBranch(
|
|
365
|
-
taskId,
|
|
366
|
-
planningBranchName,
|
|
367
|
-
);
|
|
368
|
-
this.logger.debug("Implementation branch created", { taskId, branchName });
|
|
369
|
-
return branchName;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async commitImplementation(
|
|
373
|
-
taskId: string,
|
|
374
|
-
taskTitle: string,
|
|
375
|
-
planSummary?: string,
|
|
376
|
-
): Promise<string> {
|
|
377
|
-
this.logger.info("Committing implementation", { taskId, taskTitle });
|
|
378
|
-
const commitHash = await this.gitManager.commitImplementation(
|
|
379
|
-
taskId,
|
|
380
|
-
taskTitle,
|
|
381
|
-
planSummary,
|
|
382
|
-
);
|
|
383
|
-
this.logger.debug("Implementation committed", { taskId, commitHash });
|
|
384
|
-
return commitHash;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
async createPullRequest(
|
|
388
|
-
taskId: string,
|
|
389
|
-
branchName: string,
|
|
390
|
-
taskTitle: string,
|
|
391
|
-
taskDescription: string,
|
|
392
|
-
customBody?: string,
|
|
393
|
-
): Promise<string> {
|
|
394
|
-
this.logger.info("Creating pull request", {
|
|
395
|
-
taskId,
|
|
396
|
-
branchName,
|
|
397
|
-
taskTitle,
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
const defaultBody = `## Task Details
|
|
302
|
+
|
|
303
|
+
async commitImplementation(taskId: string, taskTitle: string, planSummary?: string): Promise<string> {
|
|
304
|
+
this.logger.info('Committing implementation', { taskId, taskTitle });
|
|
305
|
+
const commitHash = await this.gitManager.commitImplementation(taskId, taskTitle, planSummary);
|
|
306
|
+
this.logger.debug('Implementation committed', { taskId, commitHash });
|
|
307
|
+
return commitHash;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async createPullRequest(
|
|
311
|
+
taskId: string,
|
|
312
|
+
branchName: string,
|
|
313
|
+
taskTitle: string,
|
|
314
|
+
taskDescription: string,
|
|
315
|
+
customBody?: string
|
|
316
|
+
): Promise<string> {
|
|
317
|
+
this.logger.info('Creating pull request', { taskId, branchName, taskTitle });
|
|
318
|
+
|
|
319
|
+
const defaultBody = `## Task Details
|
|
401
320
|
**Task ID**: ${taskId}
|
|
402
321
|
**Description**: ${taskDescription}
|
|
403
322
|
|
|
@@ -405,225 +324,171 @@ export class Agent {
|
|
|
405
324
|
This PR implements the changes described in the task.
|
|
406
325
|
|
|
407
326
|
Generated by PostHog Agent`;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const prUrl = await this.gitManager.createPullRequest(
|
|
411
|
-
branchName,
|
|
412
|
-
taskTitle,
|
|
413
|
-
prBody,
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
this.logger.info("Pull request created", { taskId, prUrl });
|
|
417
|
-
return prUrl;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
async attachPullRequestToTask(
|
|
421
|
-
taskId: string,
|
|
422
|
-
prUrl: string,
|
|
423
|
-
branchName?: string,
|
|
424
|
-
): Promise<void> {
|
|
425
|
-
this.logger.info("Attaching PR to task run", { taskId, prUrl, branchName });
|
|
426
|
-
|
|
427
|
-
if (!this.posthogAPI || !this.progressReporter.runId) {
|
|
428
|
-
const error = new Error(
|
|
429
|
-
"PostHog API not configured or no active run. Cannot attach PR to task.",
|
|
430
|
-
);
|
|
431
|
-
this.logger.error("PostHog API not configured", error);
|
|
432
|
-
throw error;
|
|
433
|
-
}
|
|
327
|
+
const prBody = customBody || defaultBody;
|
|
434
328
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
329
|
+
const prUrl = await this.gitManager.createPullRequest(
|
|
330
|
+
branchName,
|
|
331
|
+
taskTitle,
|
|
332
|
+
prBody
|
|
333
|
+
);
|
|
441
334
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
this.progressReporter.runId,
|
|
445
|
-
updates,
|
|
446
|
-
);
|
|
447
|
-
this.logger.debug("PR attached to task run", {
|
|
448
|
-
taskId,
|
|
449
|
-
runId: this.progressReporter.runId,
|
|
450
|
-
prUrl,
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async updateTaskBranch(taskId: string, branchName: string): Promise<void> {
|
|
455
|
-
this.logger.info("Updating task run branch", { taskId, branchName });
|
|
456
|
-
|
|
457
|
-
if (!this.posthogAPI || !this.progressReporter.runId) {
|
|
458
|
-
const error = new Error(
|
|
459
|
-
"PostHog API not configured or no active run. Cannot update branch.",
|
|
460
|
-
);
|
|
461
|
-
this.logger.error("PostHog API not configured", error);
|
|
462
|
-
throw error;
|
|
335
|
+
this.logger.info('Pull request created', { taskId, prUrl });
|
|
336
|
+
return prUrl;
|
|
463
337
|
}
|
|
464
338
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
this.logger.debug("Task run branch updated", {
|
|
469
|
-
taskId,
|
|
470
|
-
runId: this.progressReporter.runId,
|
|
471
|
-
branchName,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Execution management
|
|
476
|
-
cancelTask(taskId: string): void {
|
|
477
|
-
// Find the execution for this task and cancel it
|
|
478
|
-
for (const [executionId, execution] of this.taskManager.executionStates) {
|
|
479
|
-
if (execution.taskId === taskId && execution.status === "running") {
|
|
480
|
-
this.taskManager.cancelExecution(executionId);
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
getTaskExecutionStatus(taskId: string): string | null {
|
|
487
|
-
// Find the execution for this task
|
|
488
|
-
for (const execution of this.taskManager.executionStates.values()) {
|
|
489
|
-
if (execution.taskId === taskId) {
|
|
490
|
-
return execution.status;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return null;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
private async prepareTaskBranch(
|
|
497
|
-
taskSlug: string,
|
|
498
|
-
isCloudMode: boolean,
|
|
499
|
-
): Promise<void> {
|
|
500
|
-
if (await this.gitManager.hasChanges()) {
|
|
501
|
-
throw new Error(
|
|
502
|
-
"Cannot start task with uncommitted changes. Please commit or stash your changes first.",
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
await this.gitManager.resetToDefaultBranchIfNeeded();
|
|
339
|
+
async attachPullRequestToTask(taskId: string, prUrl: string, branchName?: string): Promise<void> {
|
|
340
|
+
this.logger.info('Attaching PR to task run', { taskId, prUrl, branchName });
|
|
507
341
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
branch: branchName,
|
|
514
|
-
}),
|
|
515
|
-
);
|
|
516
|
-
|
|
517
|
-
await this.gitManager.addAllPostHogFiles();
|
|
342
|
+
if (!this.posthogAPI || !this.progressReporter.runId) {
|
|
343
|
+
const error = new Error('PostHog API not configured or no active run. Cannot attach PR to task.');
|
|
344
|
+
this.logger.error('PostHog API not configured', error);
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
518
347
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
} else {
|
|
525
|
-
// Check if there are any changes before committing
|
|
526
|
-
const hasChanges = await this.gitManager.hasStagedChanges();
|
|
527
|
-
if (hasChanges) {
|
|
528
|
-
await this.gitManager.commitChanges(`Initialize task ${taskSlug}`);
|
|
348
|
+
const updates: any = {
|
|
349
|
+
output: { pr_url: prUrl }
|
|
350
|
+
};
|
|
351
|
+
if (branchName) {
|
|
352
|
+
updates.branch = branchName;
|
|
529
353
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
branch: existingBranch,
|
|
534
|
-
});
|
|
535
|
-
await this.gitManager.switchToBranch(existingBranch);
|
|
354
|
+
|
|
355
|
+
await this.posthogAPI.updateTaskRun(taskId, this.progressReporter.runId, updates);
|
|
356
|
+
this.logger.debug('PR attached to task run', { taskId, runId: this.progressReporter.runId, prUrl });
|
|
536
357
|
}
|
|
537
|
-
}
|
|
538
358
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const resolvedToken = token || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
359
|
+
async updateTaskBranch(taskId: string, branchName: string): Promise<void> {
|
|
360
|
+
this.logger.info('Updating task run branch', { taskId, branchName });
|
|
542
361
|
|
|
543
|
-
|
|
544
|
-
|
|
362
|
+
if (!this.posthogAPI || !this.progressReporter.runId) {
|
|
363
|
+
const error = new Error('PostHog API not configured or no active run. Cannot update branch.');
|
|
364
|
+
this.logger.error('PostHog API not configured', error);
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await this.posthogAPI.updateTaskRun(taskId, this.progressReporter.runId, { branch: branchName });
|
|
369
|
+
this.logger.debug('Task run branch updated', { taskId, runId: this.progressReporter.runId, branchName });
|
|
545
370
|
}
|
|
546
371
|
|
|
547
|
-
|
|
548
|
-
|
|
372
|
+
// Execution management
|
|
373
|
+
cancelTask(taskId: string): void {
|
|
374
|
+
// Find the execution for this task and cancel it
|
|
375
|
+
for (const [executionId, execution] of this.taskManager['executionStates']) {
|
|
376
|
+
if (execution.taskId === taskId && execution.status === 'running') {
|
|
377
|
+
this.taskManager.cancelExecution(executionId);
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
549
381
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
? (latestRun.output as any).pr_url
|
|
560
|
-
: null;
|
|
561
|
-
|
|
562
|
-
if (existingPr) {
|
|
563
|
-
this.logger.info("PR already exists, skipping creation", {
|
|
564
|
-
taskId: task.id,
|
|
565
|
-
prUrl: existingPr,
|
|
566
|
-
});
|
|
567
|
-
return;
|
|
382
|
+
|
|
383
|
+
getTaskExecutionStatus(taskId: string): string | null {
|
|
384
|
+
// Find the execution for this task
|
|
385
|
+
for (const execution of this.taskManager['executionStates'].values()) {
|
|
386
|
+
if (execution.taskId === taskId) {
|
|
387
|
+
return execution.status;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
568
391
|
}
|
|
569
392
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
393
|
+
private async prepareTaskBranch(taskSlug: string, isCloudMode: boolean): Promise<void> {
|
|
394
|
+
if (await this.gitManager.hasChanges()) {
|
|
395
|
+
throw new Error('Cannot start task with uncommitted changes. Please commit or stash your changes first.');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
await this.gitManager.resetToDefaultBranchIfNeeded();
|
|
399
|
+
|
|
400
|
+
const existingBranch = await this.gitManager.getTaskBranch(taskSlug);
|
|
401
|
+
if (!existingBranch) {
|
|
402
|
+
const branchName = await this.gitManager.createTaskBranch(taskSlug);
|
|
403
|
+
this.emitEvent(this.adapter.createStatusEvent('branch_created', { branch: branchName }));
|
|
404
|
+
|
|
405
|
+
await this.gitManager.addAllPostHogFiles();
|
|
406
|
+
|
|
407
|
+
// Only commit if there are changes or we're in cloud mode
|
|
408
|
+
if (isCloudMode) {
|
|
409
|
+
await this.gitManager.commitAndPush(`Initialize task ${taskSlug}`, { allowEmpty: true });
|
|
410
|
+
} else {
|
|
411
|
+
// Check if there are any changes before committing
|
|
412
|
+
const hasChanges = await this.gitManager.hasStagedChanges();
|
|
413
|
+
if (hasChanges) {
|
|
414
|
+
await this.gitManager.commitChanges(`Initialize task ${taskSlug}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
this.logger.info('Switching to existing task branch', { branch: existingBranch });
|
|
419
|
+
await this.gitManager.switchToBranch(existingBranch);
|
|
420
|
+
}
|
|
577
421
|
}
|
|
578
422
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
this.emitEvent(this.adapter.createStatusEvent("pr_created", { prUrl }));
|
|
592
|
-
|
|
593
|
-
try {
|
|
594
|
-
await this.attachPullRequestToTask(task.id, prUrl, branchName);
|
|
595
|
-
this.logger.info("PR attached to task successfully", {
|
|
596
|
-
taskId: task.id,
|
|
597
|
-
prUrl,
|
|
598
|
-
});
|
|
599
|
-
} catch (error) {
|
|
600
|
-
this.logger.warn("Could not attach PR to task", {
|
|
601
|
-
error: error instanceof Error ? error.message : String(error),
|
|
602
|
-
});
|
|
423
|
+
private ensureOpenAIGatewayEnv(gatewayUrl?: string, token?: string): void {
|
|
424
|
+
const resolvedGatewayUrl = gatewayUrl || process.env.ANTHROPIC_BASE_URL;
|
|
425
|
+
const resolvedToken = token || process.env.ANTHROPIC_AUTH_TOKEN;
|
|
426
|
+
|
|
427
|
+
if (resolvedGatewayUrl) {
|
|
428
|
+
process.env.OPENAI_BASE_URL = resolvedGatewayUrl;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (resolvedToken) {
|
|
432
|
+
process.env.OPENAI_API_KEY = resolvedToken;
|
|
433
|
+
}
|
|
603
434
|
}
|
|
604
|
-
}
|
|
605
435
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
436
|
+
private async ensurePullRequest(task: Task, stepResults: Record<string, any>): Promise<void> {
|
|
437
|
+
const latestRun = task.latest_run;
|
|
438
|
+
const existingPr =
|
|
439
|
+
latestRun?.output && typeof latestRun.output === 'object'
|
|
440
|
+
? (latestRun.output as any).pr_url
|
|
441
|
+
: null;
|
|
442
|
+
|
|
443
|
+
if (existingPr) {
|
|
444
|
+
this.logger.info('PR already exists, skipping creation', { taskId: task.id, prUrl: existingPr });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const buildResult = stepResults['build'];
|
|
449
|
+
if (!buildResult?.commitCreated) {
|
|
450
|
+
this.logger.warn('Build step did not produce a commit; skipping PR creation', { taskId: task.id });
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const branchName = await this.gitManager.getCurrentBranch();
|
|
455
|
+
const finalizeResult = stepResults['finalize'];
|
|
456
|
+
const prBody = finalizeResult?.prBody;
|
|
457
|
+
|
|
458
|
+
const prUrl = await this.createPullRequest(
|
|
459
|
+
task.id,
|
|
460
|
+
branchName,
|
|
461
|
+
task.title,
|
|
462
|
+
task.description ?? '',
|
|
463
|
+
prBody
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
this.emitEvent(this.adapter.createStatusEvent('pr_created', { prUrl }));
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
await this.attachPullRequestToTask(task.id, prUrl, branchName);
|
|
470
|
+
this.logger.info('PR attached to task successfully', { taskId: task.id, prUrl });
|
|
471
|
+
} catch (error) {
|
|
472
|
+
this.logger.warn('Could not attach PR to task', {
|
|
473
|
+
error: error instanceof Error ? error.message : String(error),
|
|
474
|
+
});
|
|
475
|
+
}
|
|
610
476
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
}
|
|
617
|
-
|
|
477
|
+
|
|
478
|
+
private emitEvent(event: any): void {
|
|
479
|
+
if (this.debug && event.type !== 'token') {
|
|
480
|
+
// Log all events except tokens (too verbose)
|
|
481
|
+
this.logger.debug('Emitting event', { type: event.type, ts: event.ts });
|
|
482
|
+
}
|
|
483
|
+
const persistPromise = this.progressReporter.recordEvent(event);
|
|
484
|
+
if (persistPromise && typeof persistPromise.then === 'function') {
|
|
485
|
+
persistPromise.catch((error: Error) =>
|
|
486
|
+
this.logger.debug('Failed to persist agent event', { message: error.message })
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
this.onEvent?.(event);
|
|
618
490
|
}
|
|
619
|
-
this.onEvent?.(event);
|
|
620
|
-
}
|
|
621
491
|
}
|
|
622
492
|
|
|
623
|
-
export
|
|
624
|
-
|
|
625
|
-
ExecutionResult,
|
|
626
|
-
SupportingFile,
|
|
627
|
-
Task,
|
|
628
|
-
} from "./types.js";
|
|
629
|
-
export { PermissionMode } from "./types.js";
|
|
493
|
+
export { PermissionMode } from './types.js';
|
|
494
|
+
export type { Task, SupportingFile, ExecutionResult, AgentConfig } from './types.js';
|