@posthog/agent 1.20.0 → 1.22.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/claude-cli/cli.js +2251 -1888
- package/dist/src/adapters/claude/claude-adapter.d.ts +1 -1
- package/dist/src/adapters/claude/claude-adapter.d.ts.map +1 -1
- package/dist/src/adapters/claude/claude-adapter.js +141 -133
- package/dist/src/adapters/claude/claude-adapter.js.map +1 -1
- package/dist/src/adapters/types.d.ts +3 -3
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/agent.d.ts +1 -1
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +9 -8
- package/dist/src/agent.js.map +1 -1
- package/dist/src/file-manager.d.ts +10 -0
- package/dist/src/file-manager.d.ts.map +1 -1
- package/dist/src/file-manager.js +49 -10
- package/dist/src/file-manager.js.map +1 -1
- package/dist/src/git-manager.d.ts +1 -0
- package/dist/src/git-manager.d.ts.map +1 -1
- package/dist/src/git-manager.js +10 -3
- package/dist/src/git-manager.js.map +1 -1
- package/dist/src/posthog-api.d.ts +2 -1
- package/dist/src/posthog-api.d.ts.map +1 -1
- package/dist/src/posthog-api.js +11 -0
- package/dist/src/posthog-api.js.map +1 -1
- package/dist/src/task-progress-reporter.d.ts +12 -4
- package/dist/src/task-progress-reporter.d.ts.map +1 -1
- package/dist/src/task-progress-reporter.js +282 -117
- package/dist/src/task-progress-reporter.js.map +1 -1
- package/dist/src/types.d.ts +17 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/workflow/config.d.ts.map +1 -1
- package/dist/src/workflow/config.js +11 -0
- package/dist/src/workflow/config.js.map +1 -1
- package/dist/src/workflow/steps/build.js +3 -3
- package/dist/src/workflow/steps/build.js.map +1 -1
- package/dist/src/workflow/steps/finalize.d.ts +3 -0
- package/dist/src/workflow/steps/finalize.d.ts.map +1 -0
- package/dist/src/workflow/steps/finalize.js +182 -0
- package/dist/src/workflow/steps/finalize.js.map +1 -0
- package/dist/src/workflow/steps/plan.js +3 -3
- package/dist/src/workflow/steps/plan.js.map +1 -1
- package/dist/src/workflow/steps/research.js +3 -3
- package/dist/src/workflow/steps/research.js.map +1 -1
- package/package.json +2 -2
- package/src/adapters/claude/claude-adapter.ts +56 -46
- package/src/adapters/types.ts +3 -3
- package/src/agent.ts +17 -8
- package/src/file-manager.ts +59 -6
- package/src/git-manager.ts +11 -3
- package/src/posthog-api.ts +33 -1
- package/src/task-progress-reporter.ts +310 -138
- package/src/types.ts +20 -1
- package/src/workflow/config.ts +11 -0
- package/src/workflow/steps/build.ts +3 -3
- package/src/workflow/steps/finalize.ts +216 -0
- package/src/workflow/steps/plan.ts +3 -3
- package/src/workflow/steps/research.ts +3 -3
|
@@ -58,9 +58,9 @@ const researchStep = async ({ step, context }) => {
|
|
|
58
58
|
let jsonContent = '';
|
|
59
59
|
for await (const message of response) {
|
|
60
60
|
emitEvent(adapter.createRawSDKEvent(message));
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
emitEvent(
|
|
61
|
+
const transformedEvents = adapter.transform(message);
|
|
62
|
+
for (const event of transformedEvents) {
|
|
63
|
+
emitEvent(event);
|
|
64
64
|
}
|
|
65
65
|
if (message.type === 'assistant' && message.message?.content) {
|
|
66
66
|
for (const c of message.message.content) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"research.js","sources":["../../../../src/workflow/steps/research.ts"],"sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport { RESEARCH_SYSTEM_PROMPT } from '../../agents/research.js';\nimport type { WorkflowStepRunner } from '../types.js';\nimport type { ResearchEvaluation } from '../../types.js';\nimport { finalizeStepGitActions } from '../utils.js';\n\nexport const researchStep: WorkflowStepRunner = async ({ step, context }) => {\n const {\n task,\n cwd,\n isCloudMode,\n options,\n logger,\n fileManager,\n gitManager,\n promptBuilder,\n adapter,\n mcpServers,\n emitEvent,\n } = context;\n\n const stepLogger = logger.child('ResearchStep');\n\n const existingResearch = await fileManager.readResearch(task.id);\n if (existingResearch) {\n stepLogger.info('Research already exists', { taskId: task.id, hasQuestions: !!existingResearch.questions, answered: existingResearch.answered });\n \n // If there are unanswered questions, re-emit them so UI can prompt user\n if (existingResearch.questions && !existingResearch.answered) {\n stepLogger.info('Re-emitting unanswered research questions', { \n taskId: task.id,\n questionCount: existingResearch.questions.length \n });\n \n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_questions',\n content: existingResearch.questions,\n });\n \n // In local mode, halt to allow user to answer\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'skipped', halt: true };\n }\n }\n \n return { status: 'skipped' };\n }\n\n stepLogger.info('Starting research phase', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_start', { phase: 'research' }));\n\n const researchPrompt = await promptBuilder.buildResearchPrompt(task, cwd);\n const fullPrompt = `${RESEARCH_SYSTEM_PROMPT}\\n\\n${researchPrompt}`;\n\n const baseOptions: Record<string, any> = {\n model: step.model,\n cwd,\n permissionMode: 'plan',\n settingSources: ['local'],\n mcpServers,\n // Allow research tools: read-only operations, web search, and MCP resources\n allowedTools: [\n 'Read',\n 'Glob',\n 'Grep',\n 'WebFetch',\n 'WebSearch',\n 'ListMcpResources',\n 'ReadMcpResource',\n 'TodoWrite',\n 'BashOutput',\n ],\n };\n\n const response = query({\n prompt: fullPrompt,\n options: { ...baseOptions, ...(options.queryOverrides || {}) },\n });\n\n let jsonContent = '';\n for await (const message of response) {\n emitEvent(adapter.createRawSDKEvent(message));\n const transformed = adapter.transform(message);\n if (transformed) {\n emitEvent(transformed);\n }\n if (message.type === 'assistant' && message.message?.content) {\n for (const c of message.message.content) {\n if (c.type === 'text' && c.text) {\n jsonContent += c.text;\n }\n }\n }\n }\n\n if (!jsonContent.trim()) {\n stepLogger.error('No JSON output from research agent', { taskId: task.id });\n emitEvent({\n type: 'error',\n ts: Date.now(),\n message: 'Research agent returned no output',\n });\n return { status: 'completed', halt: true };\n }\n\n // Parse JSON response\n let evaluation: ResearchEvaluation;\n try {\n // Extract JSON from potential markdown code blocks or other wrapping\n const jsonMatch = jsonContent.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON object found in response');\n }\n evaluation = JSON.parse(jsonMatch[0]);\n stepLogger.info('Parsed research evaluation', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n hasQuestions: !!evaluation.questions,\n });\n } catch (error) {\n stepLogger.error('Failed to parse research JSON', {\n taskId: task.id,\n error: error instanceof Error ? error.message : String(error),\n content: jsonContent.substring(0, 500),\n });\n emitEvent({\n type: 'error',\n ts: Date.now(),\n message: `Failed to parse research JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n });\n return { status: 'completed', halt: true };\n }\n\n // Add answered/answers fields to evaluation\n if (evaluation.questions && evaluation.questions.length > 0) {\n evaluation.answered = false;\n evaluation.answers = undefined;\n }\n\n // Always write research.json\n await fileManager.writeResearch(task.id, evaluation);\n stepLogger.info('Research evaluation written', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n hasQuestions: !!evaluation.questions,\n });\n\n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_evaluation',\n content: evaluation,\n });\n\n await gitManager.addAllPostHogFiles();\n await finalizeStepGitActions(context, step, {\n commitMessage: `Research phase for ${task.title}`,\n });\n\n // Log whether questions need answering\n if (evaluation.actionabilityScore < 0.7 && evaluation.questions && evaluation.questions.length > 0) {\n stepLogger.info('Actionability score below threshold, questions needed', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n questionCount: evaluation.questions.length,\n });\n \n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_questions',\n content: evaluation.questions,\n });\n } else {\n stepLogger.info('Actionability score acceptable, proceeding to planning', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n });\n }\n\n // In local mode, always halt after research for user review\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed', halt: true };\n }\n\n // In cloud mode, check if questions need answering\n const researchData = await fileManager.readResearch(task.id);\n if (researchData?.questions && !researchData.answered) {\n // Questions need answering - halt for user input in cloud mode too\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed', halt: true };\n }\n\n // No questions or questions already answered - proceed to planning\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed' };\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"research.js","sources":["../../../../src/workflow/steps/research.ts"],"sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport { RESEARCH_SYSTEM_PROMPT } from '../../agents/research.js';\nimport type { WorkflowStepRunner } from '../types.js';\nimport type { ResearchEvaluation } from '../../types.js';\nimport { finalizeStepGitActions } from '../utils.js';\n\nexport const researchStep: WorkflowStepRunner = async ({ step, context }) => {\n const {\n task,\n cwd,\n isCloudMode,\n options,\n logger,\n fileManager,\n gitManager,\n promptBuilder,\n adapter,\n mcpServers,\n emitEvent,\n } = context;\n\n const stepLogger = logger.child('ResearchStep');\n\n const existingResearch = await fileManager.readResearch(task.id);\n if (existingResearch) {\n stepLogger.info('Research already exists', { taskId: task.id, hasQuestions: !!existingResearch.questions, answered: existingResearch.answered });\n \n // If there are unanswered questions, re-emit them so UI can prompt user\n if (existingResearch.questions && !existingResearch.answered) {\n stepLogger.info('Re-emitting unanswered research questions', { \n taskId: task.id,\n questionCount: existingResearch.questions.length \n });\n \n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_questions',\n content: existingResearch.questions,\n });\n \n // In local mode, halt to allow user to answer\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'skipped', halt: true };\n }\n }\n \n return { status: 'skipped' };\n }\n\n stepLogger.info('Starting research phase', { taskId: task.id });\n emitEvent(adapter.createStatusEvent('phase_start', { phase: 'research' }));\n\n const researchPrompt = await promptBuilder.buildResearchPrompt(task, cwd);\n const fullPrompt = `${RESEARCH_SYSTEM_PROMPT}\\n\\n${researchPrompt}`;\n\n const baseOptions: Record<string, any> = {\n model: step.model,\n cwd,\n permissionMode: 'plan',\n settingSources: ['local'],\n mcpServers,\n // Allow research tools: read-only operations, web search, and MCP resources\n allowedTools: [\n 'Read',\n 'Glob',\n 'Grep',\n 'WebFetch',\n 'WebSearch',\n 'ListMcpResources',\n 'ReadMcpResource',\n 'TodoWrite',\n 'BashOutput',\n ],\n };\n\n const response = query({\n prompt: fullPrompt,\n options: { ...baseOptions, ...(options.queryOverrides || {}) },\n });\n\n let jsonContent = '';\n for await (const message of response) {\n emitEvent(adapter.createRawSDKEvent(message));\n const transformedEvents = adapter.transform(message);\n for (const event of transformedEvents) {\n emitEvent(event);\n }\n if (message.type === 'assistant' && message.message?.content) {\n for (const c of message.message.content) {\n if (c.type === 'text' && c.text) {\n jsonContent += c.text;\n }\n }\n }\n }\n\n if (!jsonContent.trim()) {\n stepLogger.error('No JSON output from research agent', { taskId: task.id });\n emitEvent({\n type: 'error',\n ts: Date.now(),\n message: 'Research agent returned no output',\n });\n return { status: 'completed', halt: true };\n }\n\n // Parse JSON response\n let evaluation: ResearchEvaluation;\n try {\n // Extract JSON from potential markdown code blocks or other wrapping\n const jsonMatch = jsonContent.match(/\\{[\\s\\S]*\\}/);\n if (!jsonMatch) {\n throw new Error('No JSON object found in response');\n }\n evaluation = JSON.parse(jsonMatch[0]);\n stepLogger.info('Parsed research evaluation', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n hasQuestions: !!evaluation.questions,\n });\n } catch (error) {\n stepLogger.error('Failed to parse research JSON', {\n taskId: task.id,\n error: error instanceof Error ? error.message : String(error),\n content: jsonContent.substring(0, 500),\n });\n emitEvent({\n type: 'error',\n ts: Date.now(),\n message: `Failed to parse research JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n });\n return { status: 'completed', halt: true };\n }\n\n // Add answered/answers fields to evaluation\n if (evaluation.questions && evaluation.questions.length > 0) {\n evaluation.answered = false;\n evaluation.answers = undefined;\n }\n\n // Always write research.json\n await fileManager.writeResearch(task.id, evaluation);\n stepLogger.info('Research evaluation written', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n hasQuestions: !!evaluation.questions,\n });\n\n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_evaluation',\n content: evaluation,\n });\n\n await gitManager.addAllPostHogFiles();\n await finalizeStepGitActions(context, step, {\n commitMessage: `Research phase for ${task.title}`,\n });\n\n // Log whether questions need answering\n if (evaluation.actionabilityScore < 0.7 && evaluation.questions && evaluation.questions.length > 0) {\n stepLogger.info('Actionability score below threshold, questions needed', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n questionCount: evaluation.questions.length,\n });\n \n emitEvent({\n type: 'artifact',\n ts: Date.now(),\n kind: 'research_questions',\n content: evaluation.questions,\n });\n } else {\n stepLogger.info('Actionability score acceptable, proceeding to planning', {\n taskId: task.id,\n score: evaluation.actionabilityScore,\n });\n }\n\n // In local mode, always halt after research for user review\n if (!isCloudMode) {\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed', halt: true };\n }\n\n // In cloud mode, check if questions need answering\n const researchData = await fileManager.readResearch(task.id);\n if (researchData?.questions && !researchData.answered) {\n // Questions need answering - halt for user input in cloud mode too\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed', halt: true };\n }\n\n // No questions or questions already answered - proceed to planning\n emitEvent(adapter.createStatusEvent('phase_complete', { phase: 'research' }));\n return { status: 'completed' };\n};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"typescript": "^5.5.0"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@anthropic-ai/claude-agent-sdk": "^0.1.
|
|
52
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.47",
|
|
53
53
|
"dotenv": "^17.2.3",
|
|
54
54
|
"yoga-wasm-web": "^0.3.3"
|
|
55
55
|
},
|
|
@@ -18,72 +18,71 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
transform(sdkMessage: SDKMessage): AgentEvent
|
|
21
|
+
transform(sdkMessage: SDKMessage): AgentEvent[] {
|
|
22
22
|
const baseEvent = { ts: Date.now() };
|
|
23
23
|
|
|
24
|
-
// Handle stream events
|
|
25
24
|
if (sdkMessage.type === 'stream_event') {
|
|
26
25
|
const event = sdkMessage.event;
|
|
27
26
|
|
|
28
27
|
switch (event.type) {
|
|
29
28
|
case 'message_start':
|
|
30
|
-
return {
|
|
29
|
+
return [{
|
|
31
30
|
...baseEvent,
|
|
32
31
|
type: 'message_start',
|
|
33
32
|
messageId: event.message?.id,
|
|
34
33
|
model: event.message?.model
|
|
35
|
-
};
|
|
34
|
+
}];
|
|
36
35
|
|
|
37
36
|
case 'content_block_start':
|
|
38
37
|
const contentBlock = event.content_block;
|
|
39
|
-
if (!contentBlock) return
|
|
38
|
+
if (!contentBlock) return [];
|
|
40
39
|
|
|
41
|
-
return {
|
|
40
|
+
return [{
|
|
42
41
|
...baseEvent,
|
|
43
42
|
type: 'content_block_start',
|
|
44
43
|
index: event.index,
|
|
45
44
|
contentType: contentBlock.type as 'text' | 'tool_use' | 'thinking',
|
|
46
45
|
toolName: contentBlock.type === 'tool_use' ? contentBlock.name : undefined,
|
|
47
46
|
toolId: contentBlock.type === 'tool_use' ? contentBlock.id : undefined
|
|
48
|
-
};
|
|
47
|
+
}];
|
|
49
48
|
|
|
50
49
|
case 'content_block_delta':
|
|
51
50
|
const delta = event.delta;
|
|
52
|
-
if (!delta) return
|
|
51
|
+
if (!delta) return [];
|
|
53
52
|
|
|
54
53
|
if (delta.type === 'text_delta') {
|
|
55
|
-
return {
|
|
54
|
+
return [{
|
|
56
55
|
...baseEvent,
|
|
57
56
|
type: 'token',
|
|
58
57
|
content: delta.text,
|
|
59
58
|
contentType: 'text'
|
|
60
|
-
};
|
|
59
|
+
}];
|
|
61
60
|
} else if (delta.type === 'input_json_delta') {
|
|
62
|
-
return {
|
|
61
|
+
return [{
|
|
63
62
|
...baseEvent,
|
|
64
63
|
type: 'token',
|
|
65
64
|
content: delta.partial_json,
|
|
66
65
|
contentType: 'tool_input'
|
|
67
|
-
};
|
|
66
|
+
}];
|
|
68
67
|
} else if (delta.type === 'thinking_delta') {
|
|
69
|
-
return {
|
|
68
|
+
return [{
|
|
70
69
|
...baseEvent,
|
|
71
70
|
type: 'token',
|
|
72
71
|
content: delta.thinking,
|
|
73
72
|
contentType: 'thinking'
|
|
74
|
-
};
|
|
73
|
+
}];
|
|
75
74
|
}
|
|
76
|
-
return
|
|
75
|
+
return [];
|
|
77
76
|
|
|
78
77
|
case 'content_block_stop':
|
|
79
|
-
return {
|
|
78
|
+
return [{
|
|
80
79
|
...baseEvent,
|
|
81
80
|
type: 'content_block_stop',
|
|
82
81
|
index: event.index
|
|
83
|
-
};
|
|
82
|
+
}];
|
|
84
83
|
|
|
85
84
|
case 'message_delta':
|
|
86
|
-
return {
|
|
85
|
+
return [{
|
|
87
86
|
...baseEvent,
|
|
88
87
|
type: 'message_delta',
|
|
89
88
|
stopReason: event.delta?.stop_reason,
|
|
@@ -91,20 +90,20 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
91
90
|
usage: event.usage ? {
|
|
92
91
|
outputTokens: event.usage.output_tokens
|
|
93
92
|
} : undefined
|
|
94
|
-
};
|
|
93
|
+
}];
|
|
95
94
|
|
|
96
95
|
case 'message_stop':
|
|
97
|
-
return {
|
|
96
|
+
return [{
|
|
98
97
|
...baseEvent,
|
|
99
98
|
type: 'message_stop'
|
|
100
|
-
};
|
|
99
|
+
}];
|
|
101
100
|
|
|
102
101
|
case 'ping':
|
|
103
102
|
// Ignore ping events
|
|
104
|
-
return
|
|
103
|
+
return [];
|
|
105
104
|
|
|
106
105
|
case 'error':
|
|
107
|
-
return {
|
|
106
|
+
return [{
|
|
108
107
|
...baseEvent,
|
|
109
108
|
type: 'error',
|
|
110
109
|
message: event.error?.message || 'Unknown error',
|
|
@@ -115,18 +114,20 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
115
114
|
code: event.error.code,
|
|
116
115
|
} : undefined,
|
|
117
116
|
sdkError: event.error
|
|
118
|
-
};
|
|
117
|
+
}];
|
|
119
118
|
|
|
120
119
|
default:
|
|
121
|
-
return
|
|
120
|
+
return [];
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
// Handle assistant messages (full message, not streaming)
|
|
126
125
|
if (sdkMessage.type === 'assistant') {
|
|
127
126
|
const message = sdkMessage.message;
|
|
127
|
+
const events: AgentEvent[] = [];
|
|
128
128
|
|
|
129
129
|
// Extract tool calls from content blocks
|
|
130
|
+
// A single assistant message can contain multiple tool_use blocks
|
|
130
131
|
if (message.content && Array.isArray(message.content)) {
|
|
131
132
|
for (const block of message.content) {
|
|
132
133
|
if (block.type === 'tool_use') {
|
|
@@ -139,31 +140,36 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
139
140
|
args: block.input || {},
|
|
140
141
|
parentToolUseId: sdkMessage.parent_tool_use_id
|
|
141
142
|
};
|
|
142
|
-
// Enrich with tool metadata
|
|
143
|
-
|
|
143
|
+
// Enrich with tool metadata and add to events array
|
|
144
|
+
events.push(this.toolMapper.enrichToolCall(toolCallEvent));
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
// If we found tool calls, return them
|
|
150
|
+
if (events.length > 0) {
|
|
151
|
+
return events;
|
|
152
|
+
}
|
|
153
|
+
|
|
148
154
|
// If no tool calls, emit status event
|
|
149
|
-
return {
|
|
155
|
+
return [{
|
|
150
156
|
...baseEvent,
|
|
151
157
|
type: 'status',
|
|
152
158
|
phase: 'assistant_message',
|
|
153
159
|
messageId: message.id,
|
|
154
160
|
model: message.model
|
|
155
|
-
};
|
|
161
|
+
}];
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
// Handle user messages
|
|
159
165
|
if (sdkMessage.type === 'user') {
|
|
160
166
|
const message = sdkMessage.message;
|
|
167
|
+
const events: AgentEvent[] = [];
|
|
161
168
|
|
|
162
|
-
// Check for tool results in content blocks
|
|
163
|
-
if (message?.content && Array.isArray(message.content)) {
|
|
169
|
+
// Check for tool results in content blocks, A single user message can contain multiple tool_result blocks
|
|
170
|
+
if (message?.content && Array.isArray(message.content)) {
|
|
164
171
|
for (const block of message.content) {
|
|
165
172
|
if (block.type === 'tool_result') {
|
|
166
|
-
// Create tool_result event and enrich with metadata
|
|
167
173
|
const toolResultEvent = {
|
|
168
174
|
...baseEvent,
|
|
169
175
|
type: 'tool_result' as const,
|
|
@@ -173,29 +179,33 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
173
179
|
isError: block.is_error,
|
|
174
180
|
parentToolUseId: sdkMessage.parent_tool_use_id
|
|
175
181
|
};
|
|
176
|
-
|
|
177
|
-
return this.toolMapper.enrichToolResult(toolResultEvent);
|
|
182
|
+
events.push(this.toolMapper.enrichToolResult(toolResultEvent));
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
186
|
|
|
187
|
+
// If we found tool results, return them
|
|
188
|
+
if (events.length > 0) {
|
|
189
|
+
return events;
|
|
190
|
+
}
|
|
191
|
+
|
|
182
192
|
// Otherwise extract text content
|
|
183
193
|
const textContent = this.extractUserContent(message?.content);
|
|
184
194
|
if (!textContent) {
|
|
185
|
-
return
|
|
195
|
+
return [];
|
|
186
196
|
}
|
|
187
|
-
return {
|
|
197
|
+
return [{
|
|
188
198
|
...baseEvent,
|
|
189
199
|
type: 'user_message',
|
|
190
200
|
content: textContent,
|
|
191
201
|
isSynthetic: sdkMessage.isSynthetic
|
|
192
|
-
};
|
|
202
|
+
}];
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
// Handle result messages
|
|
196
206
|
if (sdkMessage.type === 'result') {
|
|
197
207
|
if (sdkMessage.subtype === 'success') {
|
|
198
|
-
return {
|
|
208
|
+
return [{
|
|
199
209
|
...baseEvent,
|
|
200
210
|
type: 'done',
|
|
201
211
|
result: sdkMessage.result,
|
|
@@ -206,9 +216,9 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
206
216
|
usage: sdkMessage.usage,
|
|
207
217
|
modelUsage: sdkMessage.modelUsage,
|
|
208
218
|
permissionDenials: sdkMessage.permission_denials
|
|
209
|
-
};
|
|
219
|
+
}];
|
|
210
220
|
} else {
|
|
211
|
-
return {
|
|
221
|
+
return [{
|
|
212
222
|
...baseEvent,
|
|
213
223
|
type: 'error',
|
|
214
224
|
message: `Execution failed: ${sdkMessage.subtype}`,
|
|
@@ -220,14 +230,14 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
220
230
|
num_turns: sdkMessage.num_turns
|
|
221
231
|
},
|
|
222
232
|
sdkError: sdkMessage
|
|
223
|
-
};
|
|
233
|
+
}];
|
|
224
234
|
}
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
// Handle system messages
|
|
228
238
|
if (sdkMessage.type === 'system') {
|
|
229
239
|
if (sdkMessage.subtype === 'init') {
|
|
230
|
-
return {
|
|
240
|
+
return [{
|
|
231
241
|
...baseEvent,
|
|
232
242
|
type: 'init',
|
|
233
243
|
model: sdkMessage.model,
|
|
@@ -239,18 +249,18 @@ export class ClaudeAdapter implements ProviderAdapter {
|
|
|
239
249
|
slashCommands: sdkMessage.slash_commands,
|
|
240
250
|
outputStyle: sdkMessage.output_style,
|
|
241
251
|
mcpServers: sdkMessage.mcp_servers
|
|
242
|
-
};
|
|
252
|
+
}];
|
|
243
253
|
} else if (sdkMessage.subtype === 'compact_boundary') {
|
|
244
|
-
return {
|
|
254
|
+
return [{
|
|
245
255
|
...baseEvent,
|
|
246
256
|
type: 'compact_boundary',
|
|
247
257
|
trigger: sdkMessage.compact_metadata.trigger,
|
|
248
258
|
preTokens: sdkMessage.compact_metadata.pre_tokens
|
|
249
|
-
};
|
|
259
|
+
}];
|
|
250
260
|
}
|
|
251
261
|
}
|
|
252
262
|
|
|
253
|
-
return
|
|
263
|
+
return [];
|
|
254
264
|
}
|
|
255
265
|
|
|
256
266
|
createStatusEvent(phase: string, additionalData?: any): StatusEvent {
|
package/src/adapters/types.ts
CHANGED
|
@@ -12,10 +12,10 @@ export interface ProviderAdapter {
|
|
|
12
12
|
name: string;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Transform a provider-specific SDK message into
|
|
16
|
-
* Returns
|
|
15
|
+
* Transform a provider-specific SDK message into one or more AgentEvents.
|
|
16
|
+
* Returns an array of events (can be empty if the message should be ignored).
|
|
17
17
|
*/
|
|
18
|
-
transform(sdkMessage: unknown): AgentEvent
|
|
18
|
+
transform(sdkMessage: unknown): AgentEvent[];
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Create a standardized status event.
|
package/src/agent.ts
CHANGED
|
@@ -214,10 +214,9 @@ export class Agent {
|
|
|
214
214
|
this.logger.debug('Received message in direct run', message);
|
|
215
215
|
// Emit raw SDK event
|
|
216
216
|
this.emitEvent(this.adapter.createRawSDKEvent(message));
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
this.emitEvent(transformedEvent);
|
|
217
|
+
const transformedEvents = this.adapter.transform(message);
|
|
218
|
+
for (const event of transformedEvents) {
|
|
219
|
+
this.emitEvent(event);
|
|
221
220
|
}
|
|
222
221
|
results.push(message);
|
|
223
222
|
}
|
|
@@ -308,11 +307,16 @@ export class Agent {
|
|
|
308
307
|
return commitHash;
|
|
309
308
|
}
|
|
310
309
|
|
|
311
|
-
async createPullRequest(
|
|
310
|
+
async createPullRequest(
|
|
311
|
+
taskId: string,
|
|
312
|
+
branchName: string,
|
|
313
|
+
taskTitle: string,
|
|
314
|
+
taskDescription: string,
|
|
315
|
+
customBody?: string
|
|
316
|
+
): Promise<string> {
|
|
312
317
|
this.logger.info('Creating pull request', { taskId, branchName, taskTitle });
|
|
313
318
|
|
|
314
|
-
|
|
315
|
-
const prBody = `## Task Details
|
|
319
|
+
const defaultBody = `## Task Details
|
|
316
320
|
**Task ID**: ${taskId}
|
|
317
321
|
**Description**: ${taskDescription}
|
|
318
322
|
|
|
@@ -320,6 +324,7 @@ export class Agent {
|
|
|
320
324
|
This PR implements the changes described in the task.
|
|
321
325
|
|
|
322
326
|
Generated by PostHog Agent`;
|
|
327
|
+
const prBody = customBody || defaultBody;
|
|
323
328
|
|
|
324
329
|
const prUrl = await this.gitManager.createPullRequest(
|
|
325
330
|
branchName,
|
|
@@ -447,11 +452,15 @@ Generated by PostHog Agent`;
|
|
|
447
452
|
}
|
|
448
453
|
|
|
449
454
|
const branchName = await this.gitManager.getCurrentBranch();
|
|
455
|
+
const finalizeResult = stepResults['finalize'];
|
|
456
|
+
const prBody = finalizeResult?.prBody;
|
|
457
|
+
|
|
450
458
|
const prUrl = await this.createPullRequest(
|
|
451
459
|
task.id,
|
|
452
460
|
branchName,
|
|
453
461
|
task.title,
|
|
454
|
-
task.description ?? ''
|
|
462
|
+
task.description ?? '',
|
|
463
|
+
prBody
|
|
455
464
|
);
|
|
456
465
|
|
|
457
466
|
this.emitEvent(this.adapter.createStatusEvent('pr_created', { prUrl }));
|
package/src/file-manager.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
2
|
+
import { join, extname } from 'path';
|
|
3
3
|
import type { SupportingFile, ResearchEvaluation } from './types.js';
|
|
4
4
|
import { Logger } from './utils/logger.js';
|
|
5
5
|
|
|
@@ -9,6 +9,14 @@ export interface TaskFile {
|
|
|
9
9
|
type: 'plan' | 'context' | 'reference' | 'output' | 'artifact';
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export interface LocalArtifact {
|
|
13
|
+
name: string;
|
|
14
|
+
content: string;
|
|
15
|
+
type: TaskFile['type'];
|
|
16
|
+
contentType: string;
|
|
17
|
+
size: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export class PostHogFileManager {
|
|
13
21
|
private repositoryPath: string;
|
|
14
22
|
private logger: Logger;
|
|
@@ -223,11 +231,7 @@ export class PostHogFileManager {
|
|
|
223
231
|
const content = await this.readTaskFile(taskId, fileName);
|
|
224
232
|
if (content !== null) {
|
|
225
233
|
// Determine type based on file name
|
|
226
|
-
|
|
227
|
-
if (fileName === 'plan.md') type = 'plan';
|
|
228
|
-
else if (fileName === 'context.md') type = 'context';
|
|
229
|
-
else if (fileName === 'requirements.md') type = 'reference';
|
|
230
|
-
else if (fileName.startsWith('output_')) type = 'output';
|
|
234
|
+
const type = this.resolveFileType(fileName);
|
|
231
235
|
|
|
232
236
|
files.push({
|
|
233
237
|
name: fileName,
|
|
@@ -240,4 +244,53 @@ export class PostHogFileManager {
|
|
|
240
244
|
|
|
241
245
|
return files;
|
|
242
246
|
}
|
|
247
|
+
|
|
248
|
+
async collectTaskArtifacts(taskId: string): Promise<LocalArtifact[]> {
|
|
249
|
+
const fileNames = await this.listTaskFiles(taskId);
|
|
250
|
+
const artifacts: LocalArtifact[] = [];
|
|
251
|
+
|
|
252
|
+
for (const fileName of fileNames) {
|
|
253
|
+
const content = await this.readTaskFile(taskId, fileName);
|
|
254
|
+
if (content === null) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const type = this.resolveFileType(fileName);
|
|
259
|
+
const contentType = this.inferContentType(fileName);
|
|
260
|
+
const size = Buffer.byteLength(content, 'utf8');
|
|
261
|
+
|
|
262
|
+
artifacts.push({
|
|
263
|
+
name: fileName,
|
|
264
|
+
content,
|
|
265
|
+
type,
|
|
266
|
+
contentType,
|
|
267
|
+
size,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return artifacts;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private resolveFileType(fileName: string): TaskFile['type'] {
|
|
275
|
+
if (fileName === 'plan.md') return 'plan';
|
|
276
|
+
if (fileName === 'context.md') return 'context';
|
|
277
|
+
if (fileName === 'requirements.md') return 'reference';
|
|
278
|
+
if (fileName.startsWith('output_')) return 'output';
|
|
279
|
+
if (fileName.endsWith('.md')) return 'reference';
|
|
280
|
+
return 'artifact';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private inferContentType(fileName: string): string {
|
|
284
|
+
const extension = extname(fileName).toLowerCase();
|
|
285
|
+
switch (extension) {
|
|
286
|
+
case '.md':
|
|
287
|
+
return 'text/markdown';
|
|
288
|
+
case '.json':
|
|
289
|
+
return 'application/json';
|
|
290
|
+
case '.txt':
|
|
291
|
+
return 'text/plain';
|
|
292
|
+
default:
|
|
293
|
+
return 'text/plain';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
243
296
|
}
|
package/src/git-manager.ts
CHANGED
|
@@ -30,6 +30,14 @@ export class GitManager {
|
|
|
30
30
|
this.logger = config.logger || new Logger({ debug: false, prefix: '[GitManager]' });
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
private escapeShellArg(str: string): string {
|
|
34
|
+
return str
|
|
35
|
+
.replace(/\\/g, '\\\\')
|
|
36
|
+
.replace(/"/g, '\\"')
|
|
37
|
+
.replace(/`/g, '\\`')
|
|
38
|
+
.replace(/\$/g, '\\$');
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
private async runGitCommand(command: string): Promise<string> {
|
|
34
42
|
try {
|
|
35
43
|
const { stdout } = await execAsync(`cd "${this.repositoryPath}" && git ${command}`);
|
|
@@ -130,7 +138,7 @@ export class GitManager {
|
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
async addFiles(paths: string[]): Promise<void> {
|
|
133
|
-
const pathList = paths.map(p => `"${p}"`).join(' ');
|
|
141
|
+
const pathList = paths.map(p => `"${this.escapeShellArg(p)}"`).join(' ');
|
|
134
142
|
await this.runGitCommand(`add ${pathList}`);
|
|
135
143
|
}
|
|
136
144
|
|
|
@@ -204,7 +212,7 @@ export class GitManager {
|
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
private buildCommitCommand(message: string, options?: { allowEmpty?: boolean; authorName?: string; authorEmail?: string }): string {
|
|
207
|
-
let command = `commit -m "${
|
|
215
|
+
let command = `commit -m "${this.escapeShellArg(message)}"`;
|
|
208
216
|
|
|
209
217
|
if (options?.allowEmpty) {
|
|
210
218
|
command += ' --allow-empty';
|
|
@@ -446,7 +454,7 @@ Generated by PostHog Agent`;
|
|
|
446
454
|
|
|
447
455
|
await this.pushBranch(branchName);
|
|
448
456
|
|
|
449
|
-
let command = `gh pr create --title "${
|
|
457
|
+
let command = `gh pr create --title "${this.escapeShellArg(title)}" --body "${this.escapeShellArg(body)}"`;
|
|
450
458
|
|
|
451
459
|
if (baseBranch) {
|
|
452
460
|
command += ` --base ${baseBranch}`;
|
package/src/posthog-api.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Task,
|
|
3
|
+
TaskRun,
|
|
4
|
+
LogEntry,
|
|
5
|
+
SupportingFile,
|
|
6
|
+
PostHogAPIConfig,
|
|
7
|
+
PostHogResource,
|
|
8
|
+
ResourceType,
|
|
9
|
+
UrlMention,
|
|
10
|
+
TaskRunArtifact,
|
|
11
|
+
TaskArtifactUploadPayload,
|
|
12
|
+
} from './types.js';
|
|
2
13
|
|
|
3
14
|
interface PostHogApiResponse<T> {
|
|
4
15
|
results?: T[];
|
|
@@ -170,6 +181,27 @@ export class PostHogAPIClient {
|
|
|
170
181
|
});
|
|
171
182
|
}
|
|
172
183
|
|
|
184
|
+
async uploadTaskArtifacts(
|
|
185
|
+
taskId: string,
|
|
186
|
+
runId: string,
|
|
187
|
+
artifacts: TaskArtifactUploadPayload[]
|
|
188
|
+
): Promise<TaskRunArtifact[]> {
|
|
189
|
+
if (!artifacts.length) {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const teamId = this.getTeamId();
|
|
194
|
+
const response = await this.apiRequest<{ artifacts: TaskRunArtifact[] }>(
|
|
195
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
196
|
+
{
|
|
197
|
+
method: 'POST',
|
|
198
|
+
body: JSON.stringify({ artifacts }),
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
return response.artifacts ?? [];
|
|
203
|
+
}
|
|
204
|
+
|
|
173
205
|
/**
|
|
174
206
|
* Fetch logs from S3 using presigned URL from TaskRun
|
|
175
207
|
* @param taskRun - The task run containing the log_url
|