@posthog/agent 1.10.0 → 1.11.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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/src/agent-registry.d.ts.map +1 -1
- package/dist/src/agent-registry.js +6 -0
- package/dist/src/agent-registry.js.map +1 -1
- package/dist/src/agent.d.ts +5 -0
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +327 -2
- package/dist/src/agent.js.map +1 -1
- package/dist/src/agents/research.d.ts +2 -0
- package/dist/src/agents/research.d.ts.map +1 -0
- package/dist/src/agents/research.js +105 -0
- package/dist/src/agents/research.js.map +1 -0
- package/dist/src/file-manager.d.ts +19 -0
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +39 -0
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +4 -0
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +41 -0
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/prompt-builder.d.ts +1 -0
- package/dist/src/prompt-builder.d.ts.map +1 -1
- package/dist/src/prompt-builder.js +40 -0
- package/dist/src/prompt-builder.js.map +1 -1
- package/dist/src/stage-executor.d.ts +1 -0
- package/dist/src/stage-executor.d.ts.map +1 -1
- package/dist/src/stage-executor.js +43 -0
- package/dist/src/stage-executor.js.map +1 -1
- package/dist/src/structured-extraction.d.ts +22 -0
- package/dist/src/structured-extraction.d.ts.map +1 -0
- package/dist/src/structured-extraction.js +136 -0
- package/dist/src/structured-extraction.js.map +1 -0
- package/dist/src/types.d.ts +7 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/workflow-types.d.ts +1 -1
- package/dist/src/workflow-types.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/agent-registry.ts +6 -0
- package/src/agent.ts +364 -2
- package/src/agents/research.ts +103 -0
- package/src/file-manager.ts +64 -0
- package/src/git-manager.ts +52 -0
- package/src/prompt-builder.ts +53 -0
- package/src/stage-executor.ts +50 -0
- package/src/structured-extraction.ts +167 -0
- package/src/types.ts +8 -0
- package/src/workflow-types.ts +1 -1
package/src/prompt-builder.ts
CHANGED
|
@@ -206,6 +206,59 @@ export class PromptBuilder {
|
|
|
206
206
|
return { description: processedDescription, referencedFiles };
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
async buildResearchPrompt(task: Task, repositoryPath?: string): Promise<string> {
|
|
210
|
+
// Process file references in description
|
|
211
|
+
const { description: descriptionAfterFiles, referencedFiles } = await this.processFileReferences(
|
|
212
|
+
task.description,
|
|
213
|
+
repositoryPath
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Process URL references in description
|
|
217
|
+
const { description: processedDescription, referencedResources } = await this.processUrlReferences(
|
|
218
|
+
descriptionAfterFiles
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
let prompt = '';
|
|
222
|
+
prompt += `## Current Task\n\n**Task**: ${task.title}\n**Description**: ${processedDescription}`;
|
|
223
|
+
|
|
224
|
+
if ((task as any).primary_repository) {
|
|
225
|
+
prompt += `\n**Repository**: ${(task as any).primary_repository}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Add referenced files from @ mentions
|
|
229
|
+
if (referencedFiles.length > 0) {
|
|
230
|
+
prompt += `\n\n## Referenced Files\n\n`;
|
|
231
|
+
for (const file of referencedFiles) {
|
|
232
|
+
prompt += `### ${file.path}\n\`\`\`\n${file.content}\n\`\`\`\n\n`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Add referenced resources from URL mentions
|
|
237
|
+
if (referencedResources.length > 0) {
|
|
238
|
+
prompt += `\n\n## Referenced Resources\n\n`;
|
|
239
|
+
for (const resource of referencedResources) {
|
|
240
|
+
prompt += `### ${resource.title} (${resource.type})\n**URL**: ${resource.url}\n\n${resource.content}\n\n`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const taskFiles = await this.getTaskFiles(task.id);
|
|
246
|
+
const contextFiles = taskFiles.filter((f: any) => f.type === 'context' || f.type === 'reference');
|
|
247
|
+
if (contextFiles.length > 0) {
|
|
248
|
+
prompt += `\n\n## Supporting Files`;
|
|
249
|
+
for (const file of contextFiles) {
|
|
250
|
+
prompt += `\n\n### ${file.name} (${file.type})\n${file.content}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
this.logger.debug('No existing task files found for research', { taskId: task.id });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
prompt += `\n\nPlease explore the codebase thoroughly and generate 3-5 clarifying questions that will help guide the implementation of this task. Use the \`create_plan\` tool to create a research.md artifact with your questions in the markdown format specified in your system prompt.`;
|
|
258
|
+
|
|
259
|
+
return prompt;
|
|
260
|
+
}
|
|
261
|
+
|
|
209
262
|
async buildPlanningPrompt(task: Task, repositoryPath?: string): Promise<string> {
|
|
210
263
|
// Process file references in description
|
|
211
264
|
const { description: descriptionAfterFiles, referencedFiles } = await this.processFileReferences(
|
package/src/stage-executor.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ClaudeAdapter } from './adapters/claude/claude-adapter.js';
|
|
|
4
4
|
import { AgentRegistry } from './agent-registry.js';
|
|
5
5
|
import type { AgentEvent, Task, McpServerConfig } from './types.js';
|
|
6
6
|
import type { WorkflowStage, WorkflowStageExecutionResult, WorkflowExecutionOptions } from './workflow-types.js';
|
|
7
|
+
import { RESEARCH_SYSTEM_PROMPT } from './agents/research.js';
|
|
7
8
|
import { PLANNING_SYSTEM_PROMPT } from './agents/planning.js';
|
|
8
9
|
import { EXECUTION_SYSTEM_PROMPT } from './agents/execution.js';
|
|
9
10
|
import { PromptBuilder } from './prompt-builder.js';
|
|
@@ -57,6 +58,8 @@ export class StageExecutor {
|
|
|
57
58
|
const cwd = options.repositoryPath || process.cwd();
|
|
58
59
|
|
|
59
60
|
switch (agent.agent_type) {
|
|
61
|
+
case 'research':
|
|
62
|
+
return this.runResearch(task, cwd, options, stage.key);
|
|
60
63
|
case 'planning':
|
|
61
64
|
return this.runPlanning(task, cwd, options, stage.key);
|
|
62
65
|
case 'execution':
|
|
@@ -70,6 +73,53 @@ export class StageExecutor {
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
private async runResearch(task: Task, cwd: string, options: WorkflowExecutionOptions, stageKey: string): Promise<WorkflowStageExecutionResult> {
|
|
77
|
+
const contextPrompt = await this.promptBuilder.buildResearchPrompt(task, cwd);
|
|
78
|
+
let prompt = RESEARCH_SYSTEM_PROMPT + '\n\n' + contextPrompt;
|
|
79
|
+
|
|
80
|
+
const stageOverrides = options.stageOverrides?.[stageKey] || options.stageOverrides?.['research'];
|
|
81
|
+
const mergedOverrides = {
|
|
82
|
+
...(options.queryOverrides || {}),
|
|
83
|
+
...(stageOverrides?.queryOverrides || {}),
|
|
84
|
+
} as Record<string, any>;
|
|
85
|
+
|
|
86
|
+
const baseOptions: Record<string, any> = {
|
|
87
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
88
|
+
cwd,
|
|
89
|
+
permissionMode: 'plan',
|
|
90
|
+
settingSources: ['local'],
|
|
91
|
+
mcpServers: this.mcpServers
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const response = query({
|
|
95
|
+
prompt,
|
|
96
|
+
options: { ...baseOptions, ...mergedOverrides },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
let research = '';
|
|
100
|
+
for await (const message of response) {
|
|
101
|
+
// Emit raw SDK event first
|
|
102
|
+
this.eventHandler?.(this.adapter.createRawSDKEvent(message));
|
|
103
|
+
|
|
104
|
+
// Then emit transformed event
|
|
105
|
+
const transformed = this.adapter.transform(message);
|
|
106
|
+
if (transformed) {
|
|
107
|
+
if (transformed.type !== 'token') {
|
|
108
|
+
this.logger.debug('Research event', { type: transformed.type });
|
|
109
|
+
}
|
|
110
|
+
this.eventHandler?.(transformed);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
114
|
+
for (const c of message.message.content) {
|
|
115
|
+
if (c.type === 'text' && c.text) research += c.text + '\n';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { plan: research.trim() }; // Return as 'plan' field to match existing interface
|
|
121
|
+
}
|
|
122
|
+
|
|
73
123
|
private async runPlanning(task: Task, cwd: string, options: WorkflowExecutionOptions, stageKey: string): Promise<WorkflowStageExecutionResult> {
|
|
74
124
|
const contextPrompt = await this.promptBuilder.buildPlanningPrompt(task, cwd);
|
|
75
125
|
let prompt = PLANNING_SYSTEM_PROMPT + '\n\n' + contextPrompt;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { Logger } from './utils/logger.js';
|
|
3
|
+
|
|
4
|
+
export interface ExtractedQuestion {
|
|
5
|
+
id: string;
|
|
6
|
+
question: string;
|
|
7
|
+
options: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ExtractedQuestionWithAnswer extends ExtractedQuestion {
|
|
11
|
+
recommendedAnswer: string;
|
|
12
|
+
justification: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const questionsOnlySchema = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
questions: {
|
|
19
|
+
type: 'array',
|
|
20
|
+
items: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: 'string' },
|
|
24
|
+
question: { type: 'string' },
|
|
25
|
+
options: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: { type: 'string' }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ['id', 'question', 'options'],
|
|
31
|
+
additionalProperties: false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
required: ['questions'],
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const questionsWithAnswersSchema = {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
questions: {
|
|
43
|
+
type: 'array',
|
|
44
|
+
items: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
id: { type: 'string' },
|
|
48
|
+
question: { type: 'string' },
|
|
49
|
+
options: {
|
|
50
|
+
type: 'array',
|
|
51
|
+
items: { type: 'string' }
|
|
52
|
+
},
|
|
53
|
+
recommendedAnswer: { type: 'string' },
|
|
54
|
+
justification: { type: 'string' }
|
|
55
|
+
},
|
|
56
|
+
required: ['id', 'question', 'options', 'recommendedAnswer', 'justification'],
|
|
57
|
+
additionalProperties: false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
required: ['questions'],
|
|
62
|
+
additionalProperties: false
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export interface StructuredExtractor {
|
|
66
|
+
extractQuestions(researchContent: string): Promise<ExtractedQuestion[]>;
|
|
67
|
+
extractQuestionsWithAnswers(researchContent: string): Promise<ExtractedQuestionWithAnswer[]>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class OpenAIExtractor implements StructuredExtractor {
|
|
71
|
+
private client: OpenAI;
|
|
72
|
+
private logger: Logger;
|
|
73
|
+
|
|
74
|
+
constructor(logger?: Logger) {
|
|
75
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
76
|
+
if (!apiKey) {
|
|
77
|
+
throw new Error('OPENAI_API_KEY environment variable is required for structured extraction');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.client = new OpenAI({ apiKey });
|
|
81
|
+
this.logger = logger || new Logger({ debug: false, prefix: '[OpenAIExtractor]' });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async extractQuestions(researchContent: string): Promise<ExtractedQuestion[]> {
|
|
85
|
+
this.logger.debug('Extracting questions from research content', {
|
|
86
|
+
contentLength: researchContent.length,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const completion = await this.client.chat.completions.create({
|
|
90
|
+
model: 'gpt-4o-mini',
|
|
91
|
+
messages: [
|
|
92
|
+
{
|
|
93
|
+
role: 'system',
|
|
94
|
+
content: 'Extract the research questions from the provided markdown. Return a JSON object matching the schema.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
role: 'user',
|
|
98
|
+
content: researchContent,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
response_format: {
|
|
102
|
+
type: 'json_schema',
|
|
103
|
+
json_schema: {
|
|
104
|
+
name: 'questions',
|
|
105
|
+
strict: true,
|
|
106
|
+
schema: questionsOnlySchema,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const content = completion.choices[0].message.content;
|
|
112
|
+
if (!content) {
|
|
113
|
+
throw new Error('No content in OpenAI response');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const parsed = JSON.parse(content) as { questions: ExtractedQuestion[] };
|
|
117
|
+
|
|
118
|
+
this.logger.info('Successfully extracted questions', {
|
|
119
|
+
questionCount: parsed.questions.length,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return parsed.questions;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async extractQuestionsWithAnswers(
|
|
126
|
+
researchContent: string,
|
|
127
|
+
): Promise<ExtractedQuestionWithAnswer[]> {
|
|
128
|
+
this.logger.debug('Extracting questions with recommended answers', {
|
|
129
|
+
contentLength: researchContent.length,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const completion = await this.client.chat.completions.create({
|
|
133
|
+
model: 'gpt-4o-mini',
|
|
134
|
+
messages: [
|
|
135
|
+
{
|
|
136
|
+
role: 'system',
|
|
137
|
+
content: 'Extract the research questions from the markdown and provide recommended answers based on the analysis. For each question, include a recommendedAnswer (the letter: a, b, c, etc.) and a brief justification. Return a JSON object matching the schema.',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
role: 'user',
|
|
141
|
+
content: researchContent,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
response_format: {
|
|
145
|
+
type: 'json_schema',
|
|
146
|
+
json_schema: {
|
|
147
|
+
name: 'questions_with_answers',
|
|
148
|
+
strict: true,
|
|
149
|
+
schema: questionsWithAnswersSchema,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const content = completion.choices[0].message.content;
|
|
155
|
+
if (!content) {
|
|
156
|
+
throw new Error('No content in OpenAI response');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const parsed = JSON.parse(content) as { questions: ExtractedQuestionWithAnswer[] };
|
|
160
|
+
|
|
161
|
+
this.logger.info('Successfully extracted questions with answers', {
|
|
162
|
+
questionCount: parsed.questions.length,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return parsed.questions;
|
|
166
|
+
}
|
|
167
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -66,6 +66,14 @@ export interface ExecutionOptions {
|
|
|
66
66
|
permissionMode?: PermissionMode;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export interface TaskExecutionOptions {
|
|
70
|
+
repositoryPath?: string;
|
|
71
|
+
permissionMode?: PermissionMode;
|
|
72
|
+
isCloudMode?: boolean; // Determines local vs cloud behavior (local pauses after each phase)
|
|
73
|
+
autoProgress?: boolean;
|
|
74
|
+
queryOverrides?: Record<string, any>;
|
|
75
|
+
}
|
|
76
|
+
|
|
69
77
|
// Base event with timestamp
|
|
70
78
|
interface BaseEvent {
|
|
71
79
|
ts: number;
|
package/src/workflow-types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PermissionMode, AgentEvent } from './types.js';
|
|
2
2
|
|
|
3
|
-
export type AgentType = 'planning' | 'execution' | 'review' | 'testing';
|
|
3
|
+
export type AgentType = 'research' | 'planning' | 'execution' | 'review' | 'testing';
|
|
4
4
|
|
|
5
5
|
export interface AgentDefinition {
|
|
6
6
|
id: string;
|